1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Corporation
9 Auke Kok <auke-jan.h.kok@intel.com>
11 systemd is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2.1 of the License, or
14 (at your option) any later version.
16 systemd is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include <sys/utsname.h>
40 #include "bootchart.h"
43 #define time_to_graph(t) ((t) * arg_scale_x)
44 #define ps_to_graph(n) ((n) * arg_scale_y)
45 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
46 #define to_color(n) (192.0 - ((n) * 192.0))
48 static char str[8092];
50 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
52 static const char * const colorwheel[12] = {
53 "rgb(255,32,32)", // red
54 "rgb(32,192,192)", // cyan
55 "rgb(255,128,32)", // orange
56 "rgb(128,32,192)", // blue-violet
57 "rgb(255,255,32)", // yellow
58 "rgb(192,32,128)", // red-violet
59 "rgb(32,255,32)", // green
60 "rgb(255,64,32)", // red-orange
61 "rgb(32,32,255)", // blue
62 "rgb(255,192,32)", // yellow-orange
63 "rgb(192,32,192)", // violet
64 "rgb(32,192,32)" // yellow-green
67 static double idletime = -1.0;
68 static int pfiltered = 0;
69 static int pcount = 0;
70 static int kcount = 0;
71 static double psize = 0;
72 static double ksize = 0;
73 static double esize = 0;
74 static struct list_sample_data *sampledata;
75 static struct list_sample_data *prev_sampledata;
76 extern struct list_sample_data *head;
78 static void svg_header(void) {
81 struct list_sample_data *sampledata_last;
84 LIST_FIND_TAIL(link, sampledata, head);
85 sampledata_last = head;
86 LIST_FOREACH_BEFORE(link, sampledata, head) {
87 sampledata_last = sampledata;
90 /* min width is about 1600px due to the label */
91 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
92 w = ((w < 1600.0) ? 1600.0 : w);
94 /* height is variable based on pss, psize, ksize */
95 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
96 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
97 + psize + ksize + esize;
99 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
100 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
101 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
103 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
104 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
106 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
108 /* write some basic info as a comment, including some help */
109 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
110 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
111 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
112 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
113 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
115 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
116 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
117 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
118 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
119 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
120 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
123 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
125 svg(" rect { stroke-width: 1; }\n");
126 svg(" rect.bg { fill: rgb(255,255,255); }\n");
127 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
128 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
129 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
130 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
132 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
133 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
134 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
135 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
136 svg("// line.sec1 { }\n");
137 svg(" line.sec5 { stroke-width: 2; }\n");
138 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
139 svg(" line.dot { stroke-dasharray: 2 4; }\n");
140 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
142 svg(" .run { font-size: 8; font-style: italic; }\n");
143 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
144 svg(" text.sec { font-size: 8; }\n");
145 svg(" text.t1 { font-size: 24; }\n");
146 svg(" text.t2 { font-size: 12; }\n");
147 svg(" text.idle { font-size: 18; }\n");
149 svg(" ]]>\n </style>\n</defs>\n\n");
152 static void svg_title(const char *build) {
153 char cmdline[256] = "";
154 char filename[PATH_MAX];
156 char rootbdev[16] = "Unknown";
157 char model[256] = "Unknown";
158 char date[256] = "Unknown";
159 char cpu[256] = "Unknown";
166 /* grab /proc/cmdline */
167 fd = openat(procfd, "cmdline", O_RDONLY);
170 if (!fgets(cmdline, 255, f))
171 sprintf(cmdline, "Unknown");
175 /* extract root fs so we can find disk model name in sysfs */
176 /* FIXME: this works only in the simple case */
177 c = strstr(cmdline, "root=/dev/");
179 strncpy(rootbdev, &c[10], 3);
181 sprintf(filename, "block/%s/device/model", rootbdev);
182 fd = openat(sysfd, filename, O_RDONLY);
185 if (!fgets(model, 255, f))
186 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
191 /* various utsname parameters */
193 fprintf(stderr, "Error getting uname info\n");
197 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
200 fd = openat(procfd, "cpuinfo", O_RDONLY);
203 while (fgets(buf, 255, f)) {
204 if (strstr(buf, "model name")) {
205 strncpy(cpu, &buf[13], 255);
212 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
215 uts.sysname, uts.release, uts.version, uts.machine);
216 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
218 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
220 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
222 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
225 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
228 svg("%.03fs", idletime);
232 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",
233 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
236 static void svg_graph_box(int height) {
239 double finalsample = 0.0;
240 struct list_sample_data *sampledata_last;
242 sampledata_last = head;
243 LIST_FOREACH_BEFORE(link, sampledata, head) {
244 sampledata_last = sampledata;
247 finalsample = sampledata_last->sampletime;
249 /* outside box, fill */
250 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
252 time_to_graph(finalsample - graph_start),
253 ps_to_graph(height));
255 for (d = graph_start; d <= finalsample;
256 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
257 /* lines for each second */
259 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
260 time_to_graph(d - graph_start),
261 time_to_graph(d - graph_start),
262 ps_to_graph(height));
263 else if (i % 10 == 0)
264 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
265 time_to_graph(d - graph_start),
266 time_to_graph(d - graph_start),
267 ps_to_graph(height));
269 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
270 time_to_graph(d - graph_start),
271 time_to_graph(d - graph_start),
272 ps_to_graph(height));
276 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
277 time_to_graph(d - graph_start),
285 /* xml comments must not contain "--" */
286 static char* xml_comment_encode(const char* name) {
289 enc_name = strdup(name);
293 for (p = enc_name; *p; p++)
294 if (p[0] == '-' && p[1] == '-')
300 static void svg_pss_graph(void) {
301 struct ps_struct *ps;
303 struct list_sample_data *sampledata_last;
305 sampledata_last = head;
306 LIST_FOREACH_BEFORE(link, sampledata, head) {
307 sampledata_last = sampledata;
311 svg("\n\n<!-- Pss memory size graph -->\n");
313 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
315 /* vsize 1000 == 1000mb */
317 /* draw some hlines for usable memory sizes */
318 for (i = 100000; i < 1000000; i += 100000) {
319 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
322 time_to_graph(sampledata_last->sampletime - graph_start),
324 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
325 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
326 kb_to_graph(i), (1000000 - i) / 1000);
330 /* now plot the graph itself */
332 prev_sampledata = head;
333 LIST_FOREACH_BEFORE(link, sampledata, head) {
336 struct ps_sched_struct *cross_place;
341 /* put all the small pss blocks into the bottom */
343 while (ps->next_ps) {
347 ps->sample = ps->first;
348 while (ps->sample->next) {
349 ps->sample = ps->sample->next;
350 if (ps->sample->sampledata == sampledata)
353 if (ps->sample->sampledata == sampledata) {
354 if (ps->sample->pss <= (100 * arg_scale_y))
355 top += ps->sample->pss;
359 while (ps->sample->cross) {
360 cross_place = ps->sample->cross;
361 ps = ps->sample->cross->ps_new;
362 ps->sample = cross_place;
363 if (ps->sample->pss <= (100 * arg_scale_y))
364 top += ps->sample->pss;
367 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
369 time_to_graph(prev_sampledata->sampletime - graph_start),
370 kb_to_graph(1000000.0 - top),
371 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
372 kb_to_graph(top - bottom));
375 /* now plot the ones that are of significant size */
377 while (ps->next_ps) {
381 ps->sample = ps->first;
382 while (ps->sample->next) {
383 ps->sample = ps->sample->next;
384 if (ps->sample->sampledata == sampledata)
387 /* don't draw anything smaller than 2mb */
388 if (ps->sample->sampledata == sampledata) {
389 if (ps->sample->pss > (100 * arg_scale_y)) {
390 top = bottom + ps->sample->pss;
391 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
392 colorwheel[ps->pid % 12],
393 time_to_graph(prev_sampledata->sampletime - graph_start),
394 kb_to_graph(1000000.0 - top),
395 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
396 kb_to_graph(top - bottom));
402 while ((cross_place = ps->sample->cross)) {
403 ps = ps->sample->cross->ps_new;
404 ps->sample = cross_place;
405 if (ps->sample->pss > (100 * arg_scale_y)) {
406 top = bottom + ps->sample->pss;
407 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
408 colorwheel[ps->pid % 12],
409 time_to_graph(prev_sampledata->sampletime - graph_start),
410 kb_to_graph(1000000.0 - top),
411 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
412 kb_to_graph(top - bottom));
416 prev_sampledata = sampledata;
420 /* overlay all the text labels */
422 LIST_FOREACH_BEFORE(link, sampledata, head) {
425 struct ps_sched_struct *prev_sample;
426 struct ps_sched_struct *cross_place;
428 /* put all the small pss blocks into the bottom */
429 ps = ps_first->next_ps;
430 while (ps->next_ps) {
434 ps->sample = ps->first;
435 while (ps->sample->next) {
436 ps->sample = ps->sample->next;
437 if (ps->sample->sampledata == sampledata)
440 if (ps->sample->sampledata == sampledata) {
441 if (ps->sample->pss <= (100 * arg_scale_y))
442 top += ps->sample->pss;
446 while ((cross_place = ps->sample->cross)) {
447 ps = ps->sample->cross->ps_new;
448 ps->sample = cross_place;
449 if (ps->sample->pss <= (100 * arg_scale_y))
450 top += ps->sample->pss;
454 /* now plot the ones that are of significant size */
456 while (ps->next_ps) {
457 prev_sample = ps->sample;
461 ps->sample = ps->first;
462 while (ps->sample->next) {
463 prev_sample = ps->sample;
464 ps->sample = ps->sample->next;
465 if (ps->sample->sampledata == sampledata)
468 /* don't draw anything smaller than 2mb */
469 if (ps->sample->sampledata == sampledata) {
470 if (ps->sample->pss > (100 * arg_scale_y)) {
471 top = bottom + ps->sample->pss;
472 /* draw a label with the process / PID */
473 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
474 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
475 time_to_graph(sampledata->sampletime - graph_start),
476 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
484 while ((cross_place = ps->sample->cross)) {
485 ps = ps->sample->cross->ps_new;
486 ps->sample = cross_place;
487 prev_sample = ps->sample->prev;
488 if (ps->sample->pss > (100 * arg_scale_y)) {
489 top = bottom + ps->sample->pss;
490 /* draw a label with the process / PID */
491 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
492 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
493 time_to_graph(sampledata->sampletime - graph_start),
494 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
503 /* debug output - full data dump */
504 svg("\n\n<!-- PSS map - csv format -->\n");
506 while (ps->next_ps) {
507 _cleanup_free_ char *enc_name = NULL;
512 enc_name = xml_comment_encode(ps->name);
516 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
518 ps->sample = ps->first;
519 while (ps->sample->next) {
520 ps->sample = ps->sample->next;
521 svg("%d," , ps->sample->pss);
528 static void svg_io_bi_bar(void) {
534 struct list_sample_data *start_sampledata;
535 struct list_sample_data *stop_sampledata;
537 svg("<!-- IO utilization graph - In -->\n");
539 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
542 * calculate rounding range
544 * We need to round IO data since IO block data is not updated on
545 * each poll. Applying a smoothing function loses some burst data,
546 * so keep the smoothing range short.
548 range = 0.25 / (1.0 / arg_hz);
550 range = 2.0; /* no smoothing */
552 /* surrounding box */
555 /* find the max IO first */
557 LIST_FOREACH_BEFORE(link, sampledata, head) {
563 start = MAX(i - ((range / 2) - 1), 0);
564 stop = MIN(i + (range / 2), samples - 1);
565 diff = (stop - start);
567 start_sampledata = sampledata;
568 stop_sampledata = sampledata;
570 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
571 start_sampledata = start_sampledata->link_next;
572 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
573 stop_sampledata = stop_sampledata->link_prev;
575 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
583 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
594 prev_sampledata = head;
595 LIST_FOREACH_BEFORE(link, sampledata, head) {
602 start = MAX(i - ((range / 2) - 1), 0);
603 stop = MIN(i + (range / 2), samples);
604 diff = (stop - start);
606 start_sampledata = sampledata;
607 stop_sampledata = sampledata;
609 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
610 start_sampledata = start_sampledata->link_next;
611 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
612 stop_sampledata = stop_sampledata->link_prev;
614 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
621 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
622 time_to_graph(prev_sampledata->sampletime - graph_start),
623 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
624 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
625 pbi * (arg_scale_y * 5));
627 /* labels around highest value */
629 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
630 time_to_graph(sampledata->sampletime - graph_start) + 5,
631 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
632 max / 1024.0 / (interval / 1000000000.0));
635 prev_sampledata = sampledata;
639 static void svg_io_bo_bar(void) {
645 struct list_sample_data *start_sampledata;
646 struct list_sample_data *stop_sampledata;
648 svg("<!-- IO utilization graph - out -->\n");
650 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
653 * calculate rounding range
655 * We need to round IO data since IO block data is not updated on
656 * each poll. Applying a smoothing function loses some burst data,
657 * so keep the smoothing range short.
659 range = 0.25 / (1.0 / arg_hz);
661 range = 2.0; /* no smoothing */
663 /* surrounding box */
666 /* find the max IO first */
668 LIST_FOREACH_BEFORE(link, sampledata, head) {
674 start = MAX(i - ((range / 2) - 1), 0);
675 stop = MIN(i + (range / 2), samples - 1);
676 diff = (stop - start);
678 start_sampledata = sampledata;
679 stop_sampledata = sampledata;
681 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
682 start_sampledata = start_sampledata->link_next;
683 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
684 stop_sampledata = stop_sampledata->link_prev;
686 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
690 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
700 prev_sampledata = head;
702 LIST_FOREACH_BEFORE(link, sampledata, head) {
711 start = MAX(i - ((range / 2) - 1), 0);
712 stop = MIN(i + (range / 2), samples);
713 diff = (stop - start);
715 start_sampledata = sampledata;
716 stop_sampledata = sampledata;
718 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
719 start_sampledata = start_sampledata->link_next;
720 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
721 stop_sampledata = stop_sampledata->link_prev;
723 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
730 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
731 time_to_graph(prev_sampledata->sampletime - graph_start),
732 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
733 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
734 pbo * (arg_scale_y * 5));
736 /* labels around highest bo value */
738 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
739 time_to_graph(sampledata->sampletime - graph_start) + 5,
740 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
741 max / 1024.0 / (interval / 1000000000.0));
744 prev_sampledata = sampledata;
748 static void svg_cpu_bar(void) {
750 svg("<!-- CPU utilization graph -->\n");
752 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
753 /* surrounding box */
756 /* bars for each sample, proportional to the CPU util. */
757 prev_sampledata = head;
758 LIST_FOREACH_BEFORE(link, sampledata, head) {
765 for (c = 0; c < cpus; c++)
766 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
768 trt = trt / 1000000000.0;
770 trt = trt / (double)cpus;
773 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
779 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
780 time_to_graph(prev_sampledata->sampletime - graph_start),
781 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
782 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
783 ptrt * (arg_scale_y * 5));
785 prev_sampledata = sampledata;
789 static void svg_wait_bar(void) {
791 svg("<!-- Wait time aggregation box -->\n");
793 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
795 /* surrounding box */
798 /* bars for each sample, proportional to the CPU util. */
799 prev_sampledata = head;
800 LIST_FOREACH_BEFORE(link, sampledata, head) {
807 for (c = 0; c < cpus; c++)
808 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
810 twt = twt / 1000000000.0;
812 twt = twt / (double)cpus;
815 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
821 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
822 time_to_graph(prev_sampledata->sampletime - graph_start),
823 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
824 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
825 ptwt * (arg_scale_y * 5));
827 prev_sampledata = sampledata;
832 static void svg_entropy_bar(void) {
834 svg("<!-- entropy pool graph -->\n");
836 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
837 /* surrounding box */
840 /* bars for each sample, scale 0-4096 */
841 prev_sampledata = head;
842 LIST_FOREACH_BEFORE(link, sampledata, head) {
843 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
844 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
845 time_to_graph(prev_sampledata->sampletime - graph_start),
846 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
847 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
848 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
849 prev_sampledata = sampledata;
853 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
855 * walk the list of processes and return the next one to be
869 /* go back for parent siblings */
872 if (ps->parent->next)
873 return ps->parent->next;
882 static int ps_filter(struct ps_struct *ps) {
886 /* can't draw data when there is only 1 sample (need start + stop) */
887 if (ps->first == ps->last)
890 /* don't filter kthreadd */
894 /* drop stuff that doesn't use any real CPU time */
895 if (ps->total <= 0.001)
901 static void svg_do_initcall(int count_only) {
902 _cleanup_pclose_ FILE *f = NULL;
908 /* can't plot initcall when disabled or in relative mode */
909 if (!initcall || arg_relative) {
915 svg("<!-- initcall -->\n");
917 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
918 /* surrounding box */
919 svg_graph_box(kcount);
925 * Initcall graphing - parses dmesg buffer and displays kernel threads
926 * This somewhat uses the same methods and scaling to show processes
927 * but looks a lot simpler. It's overlaid entirely onto the PS graph
931 f = popen("dmesg", "r");
940 if (fgets(l, sizeof(l) - 1, f) == NULL)
943 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
944 &t, func, &ret, &usecs);
946 /* also parse initcalls done by module loading */
947 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
948 &t, func, &ret, &usecs);
953 /* chop the +0xXX/0xXX stuff */
954 while(func[z] != '+')
959 /* filter out irrelevant stuff */
965 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
966 func, t, usecs, ret);
972 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
973 time_to_graph(t - (usecs / 1000000.0)),
975 time_to_graph(usecs / 1000000.0),
979 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
980 time_to_graph(t - (usecs / 1000000.0)) + 5,
981 ps_to_graph(kcount) + 15,
989 static void svg_ps_bars(void) {
990 struct ps_struct *ps;
996 svg("<!-- Process graph -->\n");
998 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1000 /* surrounding box */
1001 svg_graph_box(pcount);
1003 /* pass 2 - ps boxes */
1005 while ((ps = get_next_ps(ps))) {
1006 _cleanup_free_ char *enc_name = NULL;
1011 enc_name = xml_comment_encode(ps->name);
1015 /* leave some trace of what we actually filtered etc. */
1016 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1017 ps->ppid, ps->total);
1019 starttime = ps->first->sampledata->sampletime;
1021 if (!ps_filter(ps)) {
1022 /* remember where _to_ our children need to draw a line */
1023 ps->pos_x = time_to_graph(starttime - graph_start);
1024 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1025 } else if (ps->parent){
1026 /* hook children to our parent coords instead */
1027 ps->pos_x = ps->parent->pos_x;
1028 ps->pos_y = ps->parent->pos_y;
1030 /* if this is the last child, we might still need to draw a connecting line */
1031 if ((!ps->next) && (ps->parent))
1032 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1034 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1040 endtime = ps->last->sampledata->sampletime;
1041 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1042 time_to_graph(starttime - graph_start),
1044 time_to_graph(ps->last->sampledata->sampletime - starttime),
1047 /* paint cpu load over these */
1048 ps->sample = ps->first;
1050 while (ps->sample->next) {
1053 struct ps_sched_struct *prev;
1056 ps->sample = ps->sample->next;
1058 /* calculate over interval */
1059 rt = ps->sample->runtime - prev->runtime;
1060 wt = ps->sample->waittime - prev->waittime;
1062 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1063 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1065 /* this can happen if timekeeping isn't accurate enough */
1071 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1074 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1075 time_to_graph(prev->sampledata->sampletime - graph_start),
1077 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1080 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1081 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1082 time_to_graph(prev->sampledata->sampletime - graph_start),
1083 ps_to_graph(j + (1.0 - prt)),
1084 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1089 /* determine where to display the process name */
1090 if ((endtime - starttime) < 1.5)
1091 /* too small to fit label inside the box */
1096 /* text label of process name */
1097 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1098 time_to_graph(w - graph_start) + 5.0,
1099 ps_to_graph(j) + 14.0,
1102 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1103 arg_show_cgroup ? ps->cgroup : "");
1104 /* paint lines to the parent process */
1106 /* horizontal part */
1107 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1108 time_to_graph(starttime - graph_start),
1109 ps_to_graph(j) + 10.0,
1111 ps_to_graph(j) + 10.0);
1113 /* one vertical line connecting all the horizontal ones up */
1115 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1117 ps_to_graph(j) + 10.0,
1122 j++; /* count boxes */
1127 /* last pass - determine when idle */
1129 /* make sure we start counting from the point where we actually have
1130 * data: assume that bootchart's first sample is when data started
1134 while (ps->next_ps) {
1140 /* need to know last node first */
1141 ps->sample = ps->first;
1142 i = ps->sample->next->sampledata->counter;
1144 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1149 struct ps_sched_struct *sample_hz;
1151 ps->sample = ps->sample->next;
1152 sample_hz = ps->sample;
1153 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1154 sample_hz = sample_hz->next;
1156 /* subtract bootchart cpu utilization from total */
1158 for (c = 0; c < cpus; c++)
1159 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1160 brt = sample_hz->runtime - ps->sample->runtime;
1162 * our definition of "idle":
1164 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1165 * defaults to 4.0%, which experimentally, is where atom idles
1167 if ((crt - brt) < (interval / 2.0)) {
1168 idletime = ps->sample->sampledata->sampletime - graph_start;
1169 svg("\n<!-- idle detected at %.03f seconds -->\n",
1171 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1172 time_to_graph(idletime),
1174 time_to_graph(idletime),
1175 ps_to_graph(pcount) + arg_scale_y);
1176 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1177 time_to_graph(idletime) + 5.0,
1178 ps_to_graph(pcount) + arg_scale_y,
1186 static void svg_top_ten_cpu(void) {
1187 struct ps_struct *top[10];
1188 struct ps_struct emptyps = {};
1189 struct ps_struct *ps;
1192 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1195 /* walk all ps's and setup ptrs */
1197 while ((ps = get_next_ps(ps))) {
1198 for (n = 0; n < 10; n++) {
1199 if (ps->total <= top[n]->total)
1201 /* cascade insert */
1202 for (m = 9; m > n; m--)
1209 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1210 for (n = 0; n < 10; n++)
1211 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1218 static void svg_top_ten_pss(void) {
1219 struct ps_struct *top[10];
1220 struct ps_struct emptyps = {};
1221 struct ps_struct *ps;
1224 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1227 /* walk all ps's and setup ptrs */
1229 while ((ps = get_next_ps(ps))) {
1230 for (n = 0; n < 10; n++) {
1231 if (ps->pss_max <= top[n]->pss_max)
1233 /* cascade insert */
1234 for (m = 9; m > n; m--)
1241 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1242 for (n = 0; n < 10; n++)
1243 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1250 void svg_do(const char *build) {
1251 struct ps_struct *ps;
1253 memzero(&str, sizeof(str));
1257 /* count initcall thread count first */
1259 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1261 /* then count processes */
1262 while ((ps = get_next_ps(ps))) {
1268 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1270 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1272 /* after this, we can draw the header with proper sizing */
1274 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1276 svg("<g transform=\"translate(10,400)\">\n");
1280 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1284 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1288 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1293 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1298 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1302 svg("<g transform=\"translate(10, 0)\">\n");
1306 svg("<g transform=\"translate(10,200)\">\n");
1311 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1317 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1321 svg("<g transform=\"translate(410,200)\">\n");