2 bootchart.c - This file is part of systemd-bootchart
4 Copyright (C) 2009-2013 Intel Coproration
7 Auke Kok <auke-jan.h.kok@intel.com>
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include <sys/utsname.h>
34 #include "bootchart.h"
39 #define time_to_graph(t) ((t) * scale_x)
40 #define ps_to_graph(n) ((n) * scale_y)
41 #define kb_to_graph(m) ((m) * scale_y * 0.0001)
42 #define to_color(n) (192.0 - ((n) * 192.0))
44 #define max(x, y) (((x) > (y)) ? (x) : (y))
45 #define min(x, y) (((x) < (y)) ? (x) : (y))
47 static char str[8092];
49 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
51 static const char *colorwheel[12] = {
52 "rgb(255,32,32)", // red
53 "rgb(32,192,192)", // cyan
54 "rgb(255,128,32)", // orange
55 "rgb(128,32,192)", // blue-violet
56 "rgb(255,255,32)", // yellow
57 "rgb(192,32,128)", // red-violet
58 "rgb(32,255,32)", // green
59 "rgb(255,64,32)", // red-orange
60 "rgb(32,32,255)", // blue
61 "rgb(255,192,32)", // yellow-orange
62 "rgb(192,32,192)", // violet
63 "rgb(32,192,32)" // yellow-green
66 static double idletime = -1.0;
67 static int pfiltered = 0;
68 static int pcount = 0;
69 static int kcount = 0;
70 static float psize = 0;
71 static float ksize = 0;
72 static float esize = 0;
75 static void svg_header(void)
80 /* min width is about 1600px due to the label */
81 w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
82 w = ((w < 1600.0) ? 1600.0 : w);
84 /* height is variable based on pss, psize, ksize */
85 h = 400.0 + (scale_y * 30.0) /* base graphs and title */
86 + (pss ? (100.0 * scale_y) + (scale_y * 7.0) : 0.0) /* pss estimate */
87 + psize + ksize + esize;
89 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
90 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
91 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
93 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
94 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
96 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
98 /* write some basic info as a comment, including some help */
99 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
100 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
101 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
102 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
103 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
105 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
106 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", hz, len);
107 svg("<!-- x=\"%f\" y=\"%f\" -->\n", scale_x, scale_y);
108 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", relative, filter);
109 svg("<!-- p=\"%d\" e=\"%d\" -->\n", pss, entropy);
110 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", output_path, init_path);
113 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
115 svg(" rect { stroke-width: 1; }\n");
116 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
117 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
118 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
119 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
120 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
121 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
122 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
123 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
124 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
125 svg("// line.sec1 { }\n");
126 svg(" line.sec5 { stroke-width: 2; }\n");
127 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
128 svg(" line.dot { stroke-dasharray: 2 4; }\n");
129 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
131 svg(" .run { font-size: 8; font-style: italic; }\n");
132 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
133 svg(" text.sec { font-size: 8; }\n");
134 svg(" text.t1 { font-size: 24; }\n");
135 svg(" text.t2 { font-size: 12; }\n");
136 svg(" text.idle { font-size: 18; }\n");
138 svg(" ]]>\n </style>\n</defs>\n\n");
143 static void svg_title(const char *build)
145 char cmdline[256] = "";
146 char filename[PATH_MAX];
148 char rootbdev[16] = "Unknown";
149 char model[256] = "Unknown";
150 char date[256] = "Unknown";
151 char cpu[256] = "Unknown";
158 /* grab /proc/cmdline */
159 fd = openat(procfd, "cmdline", O_RDONLY);
162 if (!fgets(cmdline, 255, f))
163 sprintf(cmdline, "Unknown");
167 /* extract root fs so we can find disk model name in sysfs */
168 /* FIXME: this works only in the simple case */
169 c = strstr(cmdline, "root=/dev/");
171 strncpy(rootbdev, &c[10], 3);
173 sprintf(filename, "block/%s/device/model", rootbdev);
174 fd = openat(sysfd, filename, O_RDONLY);
177 if (!fgets(model, 255, f))
178 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
183 /* various utsname parameters */
185 fprintf(stderr, "Error getting uname info\n");
189 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
192 fd = openat(procfd, "cpuinfo", O_RDONLY);
195 while (fgets(buf, 255, f)) {
196 if (strstr(buf, "model name")) {
197 strncpy(cpu, &buf[13], 255);
204 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
206 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
207 uts.sysname, uts.release, uts.version, uts.machine);
208 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
210 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
212 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
216 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
217 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
220 svg("%.03fs", idletime);
224 svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
225 hz, len, overrun, pscount, pfiltered);
229 static void svg_graph_box(int height)
234 /* outside box, fill */
235 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
237 time_to_graph(sampletime[samples-1] - graph_start),
238 ps_to_graph(height));
240 for (d = graph_start; d <= sampletime[samples-1];
241 d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
242 /* lines for each second */
244 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
245 time_to_graph(d - graph_start),
246 time_to_graph(d - graph_start),
247 ps_to_graph(height));
248 else if (i % 10 == 0)
249 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
250 time_to_graph(d - graph_start),
251 time_to_graph(d - graph_start),
252 ps_to_graph(height));
254 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
255 time_to_graph(d - graph_start),
256 time_to_graph(d - graph_start),
257 ps_to_graph(height));
261 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
262 time_to_graph(d - graph_start),
271 static void svg_pss_graph(void)
273 struct ps_struct *ps;
276 svg("\n\n<!-- Pss memory size graph -->\n");
278 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
280 /* vsize 1000 == 1000mb */
282 /* draw some hlines for usable memory sizes */
283 for (i = 100000; i < 1000000; i += 100000) {
284 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
287 time_to_graph(sampletime[samples-1] - graph_start),
289 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
290 time_to_graph(sampletime[samples-1] - graph_start) + 5,
291 kb_to_graph(i), (1000000 - i) / 1000);
295 /* now plot the graph itself */
296 for (i = 1; i < samples ; i++) {
303 /* put all the small pss blocks into the bottom */
305 while (ps->next_ps) {
309 if (ps->sample[i].pss <= (100 * scale_y))
310 top += ps->sample[i].pss;
312 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
314 time_to_graph(sampletime[i - 1] - graph_start),
315 kb_to_graph(1000000.0 - top),
316 time_to_graph(sampletime[i] - sampletime[i - 1]),
317 kb_to_graph(top - bottom));
321 /* now plot the ones that are of significant size */
323 while (ps->next_ps) {
327 /* don't draw anything smaller than 2mb */
328 if (ps->sample[i].pss > (100 * scale_y)) {
329 top = bottom + ps->sample[i].pss;
330 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
331 colorwheel[ps->pid % 12],
332 time_to_graph(sampletime[i - 1] - graph_start),
333 kb_to_graph(1000000.0 - top),
334 time_to_graph(sampletime[i] - sampletime[i - 1]),
335 kb_to_graph(top - bottom));
341 /* overlay all the text labels */
342 for (i = 1; i < samples ; i++) {
349 /* put all the small pss blocks into the bottom */
351 while (ps->next_ps) {
355 if (ps->sample[i].pss <= (100 * scale_y))
356 top += ps->sample[i].pss;
361 /* now plot the ones that are of significant size */
363 while (ps->next_ps) {
367 /* don't draw anything smaller than 2mb */
368 if (ps->sample[i].pss > (100 * scale_y)) {
369 top = bottom + ps->sample[i].pss;
370 /* draw a label with the process / PID */
371 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
372 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
373 time_to_graph(sampletime[i] - graph_start),
374 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
382 /* debug output - full data dump */
383 svg("\n\n<!-- PSS map - csv format -->\n");
385 while (ps->next_ps) {
389 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
390 for (i = 0; i < samples ; i++) {
391 svg("%d," , ps->sample[i].pss);
398 static void svg_io_bi_bar(void)
405 svg("<!-- IO utilization graph - In -->\n");
407 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
410 * calculate rounding range
412 * We need to round IO data since IO block data is not updated on
413 * each poll. Applying a smoothing function loses some burst data,
414 * so keep the smoothing range short.
416 range = 0.25 / (1.0 / hz);
418 range = 2.0; /* no smoothing */
420 /* surrounding box */
423 /* find the max IO first */
424 for (i = 1; i < samples; i++) {
429 start = max(i - ((range / 2) - 1), 0);
430 stop = min(i + (range / 2), samples - 1);
432 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
438 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
445 for (i = 1; i < samples; i++) {
451 start = max(i - ((range / 2) - 1), 0);
452 stop = min(i + (range / 2), samples);
454 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
459 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
460 time_to_graph(sampletime[i - 1] - graph_start),
461 (scale_y * 5) - (pbi * (scale_y * 5)),
462 time_to_graph(sampletime[i] - sampletime[i - 1]),
463 pbi * (scale_y * 5));
465 /* labels around highest value */
467 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
468 time_to_graph(sampletime[i] - graph_start) + 5,
469 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
470 max / 1024.0 / (interval / 1000000000.0));
475 static void svg_io_bo_bar(void)
482 svg("<!-- IO utilization graph - out -->\n");
484 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
487 * calculate rounding range
489 * We need to round IO data since IO block data is not updated on
490 * each poll. Applying a smoothing function loses some burst data,
491 * so keep the smoothing range short.
493 range = 0.25 / (1.0 / hz);
495 range = 2.0; /* no smoothing */
497 /* surrounding box */
500 /* find the max IO first */
501 for (i = 1; i < samples; i++) {
506 start = max(i - ((range / 2) - 1), 0);
507 stop = min(i + (range / 2), samples - 1);
509 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
513 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
522 for (i = 1; i < samples; i++) {
528 start = max(i - ((range / 2) - 1), 0);
529 stop = min(i + (range / 2), samples);
531 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
536 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
537 time_to_graph(sampletime[i - 1] - graph_start),
538 (scale_y * 5) - (pbo * (scale_y * 5)),
539 time_to_graph(sampletime[i] - sampletime[i - 1]),
540 pbo * (scale_y * 5));
542 /* labels around highest bo value */
544 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
545 time_to_graph(sampletime[i] - graph_start) + 5,
546 ((scale_y * 5) - (pbo * (scale_y * 5))),
547 max / 1024.0 / (interval / 1000000000.0));
553 static void svg_cpu_bar(void)
557 svg("<!-- CPU utilization graph -->\n");
559 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
560 /* surrounding box */
563 /* bars for each sample, proportional to the CPU util. */
564 for (i = 1; i < samples; i++) {
571 for (c = 0; c < cpus; c++)
572 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
574 trt = trt / 1000000000.0;
576 trt = trt / (double)cpus;
579 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
585 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
586 time_to_graph(sampletime[i - 1] - graph_start),
587 (scale_y * 5) - (ptrt * (scale_y * 5)),
588 time_to_graph(sampletime[i] - sampletime[i - 1]),
589 ptrt * (scale_y * 5));
594 static void svg_wait_bar(void)
598 svg("<!-- Wait time aggregation box -->\n");
600 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
602 /* surrounding box */
605 /* bars for each sample, proportional to the CPU util. */
606 for (i = 1; i < samples; i++) {
613 for (c = 0; c < cpus; c++)
614 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
616 twt = twt / 1000000000.0;
618 twt = twt / (double)cpus;
621 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
627 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
628 time_to_graph(sampletime[i - 1] - graph_start),
629 ((scale_y * 5) - (ptwt * (scale_y * 5))),
630 time_to_graph(sampletime[i] - sampletime[i - 1]),
631 ptwt * (scale_y * 5));
637 static void svg_entropy_bar(void)
641 svg("<!-- entropy pool graph -->\n");
643 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
644 /* surrounding box */
647 /* bars for each sample, scale 0-4096 */
648 for (i = 1; i < samples; i++) {
649 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
650 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
651 time_to_graph(sampletime[i - 1] - graph_start),
652 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
653 time_to_graph(sampletime[i] - sampletime[i - 1]),
654 (entropy_avail[i] / 4096.) * (scale_y * 5));
659 static struct ps_struct *get_next_ps(struct ps_struct *ps)
662 * walk the list of processes and return the next one to be
676 /* go back for parent siblings */
679 if (ps->parent->next)
680 return ps->parent->next;
690 static int ps_filter(struct ps_struct *ps)
695 /* can't draw data when there is only 1 sample (need start + stop) */
696 if (ps->first == ps->last)
699 /* don't filter kthreadd */
703 /* drop stuff that doesn't use any real CPU time */
704 if (ps->total <= 0.001)
711 static void svg_do_initcall(int count_only)
713 FILE _cleanup_pclose_ *f = NULL;
719 /* can't plot initcall when disabled or in relative mode */
720 if (!initcall || relative) {
726 svg("<!-- initcall -->\n");
728 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
729 /* surrounding box */
730 svg_graph_box(kcount);
736 * Initcall graphing - parses dmesg buffer and displays kernel threads
737 * This somewhat uses the same methods and scaling to show processes
738 * but looks a lot simpler. It's overlaid entirely onto the PS graph
742 f = popen("dmesg", "r");
751 if (fgets(l, sizeof(l) - 1, f) == NULL)
754 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
755 &t, func, &ret, &usecs);
757 /* also parse initcalls done by module loading */
758 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
759 &t, func, &ret, &usecs);
764 /* chop the +0xXX/0xXX stuff */
765 while(func[z] != '+')
770 /* filter out irrelevant stuff */
776 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
777 func, t, usecs, ret);
783 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
784 time_to_graph(t - (usecs / 1000000.0)),
786 time_to_graph(usecs / 1000000.0),
790 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
791 time_to_graph(t - (usecs / 1000000.0)) + 5,
792 ps_to_graph(kcount) + 15,
801 static void svg_ps_bars(void)
803 struct ps_struct *ps;
809 svg("<!-- Process graph -->\n");
811 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
813 /* surrounding box */
814 svg_graph_box(pcount);
816 /* pass 2 - ps boxes */
818 while ((ps = get_next_ps(ps))) {
825 /* leave some trace of what we actually filtered etc. */
826 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->name, ps->pid,
827 ps->ppid, ps->total);
829 /* it would be nice if we could use exec_start from /proc/pid/sched,
830 * but it's unreliable and gives bogus numbers */
831 starttime = sampletime[ps->first];
833 if (!ps_filter(ps)) {
834 /* remember where _to_ our children need to draw a line */
835 ps->pos_x = time_to_graph(starttime - graph_start);
836 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
838 /* hook children to our parent coords instead */
839 ps->pos_x = ps->parent->pos_x;
840 ps->pos_y = ps->parent->pos_y;
842 /* if this is the last child, we might still need to draw a connecting line */
843 if ((!ps->next) && (ps->parent))
844 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
846 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
852 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
853 time_to_graph(starttime - graph_start),
855 time_to_graph(sampletime[ps->last] - starttime),
858 /* paint cpu load over these */
859 for (t = ps->first + 1; t < ps->last; t++) {
863 /* calculate over interval */
864 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
865 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
867 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
868 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
870 /* this can happen if timekeeping isn't accurate enough */
876 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
879 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
880 time_to_graph(sampletime[t - 1] - graph_start),
882 time_to_graph(sampletime[t] - sampletime[t - 1]),
885 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
886 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
887 time_to_graph(sampletime[t - 1] - graph_start),
888 ps_to_graph(j + (1.0 - prt)),
889 time_to_graph(sampletime[t] - sampletime[t - 1]),
893 /* determine where to display the process name */
894 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
895 /* too small to fit label inside the box */
900 /* text label of process name */
901 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i] <tspan class=\"run\">%.03fs</tspan></text>\n",
902 time_to_graph(sampletime[w] - graph_start) + 5.0,
903 ps_to_graph(j) + 14.0,
906 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
907 /* paint lines to the parent process */
909 /* horizontal part */
910 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
911 time_to_graph(starttime - graph_start),
912 ps_to_graph(j) + 10.0,
914 ps_to_graph(j) + 10.0);
916 /* one vertical line connecting all the horizontal ones up */
918 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
920 ps_to_graph(j) + 10.0,
925 j++; /* count boxes */
930 /* last pass - determine when idle */
932 /* make sure we start counting from the point where we actually have
933 * data: assume that bootchart's first sample is when data started
936 while (ps->next_ps) {
942 for (i = ps->first; i < samples - (hz / 2); i++) {
947 /* subtract bootchart cpu utilization from total */
949 for (c = 0; c < cpus; c++)
950 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
951 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
954 * our definition of "idle":
956 * if for (hz / 2) we've used less CPU than (interval / 2) ...
957 * defaults to 4.0%, which experimentally, is where atom idles
959 if ((crt - brt) < (interval / 2.0)) {
960 idletime = sampletime[i] - graph_start;
961 svg("\n<!-- idle detected at %.03f seconds -->\n",
963 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
964 time_to_graph(idletime),
966 time_to_graph(idletime),
967 ps_to_graph(pcount) + scale_y);
968 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
969 time_to_graph(idletime) + 5.0,
970 ps_to_graph(pcount) + scale_y,
978 static void svg_top_ten_cpu(void)
980 struct ps_struct *top[10];
981 struct ps_struct emptyps;
982 struct ps_struct *ps;
985 memset(&emptyps, 0, sizeof(struct ps_struct));
986 for (n=0; n < 10; n++)
989 /* walk all ps's and setup ptrs */
991 while ((ps = get_next_ps(ps))) {
992 for (n = 0; n < 10; n++) {
993 if (ps->total <= top[n]->total)
996 for (m = 9; m > n; m--)
1003 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1004 for (n = 0; n < 10; n++)
1005 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
1013 static void svg_top_ten_pss(void)
1015 struct ps_struct *top[10];
1016 struct ps_struct emptyps;
1017 struct ps_struct *ps;
1020 memset(&emptyps, 0, sizeof(struct ps_struct));
1021 for (n=0; n < 10; n++)
1024 /* walk all ps's and setup ptrs */
1026 while ((ps = get_next_ps(ps))) {
1027 for (n = 0; n < 10; n++) {
1028 if (ps->pss_max <= top[n]->pss_max)
1030 /* cascade insert */
1031 for (m = 9; m > n; m--)
1038 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1039 for (n = 0; n < 10; n++)
1040 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
1048 void svg_do(const char *build)
1050 struct ps_struct *ps;
1052 memset(&str, 0, sizeof(str));
1056 /* count initcall thread count first */
1058 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1060 /* then count processes */
1061 while ((ps = get_next_ps(ps))) {
1067 psize = ps_to_graph(pcount) + (scale_y * 2);
1069 esize = (entropy ? scale_y * 7 : 0);
1071 /* after this, we can draw the header with proper sizing */
1074 svg("<g transform=\"translate(10,400)\">\n");
1078 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1082 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1086 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1091 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1096 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1100 svg("<g transform=\"translate(10, 0)\">\n");
1104 svg("<g transform=\"translate(10,200)\">\n");
1109 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1115 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1119 svg("<g transform=\"translate(410,200)\">\n");