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 c = strstr(cmdline, "root=/dev/");
170 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);
182 /* various utsname parameters */
184 fprintf(stderr, "Error getting uname info\n");
188 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
191 fd = openat(procfd, "cpuinfo", O_RDONLY);
194 while (fgets(buf, 255, f)) {
195 if (strstr(buf, "model name")) {
196 strncpy(cpu, &buf[13], 255);
203 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
205 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
206 uts.sysname, uts.release, uts.version, uts.machine);
207 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
209 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
211 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
213 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
215 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
216 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
219 svg("%.03fs", idletime);
223 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",
224 hz, len, overrun, pscount, pfiltered);
228 static void svg_graph_box(int height)
233 /* outside box, fill */
234 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
236 time_to_graph(sampletime[samples-1] - graph_start),
237 ps_to_graph(height));
239 for (d = graph_start; d <= sampletime[samples-1];
240 d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
241 /* lines for each second */
243 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
244 time_to_graph(d - graph_start),
245 time_to_graph(d - graph_start),
246 ps_to_graph(height));
247 else if (i % 10 == 0)
248 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
249 time_to_graph(d - graph_start),
250 time_to_graph(d - graph_start),
251 ps_to_graph(height));
253 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
254 time_to_graph(d - graph_start),
255 time_to_graph(d - graph_start),
256 ps_to_graph(height));
260 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
261 time_to_graph(d - graph_start),
270 static void svg_pss_graph(void)
272 struct ps_struct *ps;
275 svg("\n\n<!-- Pss memory size graph -->\n");
277 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
279 /* vsize 1000 == 1000mb */
281 /* draw some hlines for usable memory sizes */
282 for (i = 100000; i < 1000000; i += 100000) {
283 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
286 time_to_graph(sampletime[samples-1] - graph_start),
288 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
289 time_to_graph(sampletime[samples-1] - graph_start) + 5,
290 kb_to_graph(i), (1000000 - i) / 1000);
294 /* now plot the graph itself */
295 for (i = 1; i < samples ; i++) {
302 /* put all the small pss blocks into the bottom */
304 while (ps->next_ps) {
308 if (ps->sample[i].pss <= (100 * scale_y))
309 top += ps->sample[i].pss;
311 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
313 time_to_graph(sampletime[i - 1] - graph_start),
314 kb_to_graph(1000000.0 - top),
315 time_to_graph(sampletime[i] - sampletime[i - 1]),
316 kb_to_graph(top - bottom));
320 /* now plot the ones that are of significant size */
322 while (ps->next_ps) {
326 /* don't draw anything smaller than 2mb */
327 if (ps->sample[i].pss > (100 * scale_y)) {
328 top = bottom + ps->sample[i].pss;
329 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
330 colorwheel[ps->pid % 12],
331 time_to_graph(sampletime[i - 1] - graph_start),
332 kb_to_graph(1000000.0 - top),
333 time_to_graph(sampletime[i] - sampletime[i - 1]),
334 kb_to_graph(top - bottom));
340 /* overlay all the text labels */
341 for (i = 1; i < samples ; i++) {
348 /* put all the small pss blocks into the bottom */
350 while (ps->next_ps) {
354 if (ps->sample[i].pss <= (100 * scale_y))
355 top += ps->sample[i].pss;
360 /* now plot the ones that are of significant size */
362 while (ps->next_ps) {
366 /* don't draw anything smaller than 2mb */
367 if (ps->sample[i].pss > (100 * scale_y)) {
368 top = bottom + ps->sample[i].pss;
369 /* draw a label with the process / PID */
370 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
371 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
372 time_to_graph(sampletime[i] - graph_start),
373 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
381 /* debug output - full data dump */
382 svg("\n\n<!-- PSS map - csv format -->\n");
384 while (ps->next_ps) {
388 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
389 for (i = 0; i < samples ; i++) {
390 svg("%d," , ps->sample[i].pss);
397 static void svg_io_bi_bar(void)
404 svg("<!-- IO utilization graph - In -->\n");
406 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
409 * calculate rounding range
411 * We need to round IO data since IO block data is not updated on
412 * each poll. Applying a smoothing function loses some burst data,
413 * so keep the smoothing range short.
415 range = 0.25 / (1.0 / hz);
417 range = 2.0; /* no smoothing */
419 /* surrounding box */
422 /* find the max IO first */
423 for (i = 1; i < samples; i++) {
428 start = max(i - ((range / 2) - 1), 0);
429 stop = min(i + (range / 2), samples - 1);
431 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
437 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
444 for (i = 1; i < samples; i++) {
450 start = max(i - ((range / 2) - 1), 0);
451 stop = min(i + (range / 2), samples);
453 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
458 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
459 time_to_graph(sampletime[i - 1] - graph_start),
460 (scale_y * 5) - (pbi * (scale_y * 5)),
461 time_to_graph(sampletime[i] - sampletime[i - 1]),
462 pbi * (scale_y * 5));
464 /* labels around highest value */
466 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
467 time_to_graph(sampletime[i] - graph_start) + 5,
468 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
469 max / 1024.0 / (interval / 1000000000.0));
474 static void svg_io_bo_bar(void)
481 svg("<!-- IO utilization graph - out -->\n");
483 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
486 * calculate rounding range
488 * We need to round IO data since IO block data is not updated on
489 * each poll. Applying a smoothing function loses some burst data,
490 * so keep the smoothing range short.
492 range = 0.25 / (1.0 / hz);
494 range = 2.0; /* no smoothing */
496 /* surrounding box */
499 /* find the max IO first */
500 for (i = 1; i < samples; i++) {
505 start = max(i - ((range / 2) - 1), 0);
506 stop = min(i + (range / 2), samples - 1);
508 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
512 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
521 for (i = 1; i < samples; i++) {
527 start = max(i - ((range / 2) - 1), 0);
528 stop = min(i + (range / 2), samples);
530 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
535 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
536 time_to_graph(sampletime[i - 1] - graph_start),
537 (scale_y * 5) - (pbo * (scale_y * 5)),
538 time_to_graph(sampletime[i] - sampletime[i - 1]),
539 pbo * (scale_y * 5));
541 /* labels around highest bo value */
543 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
544 time_to_graph(sampletime[i] - graph_start) + 5,
545 ((scale_y * 5) - (pbo * (scale_y * 5))),
546 max / 1024.0 / (interval / 1000000000.0));
552 static void svg_cpu_bar(void)
556 svg("<!-- CPU utilization graph -->\n");
558 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
559 /* surrounding box */
562 /* bars for each sample, proportional to the CPU util. */
563 for (i = 1; i < samples; i++) {
570 for (c = 0; c < cpus; c++)
571 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
573 trt = trt / 1000000000.0;
575 trt = trt / (double)cpus;
578 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
584 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
585 time_to_graph(sampletime[i - 1] - graph_start),
586 (scale_y * 5) - (ptrt * (scale_y * 5)),
587 time_to_graph(sampletime[i] - sampletime[i - 1]),
588 ptrt * (scale_y * 5));
593 static void svg_wait_bar(void)
597 svg("<!-- Wait time aggregation box -->\n");
599 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
601 /* surrounding box */
604 /* bars for each sample, proportional to the CPU util. */
605 for (i = 1; i < samples; i++) {
612 for (c = 0; c < cpus; c++)
613 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
615 twt = twt / 1000000000.0;
617 twt = twt / (double)cpus;
620 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
626 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
627 time_to_graph(sampletime[i - 1] - graph_start),
628 ((scale_y * 5) - (ptwt * (scale_y * 5))),
629 time_to_graph(sampletime[i] - sampletime[i - 1]),
630 ptwt * (scale_y * 5));
636 static void svg_entropy_bar(void)
640 svg("<!-- entropy pool graph -->\n");
642 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
643 /* surrounding box */
646 /* bars for each sample, scale 0-4096 */
647 for (i = 1; i < samples; i++) {
648 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
649 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
650 time_to_graph(sampletime[i - 1] - graph_start),
651 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
652 time_to_graph(sampletime[i] - sampletime[i - 1]),
653 (entropy_avail[i] / 4096.) * (scale_y * 5));
658 static struct ps_struct *get_next_ps(struct ps_struct *ps)
661 * walk the list of processes and return the next one to be
675 /* go back for parent siblings */
678 if (ps->parent->next)
679 return ps->parent->next;
689 static int ps_filter(struct ps_struct *ps)
694 /* can't draw data when there is only 1 sample (need start + stop) */
695 if (ps->first == ps->last)
698 /* don't filter kthreadd */
702 /* drop stuff that doesn't use any real CPU time */
703 if (ps->total <= 0.001)
710 static void svg_do_initcall(int count_only)
712 FILE _cleanup_pclose_ *f = NULL;
718 /* can't plot initcall when disabled or in relative mode */
719 if (!initcall || relative) {
725 svg("<!-- initcall -->\n");
727 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
728 /* surrounding box */
729 svg_graph_box(kcount);
735 * Initcall graphing - parses dmesg buffer and displays kernel threads
736 * This somewhat uses the same methods and scaling to show processes
737 * but looks a lot simpler. It's overlaid entirely onto the PS graph
741 f = popen("dmesg", "r");
750 if (fgets(l, sizeof(l) - 1, f) == NULL)
753 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
754 &t, func, &ret, &usecs);
756 /* also parse initcalls done by module loading */
757 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
758 &t, func, &ret, &usecs);
763 /* chop the +0xXX/0xXX stuff */
764 while(func[z] != '+')
769 /* filter out irrelevant stuff */
775 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
776 func, t, usecs, ret);
782 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
783 time_to_graph(t - (usecs / 1000000.0)),
785 time_to_graph(usecs / 1000000.0),
789 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
790 time_to_graph(t - (usecs / 1000000.0)) + 5,
791 ps_to_graph(kcount) + 15,
800 static void svg_ps_bars(void)
802 struct ps_struct *ps;
808 svg("<!-- Process graph -->\n");
810 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
812 /* surrounding box */
813 svg_graph_box(pcount);
815 /* pass 2 - ps boxes */
817 while ((ps = get_next_ps(ps))) {
824 /* leave some trace of what we actually filtered etc. */
825 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->name, ps->pid,
826 ps->ppid, ps->total);
828 /* it would be nice if we could use exec_start from /proc/pid/sched,
829 * but it's unreliable and gives bogus numbers */
830 starttime = sampletime[ps->first];
832 if (!ps_filter(ps)) {
833 /* remember where _to_ our children need to draw a line */
834 ps->pos_x = time_to_graph(starttime - graph_start);
835 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
837 /* hook children to our parent coords instead */
838 ps->pos_x = ps->parent->pos_x;
839 ps->pos_y = ps->parent->pos_y;
841 /* if this is the last child, we might still need to draw a connecting line */
842 if ((!ps->next) && (ps->parent))
843 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
845 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
851 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
852 time_to_graph(starttime - graph_start),
854 time_to_graph(sampletime[ps->last] - starttime),
857 /* paint cpu load over these */
858 for (t = ps->first + 1; t < ps->last; t++) {
862 /* calculate over interval */
863 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
864 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
866 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
867 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
869 /* this can happen if timekeeping isn't accurate enough */
875 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
878 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
879 time_to_graph(sampletime[t - 1] - graph_start),
881 time_to_graph(sampletime[t] - sampletime[t - 1]),
884 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
885 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
886 time_to_graph(sampletime[t - 1] - graph_start),
887 ps_to_graph(j + (1.0 - prt)),
888 time_to_graph(sampletime[t] - sampletime[t - 1]),
892 /* determine where to display the process name */
893 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
894 /* too small to fit label inside the box */
899 /* text label of process name */
900 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i] <tspan class=\"run\">%.03fs</tspan></text>\n",
901 time_to_graph(sampletime[w] - graph_start) + 5.0,
902 ps_to_graph(j) + 14.0,
905 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
906 /* paint lines to the parent process */
908 /* horizontal part */
909 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
910 time_to_graph(starttime - graph_start),
911 ps_to_graph(j) + 10.0,
913 ps_to_graph(j) + 10.0);
915 /* one vertical line connecting all the horizontal ones up */
917 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
919 ps_to_graph(j) + 10.0,
924 j++; /* count boxes */
929 /* last pass - determine when idle */
931 /* make sure we start counting from the point where we actually have
932 * data: assume that bootchart's first sample is when data started
935 while (ps->next_ps) {
941 for (i = ps->first; i < samples - (hz / 2); i++) {
946 /* subtract bootchart cpu utilization from total */
948 for (c = 0; c < cpus; c++)
949 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
950 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
953 * our definition of "idle":
955 * if for (hz / 2) we've used less CPU than (interval / 2) ...
956 * defaults to 4.0%, which experimentally, is where atom idles
958 if ((crt - brt) < (interval / 2.0)) {
959 idletime = sampletime[i] - graph_start;
960 svg("\n<!-- idle detected at %.03f seconds -->\n",
962 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
963 time_to_graph(idletime),
965 time_to_graph(idletime),
966 ps_to_graph(pcount) + scale_y);
967 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
968 time_to_graph(idletime) + 5.0,
969 ps_to_graph(pcount) + scale_y,
977 static void svg_top_ten_cpu(void)
979 struct ps_struct *top[10];
980 struct ps_struct emptyps;
981 struct ps_struct *ps;
984 memset(&emptyps, 0, sizeof(struct ps_struct));
985 for (n=0; n < 10; n++)
988 /* walk all ps's and setup ptrs */
990 while ((ps = get_next_ps(ps))) {
991 for (n = 0; n < 10; n++) {
992 if (ps->total <= top[n]->total)
995 for (m = 9; m > n; m--)
1002 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1003 for (n = 0; n < 10; n++)
1004 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
1012 static void svg_top_ten_pss(void)
1014 struct ps_struct *top[10];
1015 struct ps_struct emptyps;
1016 struct ps_struct *ps;
1019 memset(&emptyps, 0, sizeof(struct ps_struct));
1020 for (n=0; n < 10; n++)
1023 /* walk all ps's and setup ptrs */
1025 while ((ps = get_next_ps(ps))) {
1026 for (n = 0; n < 10; n++) {
1027 if (ps->pss_max <= top[n]->pss_max)
1029 /* cascade insert */
1030 for (m = 9; m > n; m--)
1037 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1038 for (n = 0; n < 10; n++)
1039 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
1047 void svg_do(const char *build)
1049 struct ps_struct *ps;
1051 memset(&str, 0, sizeof(str));
1055 /* count initcall thread count first */
1057 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1059 /* then count processes */
1060 while ((ps = get_next_ps(ps))) {
1066 psize = ps_to_graph(pcount) + (scale_y * 2);
1068 esize = (entropy ? scale_y * 7 : 0);
1070 /* after this, we can draw the header with proper sizing */
1073 svg("<g transform=\"translate(10,400)\">\n");
1077 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1081 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1085 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1090 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1095 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1099 svg("<g transform=\"translate(10, 0)\">\n");
1103 svg("<g transform=\"translate(10,200)\">\n");
1108 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1114 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1118 svg("<g transform=\"translate(410,200)\">\n");