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";
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, samples_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),
270 /* xml comments must not contain "--" */
271 static char* xml_comment_encode(const char* name) {
274 enc_name = strdup(name);
278 for (p = enc_name; *p; p++)
279 if (p[0] == '-' && p[1] == '-')
285 static void svg_pss_graph(void)
287 struct ps_struct *ps;
290 svg("\n\n<!-- Pss memory size graph -->\n");
292 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
294 /* vsize 1000 == 1000mb */
296 /* draw some hlines for usable memory sizes */
297 for (i = 100000; i < 1000000; i += 100000) {
298 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
301 time_to_graph(sampletime[samples-1] - graph_start),
303 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
304 time_to_graph(sampletime[samples-1] - graph_start) + 5,
305 kb_to_graph(i), (1000000 - i) / 1000);
309 /* now plot the graph itself */
310 for (i = 1; i < samples ; i++) {
317 /* put all the small pss blocks into the bottom */
319 while (ps->next_ps) {
323 if (ps->sample[i].pss <= (100 * scale_y))
324 top += ps->sample[i].pss;
326 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
328 time_to_graph(sampletime[i - 1] - graph_start),
329 kb_to_graph(1000000.0 - top),
330 time_to_graph(sampletime[i] - sampletime[i - 1]),
331 kb_to_graph(top - bottom));
335 /* now plot the ones that are of significant size */
337 while (ps->next_ps) {
341 /* don't draw anything smaller than 2mb */
342 if (ps->sample[i].pss > (100 * scale_y)) {
343 top = bottom + ps->sample[i].pss;
344 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
345 colorwheel[ps->pid % 12],
346 time_to_graph(sampletime[i - 1] - graph_start),
347 kb_to_graph(1000000.0 - top),
348 time_to_graph(sampletime[i] - sampletime[i - 1]),
349 kb_to_graph(top - bottom));
355 /* overlay all the text labels */
356 for (i = 1; i < samples ; i++) {
363 /* put all the small pss blocks into the bottom */
365 while (ps->next_ps) {
369 if (ps->sample[i].pss <= (100 * scale_y))
370 top += ps->sample[i].pss;
375 /* now plot the ones that are of significant size */
377 while (ps->next_ps) {
381 /* don't draw anything smaller than 2mb */
382 if (ps->sample[i].pss > (100 * scale_y)) {
383 top = bottom + ps->sample[i].pss;
384 /* draw a label with the process / PID */
385 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
386 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
387 time_to_graph(sampletime[i] - graph_start),
388 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
396 /* debug output - full data dump */
397 svg("\n\n<!-- PSS map - csv format -->\n");
399 while (ps->next_ps) {
400 char _cleanup_free_*enc_name;
405 enc_name = xml_comment_encode(ps->name);
409 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
411 for (i = 0; i < samples ; i++) {
412 svg("%d," , ps->sample[i].pss);
419 static void svg_io_bi_bar(void)
426 svg("<!-- IO utilization graph - In -->\n");
428 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
431 * calculate rounding range
433 * We need to round IO data since IO block data is not updated on
434 * each poll. Applying a smoothing function loses some burst data,
435 * so keep the smoothing range short.
437 range = 0.25 / (1.0 / hz);
439 range = 2.0; /* no smoothing */
441 /* surrounding box */
444 /* find the max IO first */
445 for (i = 1; i < samples; i++) {
450 start = max(i - ((range / 2) - 1), 0);
451 stop = min(i + (range / 2), samples - 1);
453 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
459 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
466 for (i = 1; i < samples; i++) {
472 start = max(i - ((range / 2) - 1), 0);
473 stop = min(i + (range / 2), samples);
475 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
480 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
481 time_to_graph(sampletime[i - 1] - graph_start),
482 (scale_y * 5) - (pbi * (scale_y * 5)),
483 time_to_graph(sampletime[i] - sampletime[i - 1]),
484 pbi * (scale_y * 5));
486 /* labels around highest value */
488 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
489 time_to_graph(sampletime[i] - graph_start) + 5,
490 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
491 max / 1024.0 / (interval / 1000000000.0));
496 static void svg_io_bo_bar(void)
503 svg("<!-- IO utilization graph - out -->\n");
505 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
508 * calculate rounding range
510 * We need to round IO data since IO block data is not updated on
511 * each poll. Applying a smoothing function loses some burst data,
512 * so keep the smoothing range short.
514 range = 0.25 / (1.0 / hz);
516 range = 2.0; /* no smoothing */
518 /* surrounding box */
521 /* find the max IO first */
522 for (i = 1; i < samples; i++) {
527 start = max(i - ((range / 2) - 1), 0);
528 stop = min(i + (range / 2), samples - 1);
530 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
534 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
543 for (i = 1; i < samples; i++) {
549 start = max(i - ((range / 2) - 1), 0);
550 stop = min(i + (range / 2), samples);
552 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
557 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
558 time_to_graph(sampletime[i - 1] - graph_start),
559 (scale_y * 5) - (pbo * (scale_y * 5)),
560 time_to_graph(sampletime[i] - sampletime[i - 1]),
561 pbo * (scale_y * 5));
563 /* labels around highest bo value */
565 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
566 time_to_graph(sampletime[i] - graph_start) + 5,
567 ((scale_y * 5) - (pbo * (scale_y * 5))),
568 max / 1024.0 / (interval / 1000000000.0));
574 static void svg_cpu_bar(void)
578 svg("<!-- CPU utilization graph -->\n");
580 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
581 /* surrounding box */
584 /* bars for each sample, proportional to the CPU util. */
585 for (i = 1; i < samples; i++) {
592 for (c = 0; c < cpus; c++)
593 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
595 trt = trt / 1000000000.0;
597 trt = trt / (double)cpus;
600 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
606 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
607 time_to_graph(sampletime[i - 1] - graph_start),
608 (scale_y * 5) - (ptrt * (scale_y * 5)),
609 time_to_graph(sampletime[i] - sampletime[i - 1]),
610 ptrt * (scale_y * 5));
615 static void svg_wait_bar(void)
619 svg("<!-- Wait time aggregation box -->\n");
621 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
623 /* surrounding box */
626 /* bars for each sample, proportional to the CPU util. */
627 for (i = 1; i < samples; i++) {
634 for (c = 0; c < cpus; c++)
635 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
637 twt = twt / 1000000000.0;
639 twt = twt / (double)cpus;
642 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
648 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
649 time_to_graph(sampletime[i - 1] - graph_start),
650 ((scale_y * 5) - (ptwt * (scale_y * 5))),
651 time_to_graph(sampletime[i] - sampletime[i - 1]),
652 ptwt * (scale_y * 5));
658 static void svg_entropy_bar(void)
662 svg("<!-- entropy pool graph -->\n");
664 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
665 /* surrounding box */
668 /* bars for each sample, scale 0-4096 */
669 for (i = 1; i < samples; i++) {
670 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
671 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
672 time_to_graph(sampletime[i - 1] - graph_start),
673 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
674 time_to_graph(sampletime[i] - sampletime[i - 1]),
675 (entropy_avail[i] / 4096.) * (scale_y * 5));
680 static struct ps_struct *get_next_ps(struct ps_struct *ps)
683 * walk the list of processes and return the next one to be
697 /* go back for parent siblings */
700 if (ps->parent->next)
701 return ps->parent->next;
711 static int ps_filter(struct ps_struct *ps)
716 /* can't draw data when there is only 1 sample (need start + stop) */
717 if (ps->first == ps->last)
720 /* don't filter kthreadd */
724 /* drop stuff that doesn't use any real CPU time */
725 if (ps->total <= 0.001)
732 static void svg_do_initcall(int count_only)
734 FILE _cleanup_pclose_ *f = NULL;
740 /* can't plot initcall when disabled or in relative mode */
741 if (!initcall || relative) {
747 svg("<!-- initcall -->\n");
749 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
750 /* surrounding box */
751 svg_graph_box(kcount);
757 * Initcall graphing - parses dmesg buffer and displays kernel threads
758 * This somewhat uses the same methods and scaling to show processes
759 * but looks a lot simpler. It's overlaid entirely onto the PS graph
763 f = popen("dmesg", "r");
772 if (fgets(l, sizeof(l) - 1, f) == NULL)
775 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
776 &t, func, &ret, &usecs);
778 /* also parse initcalls done by module loading */
779 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
780 &t, func, &ret, &usecs);
785 /* chop the +0xXX/0xXX stuff */
786 while(func[z] != '+')
791 /* filter out irrelevant stuff */
797 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
798 func, t, usecs, ret);
804 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
805 time_to_graph(t - (usecs / 1000000.0)),
807 time_to_graph(usecs / 1000000.0),
811 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
812 time_to_graph(t - (usecs / 1000000.0)) + 5,
813 ps_to_graph(kcount) + 15,
822 static void svg_ps_bars(void)
824 struct ps_struct *ps;
830 svg("<!-- Process graph -->\n");
832 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
834 /* surrounding box */
835 svg_graph_box(pcount);
837 /* pass 2 - ps boxes */
839 while ((ps = get_next_ps(ps))) {
840 char _cleanup_free_*enc_name;
848 enc_name = xml_comment_encode(ps->name);
852 /* leave some trace of what we actually filtered etc. */
853 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
854 ps->ppid, ps->total);
856 /* it would be nice if we could use exec_start from /proc/pid/sched,
857 * but it's unreliable and gives bogus numbers */
858 starttime = sampletime[ps->first];
860 if (!ps_filter(ps)) {
861 /* remember where _to_ our children need to draw a line */
862 ps->pos_x = time_to_graph(starttime - graph_start);
863 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
865 /* hook children to our parent coords instead */
866 ps->pos_x = ps->parent->pos_x;
867 ps->pos_y = ps->parent->pos_y;
869 /* if this is the last child, we might still need to draw a connecting line */
870 if ((!ps->next) && (ps->parent))
871 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
873 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
879 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
880 time_to_graph(starttime - graph_start),
882 time_to_graph(sampletime[ps->last] - starttime),
885 /* paint cpu load over these */
886 for (t = ps->first + 1; t < ps->last; t++) {
890 /* calculate over interval */
891 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
892 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
894 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
895 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
897 /* this can happen if timekeeping isn't accurate enough */
903 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
906 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
907 time_to_graph(sampletime[t - 1] - graph_start),
909 time_to_graph(sampletime[t] - sampletime[t - 1]),
912 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
913 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
914 time_to_graph(sampletime[t - 1] - graph_start),
915 ps_to_graph(j + (1.0 - prt)),
916 time_to_graph(sampletime[t] - sampletime[t - 1]),
920 /* determine where to display the process name */
921 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
922 /* too small to fit label inside the box */
927 /* text label of process name */
928 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
929 time_to_graph(sampletime[w] - graph_start) + 5.0,
930 ps_to_graph(j) + 14.0,
933 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
934 /* paint lines to the parent process */
936 /* horizontal part */
937 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
938 time_to_graph(starttime - graph_start),
939 ps_to_graph(j) + 10.0,
941 ps_to_graph(j) + 10.0);
943 /* one vertical line connecting all the horizontal ones up */
945 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
947 ps_to_graph(j) + 10.0,
952 j++; /* count boxes */
957 /* last pass - determine when idle */
959 /* make sure we start counting from the point where we actually have
960 * data: assume that bootchart's first sample is when data started
963 while (ps->next_ps) {
969 for (i = ps->first; i < samples - (hz / 2); i++) {
974 /* subtract bootchart cpu utilization from total */
976 for (c = 0; c < cpus; c++)
977 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
978 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
981 * our definition of "idle":
983 * if for (hz / 2) we've used less CPU than (interval / 2) ...
984 * defaults to 4.0%, which experimentally, is where atom idles
986 if ((crt - brt) < (interval / 2.0)) {
987 idletime = sampletime[i] - graph_start;
988 svg("\n<!-- idle detected at %.03f seconds -->\n",
990 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
991 time_to_graph(idletime),
993 time_to_graph(idletime),
994 ps_to_graph(pcount) + scale_y);
995 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
996 time_to_graph(idletime) + 5.0,
997 ps_to_graph(pcount) + scale_y,
1005 static void svg_top_ten_cpu(void)
1007 struct ps_struct *top[10];
1008 struct ps_struct emptyps;
1009 struct ps_struct *ps;
1012 memset(&emptyps, 0, sizeof(struct ps_struct));
1013 for (n=0; n < 10; n++)
1016 /* walk all ps's and setup ptrs */
1018 while ((ps = get_next_ps(ps))) {
1019 for (n = 0; n < 10; n++) {
1020 if (ps->total <= top[n]->total)
1022 /* cascade insert */
1023 for (m = 9; m > n; m--)
1030 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1031 for (n = 0; n < 10; n++)
1032 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1040 static void svg_top_ten_pss(void)
1042 struct ps_struct *top[10];
1043 struct ps_struct emptyps;
1044 struct ps_struct *ps;
1047 memset(&emptyps, 0, sizeof(struct ps_struct));
1048 for (n=0; n < 10; n++)
1051 /* walk all ps's and setup ptrs */
1053 while ((ps = get_next_ps(ps))) {
1054 for (n = 0; n < 10; n++) {
1055 if (ps->pss_max <= top[n]->pss_max)
1057 /* cascade insert */
1058 for (m = 9; m > n; m--)
1065 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1066 for (n = 0; n < 10; n++)
1067 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1075 void svg_do(const char *build)
1077 struct ps_struct *ps;
1079 memset(&str, 0, sizeof(str));
1083 /* count initcall thread count first */
1085 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1087 /* then count processes */
1088 while ((ps = get_next_ps(ps))) {
1094 psize = ps_to_graph(pcount) + (scale_y * 2);
1096 esize = (entropy ? scale_y * 7 : 0);
1098 /* after this, we can draw the header with proper sizing */
1101 svg("<g transform=\"translate(10,400)\">\n");
1105 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1109 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1113 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1118 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1123 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1127 svg("<g transform=\"translate(10, 0)\">\n");
1131 svg("<g transform=\"translate(10,200)\">\n");
1136 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1142 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1146 svg("<g transform=\"translate(410,200)\">\n");