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, samples_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";
153 FILE _cleanup_fclose_ *f = NULL;
158 /* grab /proc/cmdline */
159 fd = openat(procfd, "cmdline", O_RDONLY);
162 if (!fgets(cmdline, 255, f))
163 sprintf(cmdline, "Unknown");
166 /* extract root fs so we can find disk model name in sysfs */
167 /* FIXME: this works only in the simple case */
168 c = strstr(cmdline, "root=/dev/");
170 strncpy(rootbdev, &c[10], 3);
172 sprintf(filename, "block/%s/device/model", rootbdev);
173 fd = openat(sysfd, filename, O_RDONLY);
176 if (!fgets(model, 255, f))
177 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
181 /* various utsname parameters */
183 fprintf(stderr, "Error getting uname info\n");
187 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
190 fd = openat(procfd, "cpuinfo", O_RDONLY);
193 while (fgets(buf, 255, f)) {
194 if (strstr(buf, "model name")) {
195 strncpy(cpu, &buf[13], 255);
201 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
203 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
204 uts.sysname, uts.release, uts.version, uts.machine);
205 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
207 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
209 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
211 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
213 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
214 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
217 svg("%.03fs", idletime);
221 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",
222 hz, samples_len, overrun, pscount, pfiltered);
226 static void svg_graph_box(int height)
231 /* outside box, fill */
232 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
234 time_to_graph(sampletime[samples-1] - graph_start),
235 ps_to_graph(height));
237 for (d = graph_start; d <= sampletime[samples-1];
238 d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
239 /* lines for each second */
241 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
242 time_to_graph(d - graph_start),
243 time_to_graph(d - graph_start),
244 ps_to_graph(height));
245 else if (i % 10 == 0)
246 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
247 time_to_graph(d - graph_start),
248 time_to_graph(d - graph_start),
249 ps_to_graph(height));
251 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
252 time_to_graph(d - graph_start),
253 time_to_graph(d - graph_start),
254 ps_to_graph(height));
258 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
259 time_to_graph(d - graph_start),
267 /* xml comments must not contain "--" */
268 static char* xml_comment_encode(const char* name) {
271 enc_name = strdup(name);
275 for (p = enc_name; *p; p++)
276 if (p[0] == '-' && p[1] == '-')
282 static void svg_pss_graph(void)
284 struct ps_struct *ps;
287 svg("\n\n<!-- Pss memory size graph -->\n");
289 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
291 /* vsize 1000 == 1000mb */
293 /* draw some hlines for usable memory sizes */
294 for (i = 100000; i < 1000000; i += 100000) {
295 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
298 time_to_graph(sampletime[samples-1] - graph_start),
300 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
301 time_to_graph(sampletime[samples-1] - graph_start) + 5,
302 kb_to_graph(i), (1000000 - i) / 1000);
306 /* now plot the graph itself */
307 for (i = 1; i < samples ; i++) {
314 /* put all the small pss blocks into the bottom */
316 while (ps->next_ps) {
320 if (ps->sample[i].pss <= (100 * scale_y))
321 top += ps->sample[i].pss;
323 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
325 time_to_graph(sampletime[i - 1] - graph_start),
326 kb_to_graph(1000000.0 - top),
327 time_to_graph(sampletime[i] - sampletime[i - 1]),
328 kb_to_graph(top - bottom));
332 /* now plot the ones that are of significant size */
334 while (ps->next_ps) {
338 /* don't draw anything smaller than 2mb */
339 if (ps->sample[i].pss > (100 * scale_y)) {
340 top = bottom + ps->sample[i].pss;
341 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
342 colorwheel[ps->pid % 12],
343 time_to_graph(sampletime[i - 1] - graph_start),
344 kb_to_graph(1000000.0 - top),
345 time_to_graph(sampletime[i] - sampletime[i - 1]),
346 kb_to_graph(top - bottom));
352 /* overlay all the text labels */
353 for (i = 1; i < samples ; i++) {
360 /* put all the small pss blocks into the bottom */
362 while (ps->next_ps) {
366 if (ps->sample[i].pss <= (100 * scale_y))
367 top += ps->sample[i].pss;
372 /* now plot the ones that are of significant size */
374 while (ps->next_ps) {
378 /* don't draw anything smaller than 2mb */
379 if (ps->sample[i].pss > (100 * scale_y)) {
380 top = bottom + ps->sample[i].pss;
381 /* draw a label with the process / PID */
382 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
383 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
384 time_to_graph(sampletime[i] - graph_start),
385 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
393 /* debug output - full data dump */
394 svg("\n\n<!-- PSS map - csv format -->\n");
396 while (ps->next_ps) {
397 char _cleanup_free_*enc_name;
402 enc_name = xml_comment_encode(ps->name);
406 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
408 for (i = 0; i < samples ; i++) {
409 svg("%d," , ps->sample[i].pss);
416 static void svg_io_bi_bar(void)
423 svg("<!-- IO utilization graph - In -->\n");
425 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
428 * calculate rounding range
430 * We need to round IO data since IO block data is not updated on
431 * each poll. Applying a smoothing function loses some burst data,
432 * so keep the smoothing range short.
434 range = 0.25 / (1.0 / hz);
436 range = 2.0; /* no smoothing */
438 /* surrounding box */
441 /* find the max IO first */
442 for (i = 1; i < samples; i++) {
447 start = max(i - ((range / 2) - 1), 0);
448 stop = min(i + (range / 2), samples - 1);
450 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
456 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
463 for (i = 1; i < samples; i++) {
469 start = max(i - ((range / 2) - 1), 0);
470 stop = min(i + (range / 2), samples);
472 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
477 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
478 time_to_graph(sampletime[i - 1] - graph_start),
479 (scale_y * 5) - (pbi * (scale_y * 5)),
480 time_to_graph(sampletime[i] - sampletime[i - 1]),
481 pbi * (scale_y * 5));
483 /* labels around highest value */
485 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
486 time_to_graph(sampletime[i] - graph_start) + 5,
487 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
488 max / 1024.0 / (interval / 1000000000.0));
493 static void svg_io_bo_bar(void)
500 svg("<!-- IO utilization graph - out -->\n");
502 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
505 * calculate rounding range
507 * We need to round IO data since IO block data is not updated on
508 * each poll. Applying a smoothing function loses some burst data,
509 * so keep the smoothing range short.
511 range = 0.25 / (1.0 / hz);
513 range = 2.0; /* no smoothing */
515 /* surrounding box */
518 /* find the max IO first */
519 for (i = 1; i < samples; i++) {
524 start = max(i - ((range / 2) - 1), 0);
525 stop = min(i + (range / 2), samples - 1);
527 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
531 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
540 for (i = 1; i < samples; i++) {
546 start = max(i - ((range / 2) - 1), 0);
547 stop = min(i + (range / 2), samples);
549 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
554 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
555 time_to_graph(sampletime[i - 1] - graph_start),
556 (scale_y * 5) - (pbo * (scale_y * 5)),
557 time_to_graph(sampletime[i] - sampletime[i - 1]),
558 pbo * (scale_y * 5));
560 /* labels around highest bo value */
562 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
563 time_to_graph(sampletime[i] - graph_start) + 5,
564 ((scale_y * 5) - (pbo * (scale_y * 5))),
565 max / 1024.0 / (interval / 1000000000.0));
571 static void svg_cpu_bar(void)
575 svg("<!-- CPU utilization graph -->\n");
577 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
578 /* surrounding box */
581 /* bars for each sample, proportional to the CPU util. */
582 for (i = 1; i < samples; i++) {
589 for (c = 0; c < cpus; c++)
590 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
592 trt = trt / 1000000000.0;
594 trt = trt / (double)cpus;
597 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
603 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
604 time_to_graph(sampletime[i - 1] - graph_start),
605 (scale_y * 5) - (ptrt * (scale_y * 5)),
606 time_to_graph(sampletime[i] - sampletime[i - 1]),
607 ptrt * (scale_y * 5));
612 static void svg_wait_bar(void)
616 svg("<!-- Wait time aggregation box -->\n");
618 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
620 /* surrounding box */
623 /* bars for each sample, proportional to the CPU util. */
624 for (i = 1; i < samples; i++) {
631 for (c = 0; c < cpus; c++)
632 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
634 twt = twt / 1000000000.0;
636 twt = twt / (double)cpus;
639 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
645 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
646 time_to_graph(sampletime[i - 1] - graph_start),
647 ((scale_y * 5) - (ptwt * (scale_y * 5))),
648 time_to_graph(sampletime[i] - sampletime[i - 1]),
649 ptwt * (scale_y * 5));
655 static void svg_entropy_bar(void)
659 svg("<!-- entropy pool graph -->\n");
661 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
662 /* surrounding box */
665 /* bars for each sample, scale 0-4096 */
666 for (i = 1; i < samples; i++) {
667 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
668 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
669 time_to_graph(sampletime[i - 1] - graph_start),
670 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
671 time_to_graph(sampletime[i] - sampletime[i - 1]),
672 (entropy_avail[i] / 4096.) * (scale_y * 5));
677 static struct ps_struct *get_next_ps(struct ps_struct *ps)
680 * walk the list of processes and return the next one to be
694 /* go back for parent siblings */
697 if (ps->parent->next)
698 return ps->parent->next;
708 static int ps_filter(struct ps_struct *ps)
713 /* can't draw data when there is only 1 sample (need start + stop) */
714 if (ps->first == ps->last)
717 /* don't filter kthreadd */
721 /* drop stuff that doesn't use any real CPU time */
722 if (ps->total <= 0.001)
729 static void svg_do_initcall(int count_only)
731 FILE _cleanup_pclose_ *f = NULL;
737 /* can't plot initcall when disabled or in relative mode */
738 if (!initcall || relative) {
744 svg("<!-- initcall -->\n");
746 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
747 /* surrounding box */
748 svg_graph_box(kcount);
754 * Initcall graphing - parses dmesg buffer and displays kernel threads
755 * This somewhat uses the same methods and scaling to show processes
756 * but looks a lot simpler. It's overlaid entirely onto the PS graph
760 f = popen("dmesg", "r");
769 if (fgets(l, sizeof(l) - 1, f) == NULL)
772 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
773 &t, func, &ret, &usecs);
775 /* also parse initcalls done by module loading */
776 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
777 &t, func, &ret, &usecs);
782 /* chop the +0xXX/0xXX stuff */
783 while(func[z] != '+')
788 /* filter out irrelevant stuff */
794 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
795 func, t, usecs, ret);
801 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
802 time_to_graph(t - (usecs / 1000000.0)),
804 time_to_graph(usecs / 1000000.0),
808 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
809 time_to_graph(t - (usecs / 1000000.0)) + 5,
810 ps_to_graph(kcount) + 15,
819 static void svg_ps_bars(void)
821 struct ps_struct *ps;
827 svg("<!-- Process graph -->\n");
829 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
831 /* surrounding box */
832 svg_graph_box(pcount);
834 /* pass 2 - ps boxes */
836 while ((ps = get_next_ps(ps))) {
837 char _cleanup_free_*enc_name;
845 enc_name = xml_comment_encode(ps->name);
849 /* leave some trace of what we actually filtered etc. */
850 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
851 ps->ppid, ps->total);
853 /* it would be nice if we could use exec_start from /proc/pid/sched,
854 * but it's unreliable and gives bogus numbers */
855 starttime = sampletime[ps->first];
857 if (!ps_filter(ps)) {
858 /* remember where _to_ our children need to draw a line */
859 ps->pos_x = time_to_graph(starttime - graph_start);
860 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
862 /* hook children to our parent coords instead */
863 ps->pos_x = ps->parent->pos_x;
864 ps->pos_y = ps->parent->pos_y;
866 /* if this is the last child, we might still need to draw a connecting line */
867 if ((!ps->next) && (ps->parent))
868 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
870 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
876 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
877 time_to_graph(starttime - graph_start),
879 time_to_graph(sampletime[ps->last] - starttime),
882 /* paint cpu load over these */
883 for (t = ps->first + 1; t < ps->last; t++) {
887 /* calculate over interval */
888 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
889 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
891 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
892 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
894 /* this can happen if timekeeping isn't accurate enough */
900 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
903 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
904 time_to_graph(sampletime[t - 1] - graph_start),
906 time_to_graph(sampletime[t] - sampletime[t - 1]),
909 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
910 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
911 time_to_graph(sampletime[t - 1] - graph_start),
912 ps_to_graph(j + (1.0 - prt)),
913 time_to_graph(sampletime[t] - sampletime[t - 1]),
917 /* determine where to display the process name */
918 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
919 /* too small to fit label inside the box */
924 /* text label of process name */
925 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
926 time_to_graph(sampletime[w] - graph_start) + 5.0,
927 ps_to_graph(j) + 14.0,
930 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
931 /* paint lines to the parent process */
933 /* horizontal part */
934 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
935 time_to_graph(starttime - graph_start),
936 ps_to_graph(j) + 10.0,
938 ps_to_graph(j) + 10.0);
940 /* one vertical line connecting all the horizontal ones up */
942 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
944 ps_to_graph(j) + 10.0,
949 j++; /* count boxes */
954 /* last pass - determine when idle */
956 /* make sure we start counting from the point where we actually have
957 * data: assume that bootchart's first sample is when data started
960 while (ps->next_ps) {
966 for (i = ps->first; i < samples - (hz / 2); i++) {
971 /* subtract bootchart cpu utilization from total */
973 for (c = 0; c < cpus; c++)
974 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
975 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
978 * our definition of "idle":
980 * if for (hz / 2) we've used less CPU than (interval / 2) ...
981 * defaults to 4.0%, which experimentally, is where atom idles
983 if ((crt - brt) < (interval / 2.0)) {
984 idletime = sampletime[i] - graph_start;
985 svg("\n<!-- idle detected at %.03f seconds -->\n",
987 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
988 time_to_graph(idletime),
990 time_to_graph(idletime),
991 ps_to_graph(pcount) + scale_y);
992 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
993 time_to_graph(idletime) + 5.0,
994 ps_to_graph(pcount) + scale_y,
1002 static void svg_top_ten_cpu(void)
1004 struct ps_struct *top[10];
1005 struct ps_struct emptyps;
1006 struct ps_struct *ps;
1009 memset(&emptyps, 0, sizeof(struct ps_struct));
1010 for (n=0; n < 10; n++)
1013 /* walk all ps's and setup ptrs */
1015 while ((ps = get_next_ps(ps))) {
1016 for (n = 0; n < 10; n++) {
1017 if (ps->total <= top[n]->total)
1019 /* cascade insert */
1020 for (m = 9; m > n; m--)
1027 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1028 for (n = 0; n < 10; n++)
1029 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1037 static void svg_top_ten_pss(void)
1039 struct ps_struct *top[10];
1040 struct ps_struct emptyps;
1041 struct ps_struct *ps;
1044 memset(&emptyps, 0, sizeof(struct ps_struct));
1045 for (n=0; n < 10; n++)
1048 /* walk all ps's and setup ptrs */
1050 while ((ps = get_next_ps(ps))) {
1051 for (n = 0; n < 10; n++) {
1052 if (ps->pss_max <= top[n]->pss_max)
1054 /* cascade insert */
1055 for (m = 9; m > n; m--)
1062 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1063 for (n = 0; n < 10; n++)
1064 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1072 void svg_do(const char *build)
1074 struct ps_struct *ps;
1076 memset(&str, 0, sizeof(str));
1080 /* count initcall thread count first */
1082 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1084 /* then count processes */
1085 while ((ps = get_next_ps(ps))) {
1091 psize = ps_to_graph(pcount) + (scale_y * 2);
1093 esize = (entropy ? scale_y * 7 : 0);
1095 /* after this, we can draw the header with proper sizing */
1098 svg("<g transform=\"translate(10,400)\">\n");
1102 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1106 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1110 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1115 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1120 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1124 svg("<g transform=\"translate(10, 0)\">\n");
1128 svg("<g transform=\"translate(10,200)\">\n");
1133 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1139 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1143 svg("<g transform=\"translate(410,200)\">\n");