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;
86 LIST_FIND_TAIL(link, sampledata, head);
87 sampledata_last = head;
88 LIST_FOREACH_BEFORE(link, sampledata, head) {
89 sampledata_last = sampledata;
92 /* min width is about 1600px due to the label */
93 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
94 w = ((w < 1600.0) ? 1600.0 : w);
96 /* height is variable based on pss, psize, ksize */
97 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
98 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
99 + psize + ksize + esize;
101 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
102 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
103 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
105 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
106 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
108 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
110 /* write some basic info as a comment, including some help */
111 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
112 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
113 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
114 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
115 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
117 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
118 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
119 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
120 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
121 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
122 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
125 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
127 svg(" rect { stroke-width: 1; }\n");
128 svg(" rect.bg { fill: rgb(255,255,255); }\n");
129 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
130 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
132 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
133 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
134 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
135 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
136 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
137 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
138 svg("// line.sec1 { }\n");
139 svg(" line.sec5 { stroke-width: 2; }\n");
140 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
141 svg(" line.dot { stroke-dasharray: 2 4; }\n");
142 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
144 svg(" .run { font-size: 8; font-style: italic; }\n");
145 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
146 svg(" text.sec { font-size: 8; }\n");
147 svg(" text.t1 { font-size: 24; }\n");
148 svg(" text.t2 { font-size: 12; }\n");
149 svg(" text.idle { font-size: 18; }\n");
151 svg(" ]]>\n </style>\n</defs>\n\n");
154 static void svg_title(const char *build) {
155 char cmdline[256] = "";
156 char filename[PATH_MAX];
158 char rootbdev[16] = "Unknown";
159 char model[256] = "Unknown";
160 char date[256] = "Unknown";
161 char cpu[256] = "Unknown";
168 /* grab /proc/cmdline */
169 fd = openat(procfd, "cmdline", O_RDONLY);
172 if (!fgets(cmdline, 255, f))
173 sprintf(cmdline, "Unknown");
177 /* extract root fs so we can find disk model name in sysfs */
178 /* FIXME: this works only in the simple case */
179 c = strstr(cmdline, "root=/dev/");
181 strncpy(rootbdev, &c[10], 3);
183 sprintf(filename, "block/%s/device/model", rootbdev);
184 fd = openat(sysfd, filename, O_RDONLY);
187 if (!fgets(model, 255, f))
188 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
193 /* various utsname parameters */
195 fprintf(stderr, "Error getting uname info\n");
199 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
202 fd = openat(procfd, "cpuinfo", O_RDONLY);
205 while (fgets(buf, 255, f)) {
206 if (strstr(buf, "model name")) {
207 strncpy(cpu, &buf[13], 255);
214 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
216 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
217 uts.sysname, uts.release, uts.version, uts.machine);
218 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
220 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
222 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
226 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
227 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
230 svg("%.03fs", idletime);
234 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",
235 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
238 static void svg_graph_box(int height) {
241 double finalsample = 0.0;
242 struct list_sample_data *sampledata_last;
244 sampledata_last = head;
245 LIST_FOREACH_BEFORE(link, sampledata, head) {
246 sampledata_last = sampledata;
249 finalsample = sampledata_last->sampletime;
251 /* outside box, fill */
252 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
254 time_to_graph(finalsample - graph_start),
255 ps_to_graph(height));
257 for (d = graph_start; d <= finalsample;
258 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
259 /* lines for each second */
261 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
262 time_to_graph(d - graph_start),
263 time_to_graph(d - graph_start),
264 ps_to_graph(height));
265 else if (i % 10 == 0)
266 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
267 time_to_graph(d - graph_start),
268 time_to_graph(d - graph_start),
269 ps_to_graph(height));
271 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
272 time_to_graph(d - graph_start),
273 time_to_graph(d - graph_start),
274 ps_to_graph(height));
278 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
279 time_to_graph(d - graph_start),
287 /* xml comments must not contain "--" */
288 static char* xml_comment_encode(const char* name) {
291 enc_name = strdup(name);
295 for (p = enc_name; *p; p++)
296 if (p[0] == '-' && p[1] == '-')
302 static void svg_pss_graph(void) {
303 struct ps_struct *ps;
305 struct list_sample_data *sampledata_last;
307 sampledata_last = head;
308 LIST_FOREACH_BEFORE(link, sampledata, head) {
309 sampledata_last = sampledata;
313 svg("\n\n<!-- Pss memory size graph -->\n");
315 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
317 /* vsize 1000 == 1000mb */
319 /* draw some hlines for usable memory sizes */
320 for (i = 100000; i < 1000000; i += 100000) {
321 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
324 time_to_graph(sampledata_last->sampletime - graph_start),
326 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
327 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
328 kb_to_graph(i), (1000000 - i) / 1000);
332 /* now plot the graph itself */
334 prev_sampledata = head;
335 LIST_FOREACH_BEFORE(link, sampledata, head) {
338 struct ps_sched_struct *cross_place;
343 /* put all the small pss blocks into the bottom */
345 while (ps->next_ps) {
349 ps->sample = ps->first;
350 while (ps->sample->next) {
351 ps->sample = ps->sample->next;
352 if (ps->sample->sampledata == sampledata)
355 if (ps->sample->sampledata == sampledata) {
356 if (ps->sample->pss <= (100 * arg_scale_y))
357 top += ps->sample->pss;
361 while (ps->sample->cross) {
362 cross_place = ps->sample->cross;
363 ps = ps->sample->cross->ps_new;
364 ps->sample = cross_place;
365 if (ps->sample->pss <= (100 * arg_scale_y))
366 top += ps->sample->pss;
369 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
371 time_to_graph(prev_sampledata->sampletime - graph_start),
372 kb_to_graph(1000000.0 - top),
373 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
374 kb_to_graph(top - bottom));
377 /* now plot the ones that are of significant size */
379 while (ps->next_ps) {
383 ps->sample = ps->first;
384 while (ps->sample->next) {
385 ps->sample = ps->sample->next;
386 if (ps->sample->sampledata == sampledata)
389 /* don't draw anything smaller than 2mb */
390 if (ps->sample->sampledata == sampledata) {
391 if (ps->sample->pss > (100 * arg_scale_y)) {
392 top = bottom + ps->sample->pss;
393 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
394 colorwheel[ps->pid % 12],
395 time_to_graph(prev_sampledata->sampletime - graph_start),
396 kb_to_graph(1000000.0 - top),
397 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
398 kb_to_graph(top - bottom));
404 while ((cross_place = ps->sample->cross)) {
405 ps = ps->sample->cross->ps_new;
406 ps->sample = cross_place;
407 if (ps->sample->pss > (100 * arg_scale_y)) {
408 top = bottom + ps->sample->pss;
409 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
410 colorwheel[ps->pid % 12],
411 time_to_graph(prev_sampledata->sampletime - graph_start),
412 kb_to_graph(1000000.0 - top),
413 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
414 kb_to_graph(top - bottom));
418 prev_sampledata = sampledata;
422 /* overlay all the text labels */
424 LIST_FOREACH_BEFORE(link, sampledata, head) {
427 struct ps_sched_struct *prev_sample;
428 struct ps_sched_struct *cross_place;
430 /* put all the small pss blocks into the bottom */
431 ps = ps_first->next_ps;
432 while (ps->next_ps) {
436 ps->sample = ps->first;
437 while (ps->sample->next) {
438 ps->sample = ps->sample->next;
439 if (ps->sample->sampledata == sampledata)
442 if (ps->sample->sampledata == sampledata) {
443 if (ps->sample->pss <= (100 * arg_scale_y))
444 top += ps->sample->pss;
448 while ((cross_place = ps->sample->cross)) {
449 ps = ps->sample->cross->ps_new;
450 ps->sample = cross_place;
451 if (ps->sample->pss <= (100 * arg_scale_y))
452 top += ps->sample->pss;
456 /* now plot the ones that are of significant size */
458 while (ps->next_ps) {
459 prev_sample = ps->sample;
463 ps->sample = ps->first;
464 while (ps->sample->next) {
465 prev_sample = ps->sample;
466 ps->sample = ps->sample->next;
467 if (ps->sample->sampledata == sampledata)
470 /* don't draw anything smaller than 2mb */
471 if (ps->sample->sampledata == sampledata) {
472 if (ps->sample->pss > (100 * arg_scale_y)) {
473 top = bottom + ps->sample->pss;
474 /* draw a label with the process / PID */
475 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
476 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
477 time_to_graph(sampledata->sampletime - graph_start),
478 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
486 while ((cross_place = ps->sample->cross)) {
487 ps = ps->sample->cross->ps_new;
488 ps->sample = cross_place;
489 prev_sample = ps->sample->prev;
490 if (ps->sample->pss > (100 * arg_scale_y)) {
491 top = bottom + ps->sample->pss;
492 /* draw a label with the process / PID */
493 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
494 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
495 time_to_graph(sampledata->sampletime - graph_start),
496 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
505 /* debug output - full data dump */
506 svg("\n\n<!-- PSS map - csv format -->\n");
508 while (ps->next_ps) {
509 _cleanup_free_ char *enc_name = NULL;
514 enc_name = xml_comment_encode(ps->name);
518 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
520 ps->sample = ps->first;
521 while (ps->sample->next) {
522 ps->sample = ps->sample->next;
523 svg("%d," , ps->sample->pss);
530 static void svg_io_bi_bar(void) {
536 struct list_sample_data *start_sampledata;
537 struct list_sample_data *stop_sampledata;
539 svg("<!-- IO utilization graph - In -->\n");
541 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
544 * calculate rounding range
546 * We need to round IO data since IO block data is not updated on
547 * each poll. Applying a smoothing function loses some burst data,
548 * so keep the smoothing range short.
550 range = 0.25 / (1.0 / arg_hz);
552 range = 2.0; /* no smoothing */
554 /* surrounding box */
557 /* find the max IO first */
559 LIST_FOREACH_BEFORE(link, sampledata, head) {
565 start = MAX(i - ((range / 2) - 1), 0);
566 stop = MIN(i + (range / 2), samples - 1);
567 diff = (stop - start);
569 start_sampledata = sampledata;
570 stop_sampledata = sampledata;
572 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
573 start_sampledata = start_sampledata->link_next;
574 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
575 stop_sampledata = stop_sampledata->link_prev;
577 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
585 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
596 prev_sampledata = head;
597 LIST_FOREACH_BEFORE(link, sampledata, head) {
604 start = MAX(i - ((range / 2) - 1), 0);
605 stop = MIN(i + (range / 2), samples);
606 diff = (stop - start);
608 start_sampledata = sampledata;
609 stop_sampledata = sampledata;
611 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
612 start_sampledata = start_sampledata->link_next;
613 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
614 stop_sampledata = stop_sampledata->link_prev;
616 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
623 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
624 time_to_graph(prev_sampledata->sampletime - graph_start),
625 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
626 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
627 pbi * (arg_scale_y * 5));
629 /* labels around highest value */
631 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
632 time_to_graph(sampledata->sampletime - graph_start) + 5,
633 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
634 max / 1024.0 / (interval / 1000000000.0));
637 prev_sampledata = sampledata;
641 static void svg_io_bo_bar(void) {
647 struct list_sample_data *start_sampledata;
648 struct list_sample_data *stop_sampledata;
650 svg("<!-- IO utilization graph - out -->\n");
652 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
655 * calculate rounding range
657 * We need to round IO data since IO block data is not updated on
658 * each poll. Applying a smoothing function loses some burst data,
659 * so keep the smoothing range short.
661 range = 0.25 / (1.0 / arg_hz);
663 range = 2.0; /* no smoothing */
665 /* surrounding box */
668 /* find the max IO first */
670 LIST_FOREACH_BEFORE(link, sampledata, head) {
676 start = MAX(i - ((range / 2) - 1), 0);
677 stop = MIN(i + (range / 2), samples - 1);
678 diff = (stop - start);
680 start_sampledata = sampledata;
681 stop_sampledata = sampledata;
683 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
684 start_sampledata = start_sampledata->link_next;
685 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
686 stop_sampledata = stop_sampledata->link_prev;
688 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
692 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
702 prev_sampledata = head;
704 LIST_FOREACH_BEFORE(link, sampledata, head) {
713 start = MAX(i - ((range / 2) - 1), 0);
714 stop = MIN(i + (range / 2), samples);
715 diff = (stop - start);
717 start_sampledata = sampledata;
718 stop_sampledata = sampledata;
720 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
721 start_sampledata = start_sampledata->link_next;
722 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
723 stop_sampledata = stop_sampledata->link_prev;
725 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
732 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
733 time_to_graph(prev_sampledata->sampletime - graph_start),
734 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
735 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
736 pbo * (arg_scale_y * 5));
738 /* labels around highest bo value */
740 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
741 time_to_graph(sampledata->sampletime - graph_start) + 5,
742 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
743 max / 1024.0 / (interval / 1000000000.0));
746 prev_sampledata = sampledata;
750 static void svg_cpu_bar(void) {
752 svg("<!-- CPU utilization graph -->\n");
754 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
755 /* surrounding box */
758 /* bars for each sample, proportional to the CPU util. */
759 prev_sampledata = head;
760 LIST_FOREACH_BEFORE(link, sampledata, head) {
767 for (c = 0; c < cpus; c++)
768 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
770 trt = trt / 1000000000.0;
772 trt = trt / (double)cpus;
775 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
781 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
782 time_to_graph(prev_sampledata->sampletime - graph_start),
783 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
784 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
785 ptrt * (arg_scale_y * 5));
787 prev_sampledata = sampledata;
791 static void svg_wait_bar(void) {
793 svg("<!-- Wait time aggregation box -->\n");
795 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
797 /* surrounding box */
800 /* bars for each sample, proportional to the CPU util. */
801 prev_sampledata = head;
802 LIST_FOREACH_BEFORE(link, sampledata, head) {
809 for (c = 0; c < cpus; c++)
810 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
812 twt = twt / 1000000000.0;
814 twt = twt / (double)cpus;
817 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
823 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
824 time_to_graph(prev_sampledata->sampletime - graph_start),
825 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
826 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
827 ptwt * (arg_scale_y * 5));
829 prev_sampledata = sampledata;
834 static void svg_entropy_bar(void) {
836 svg("<!-- entropy pool graph -->\n");
838 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
839 /* surrounding box */
842 /* bars for each sample, scale 0-4096 */
843 prev_sampledata = head;
844 LIST_FOREACH_BEFORE(link, sampledata, head) {
845 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
846 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
847 time_to_graph(prev_sampledata->sampletime - graph_start),
848 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
849 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
850 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
851 prev_sampledata = sampledata;
855 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
857 * walk the list of processes and return the next one to be
871 /* go back for parent siblings */
874 if (ps->parent->next)
875 return ps->parent->next;
884 static int ps_filter(struct ps_struct *ps) {
888 /* can't draw data when there is only 1 sample (need start + stop) */
889 if (ps->first == ps->last)
892 /* don't filter kthreadd */
896 /* drop stuff that doesn't use any real CPU time */
897 if (ps->total <= 0.001)
903 static void svg_do_initcall(int count_only) {
904 _cleanup_pclose_ FILE *f = NULL;
910 /* can't plot initcall when disabled or in relative mode */
911 if (!initcall || arg_relative) {
917 svg("<!-- initcall -->\n");
919 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
920 /* surrounding box */
921 svg_graph_box(kcount);
927 * Initcall graphing - parses dmesg buffer and displays kernel threads
928 * This somewhat uses the same methods and scaling to show processes
929 * but looks a lot simpler. It's overlaid entirely onto the PS graph
933 f = popen("dmesg", "r");
942 if (fgets(l, sizeof(l) - 1, f) == NULL)
945 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
946 &t, func, &ret, &usecs);
948 /* also parse initcalls done by module loading */
949 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
950 &t, func, &ret, &usecs);
955 /* chop the +0xXX/0xXX stuff */
956 while(func[z] != '+')
961 /* filter out irrelevant stuff */
967 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
968 func, t, usecs, ret);
974 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
975 time_to_graph(t - (usecs / 1000000.0)),
977 time_to_graph(usecs / 1000000.0),
981 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
982 time_to_graph(t - (usecs / 1000000.0)) + 5,
983 ps_to_graph(kcount) + 15,
991 static void svg_ps_bars(void) {
992 struct ps_struct *ps;
998 svg("<!-- Process graph -->\n");
1000 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1002 /* surrounding box */
1003 svg_graph_box(pcount);
1005 /* pass 2 - ps boxes */
1007 while ((ps = get_next_ps(ps))) {
1008 _cleanup_free_ char *enc_name = NULL;
1013 enc_name = xml_comment_encode(ps->name);
1017 /* leave some trace of what we actually filtered etc. */
1018 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1019 ps->ppid, ps->total);
1021 starttime = ps->first->sampledata->sampletime;
1023 if (!ps_filter(ps)) {
1024 /* remember where _to_ our children need to draw a line */
1025 ps->pos_x = time_to_graph(starttime - graph_start);
1026 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1027 } else if (ps->parent){
1028 /* hook children to our parent coords instead */
1029 ps->pos_x = ps->parent->pos_x;
1030 ps->pos_y = ps->parent->pos_y;
1032 /* if this is the last child, we might still need to draw a connecting line */
1033 if ((!ps->next) && (ps->parent))
1034 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1036 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1042 endtime = ps->last->sampledata->sampletime;
1043 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1044 time_to_graph(starttime - graph_start),
1046 time_to_graph(ps->last->sampledata->sampletime - starttime),
1049 /* paint cpu load over these */
1050 ps->sample = ps->first;
1052 while (ps->sample->next) {
1055 struct ps_sched_struct *prev;
1058 ps->sample = ps->sample->next;
1060 /* calculate over interval */
1061 rt = ps->sample->runtime - prev->runtime;
1062 wt = ps->sample->waittime - prev->waittime;
1064 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1065 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1067 /* this can happen if timekeeping isn't accurate enough */
1073 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1076 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1077 time_to_graph(prev->sampledata->sampletime - graph_start),
1079 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1082 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1083 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1084 time_to_graph(prev->sampledata->sampletime - graph_start),
1085 ps_to_graph(j + (1.0 - prt)),
1086 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1091 /* determine where to display the process name */
1092 if ((endtime - starttime) < 1.5)
1093 /* too small to fit label inside the box */
1098 /* text label of process name */
1099 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1100 time_to_graph(w - graph_start) + 5.0,
1101 ps_to_graph(j) + 14.0,
1104 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1105 arg_show_cgroup ? ps->cgroup : "");
1106 /* paint lines to the parent process */
1108 /* horizontal part */
1109 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1110 time_to_graph(starttime - graph_start),
1111 ps_to_graph(j) + 10.0,
1113 ps_to_graph(j) + 10.0);
1115 /* one vertical line connecting all the horizontal ones up */
1117 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1119 ps_to_graph(j) + 10.0,
1124 j++; /* count boxes */
1129 /* last pass - determine when idle */
1131 /* make sure we start counting from the point where we actually have
1132 * data: assume that bootchart's first sample is when data started
1136 while (ps->next_ps) {
1142 /* need to know last node first */
1143 ps->sample = ps->first;
1144 i = ps->sample->next->sampledata->counter;
1146 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1151 struct ps_sched_struct *sample_hz;
1153 ps->sample = ps->sample->next;
1154 sample_hz = ps->sample;
1155 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1156 sample_hz = sample_hz->next;
1158 /* subtract bootchart cpu utilization from total */
1160 for (c = 0; c < cpus; c++)
1161 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1162 brt = sample_hz->runtime - ps->sample->runtime;
1164 * our definition of "idle":
1166 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1167 * defaults to 4.0%, which experimentally, is where atom idles
1169 if ((crt - brt) < (interval / 2.0)) {
1170 idletime = ps->sample->sampledata->sampletime - graph_start;
1171 svg("\n<!-- idle detected at %.03f seconds -->\n",
1173 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1174 time_to_graph(idletime),
1176 time_to_graph(idletime),
1177 ps_to_graph(pcount) + arg_scale_y);
1178 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1179 time_to_graph(idletime) + 5.0,
1180 ps_to_graph(pcount) + arg_scale_y,
1188 static void svg_top_ten_cpu(void) {
1189 struct ps_struct *top[10];
1190 struct ps_struct emptyps = {};
1191 struct ps_struct *ps;
1194 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1197 /* walk all ps's and setup ptrs */
1199 while ((ps = get_next_ps(ps))) {
1200 for (n = 0; n < 10; n++) {
1201 if (ps->total <= top[n]->total)
1203 /* cascade insert */
1204 for (m = 9; m > n; m--)
1211 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1212 for (n = 0; n < 10; n++)
1213 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1220 static void svg_top_ten_pss(void) {
1221 struct ps_struct *top[10];
1222 struct ps_struct emptyps = {};
1223 struct ps_struct *ps;
1226 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1229 /* walk all ps's and setup ptrs */
1231 while ((ps = get_next_ps(ps))) {
1232 for (n = 0; n < 10; n++) {
1233 if (ps->pss_max <= top[n]->pss_max)
1235 /* cascade insert */
1236 for (m = 9; m > n; m--)
1243 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1244 for (n = 0; n < 10; n++)
1245 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1252 void svg_do(const char *build) {
1253 struct ps_struct *ps;
1255 memzero(&str, sizeof(str));
1259 /* count initcall thread count first */
1261 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1263 /* then count processes */
1264 while ((ps = get_next_ps(ps))) {
1270 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1272 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1274 /* after this, we can draw the header with proper sizing */
1276 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1278 svg("<g transform=\"translate(10,400)\">\n");
1282 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1286 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1290 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1295 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1300 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1304 svg("<g transform=\"translate(10, 0)\">\n");
1308 svg("<g transform=\"translate(10,200)\">\n");
1313 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1319 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1323 svg("<g transform=\"translate(410,200)\">\n");