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 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
203 fd = openat(procfd, "cpuinfo", O_RDONLY);
206 while (fgets(buf, 255, f)) {
207 if (strstr(buf, "model name")) {
208 strncpy(cpu, &buf[13], 255);
215 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
217 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
218 uts.sysname, uts.release, uts.version, uts.machine);
219 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
221 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
223 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
225 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
227 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
228 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
231 svg("%.03fs", idletime);
235 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",
236 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
239 static void svg_graph_box(int height) {
242 double finalsample = 0.0;
243 struct list_sample_data *sampledata_last;
245 sampledata_last = head;
246 LIST_FOREACH_BEFORE(link, sampledata, head) {
247 sampledata_last = sampledata;
250 finalsample = sampledata_last->sampletime;
252 /* outside box, fill */
253 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
255 time_to_graph(finalsample - graph_start),
256 ps_to_graph(height));
258 for (d = graph_start; d <= finalsample;
259 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
260 /* lines for each second */
262 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
263 time_to_graph(d - graph_start),
264 time_to_graph(d - graph_start),
265 ps_to_graph(height));
266 else if (i % 10 == 0)
267 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
268 time_to_graph(d - graph_start),
269 time_to_graph(d - graph_start),
270 ps_to_graph(height));
272 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
273 time_to_graph(d - graph_start),
274 time_to_graph(d - graph_start),
275 ps_to_graph(height));
279 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
280 time_to_graph(d - graph_start),
288 /* xml comments must not contain "--" */
289 static char* xml_comment_encode(const char* name) {
292 enc_name = strdup(name);
296 for (p = enc_name; *p; p++)
297 if (p[0] == '-' && p[1] == '-')
303 static void svg_pss_graph(void) {
304 struct ps_struct *ps;
306 struct list_sample_data *sampledata_last;
308 sampledata_last = head;
309 LIST_FOREACH_BEFORE(link, sampledata, head) {
310 sampledata_last = sampledata;
314 svg("\n\n<!-- Pss memory size graph -->\n");
316 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
318 /* vsize 1000 == 1000mb */
320 /* draw some hlines for usable memory sizes */
321 for (i = 100000; i < 1000000; i += 100000) {
322 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
325 time_to_graph(sampledata_last->sampletime - graph_start),
327 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
328 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
329 kb_to_graph(i), (1000000 - i) / 1000);
333 /* now plot the graph itself */
335 prev_sampledata = head;
336 LIST_FOREACH_BEFORE(link, sampledata, head) {
339 struct ps_sched_struct *cross_place;
344 /* put all the small pss blocks into the bottom */
346 while (ps->next_ps) {
350 ps->sample = ps->first;
351 while (ps->sample->next) {
352 ps->sample = ps->sample->next;
353 if (ps->sample->sampledata == sampledata)
356 if (ps->sample->sampledata == sampledata) {
357 if (ps->sample->pss <= (100 * arg_scale_y))
358 top += ps->sample->pss;
362 while (ps->sample->cross) {
363 cross_place = ps->sample->cross;
364 ps = ps->sample->cross->ps_new;
365 ps->sample = cross_place;
366 if (ps->sample->pss <= (100 * arg_scale_y))
367 top += ps->sample->pss;
370 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
372 time_to_graph(prev_sampledata->sampletime - graph_start),
373 kb_to_graph(1000000.0 - top),
374 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
375 kb_to_graph(top - bottom));
378 /* now plot the ones that are of significant size */
380 while (ps->next_ps) {
384 ps->sample = ps->first;
385 while (ps->sample->next) {
386 ps->sample = ps->sample->next;
387 if (ps->sample->sampledata == sampledata)
390 /* don't draw anything smaller than 2mb */
391 if (ps->sample->sampledata == sampledata) {
392 if (ps->sample->pss > (100 * arg_scale_y)) {
393 top = bottom + ps->sample->pss;
394 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
395 colorwheel[ps->pid % 12],
396 time_to_graph(prev_sampledata->sampletime - graph_start),
397 kb_to_graph(1000000.0 - top),
398 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
399 kb_to_graph(top - bottom));
405 while ((cross_place = ps->sample->cross)) {
406 ps = ps->sample->cross->ps_new;
407 ps->sample = cross_place;
408 if (ps->sample->pss > (100 * arg_scale_y)) {
409 top = bottom + ps->sample->pss;
410 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
411 colorwheel[ps->pid % 12],
412 time_to_graph(prev_sampledata->sampletime - graph_start),
413 kb_to_graph(1000000.0 - top),
414 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
415 kb_to_graph(top - bottom));
419 prev_sampledata = sampledata;
423 /* overlay all the text labels */
425 LIST_FOREACH_BEFORE(link, sampledata, head) {
428 struct ps_sched_struct *prev_sample;
429 struct ps_sched_struct *cross_place;
431 /* put all the small pss blocks into the bottom */
432 ps = ps_first->next_ps;
433 while (ps->next_ps) {
437 ps->sample = ps->first;
438 while (ps->sample->next) {
439 ps->sample = ps->sample->next;
440 if (ps->sample->sampledata == sampledata)
443 if (ps->sample->sampledata == sampledata) {
444 if (ps->sample->pss <= (100 * arg_scale_y))
445 top += ps->sample->pss;
449 while ((cross_place = ps->sample->cross)) {
450 ps = ps->sample->cross->ps_new;
451 ps->sample = cross_place;
452 if (ps->sample->pss <= (100 * arg_scale_y))
453 top += ps->sample->pss;
457 /* now plot the ones that are of significant size */
459 while (ps->next_ps) {
460 prev_sample = ps->sample;
464 ps->sample = ps->first;
465 while (ps->sample->next) {
466 prev_sample = ps->sample;
467 ps->sample = ps->sample->next;
468 if (ps->sample->sampledata == sampledata)
471 /* don't draw anything smaller than 2mb */
472 if (ps->sample->sampledata == sampledata) {
473 if (ps->sample->pss > (100 * arg_scale_y)) {
474 top = bottom + ps->sample->pss;
475 /* draw a label with the process / PID */
476 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
477 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
478 time_to_graph(sampledata->sampletime - graph_start),
479 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
487 while ((cross_place = ps->sample->cross)) {
488 ps = ps->sample->cross->ps_new;
489 ps->sample = cross_place;
490 prev_sample = ps->sample->prev;
491 if (ps->sample->pss > (100 * arg_scale_y)) {
492 top = bottom + ps->sample->pss;
493 /* draw a label with the process / PID */
494 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
495 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
496 time_to_graph(sampledata->sampletime - graph_start),
497 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
506 /* debug output - full data dump */
507 svg("\n\n<!-- PSS map - csv format -->\n");
509 while (ps->next_ps) {
510 _cleanup_free_ char *enc_name = NULL;
515 enc_name = xml_comment_encode(ps->name);
519 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
521 ps->sample = ps->first;
522 while (ps->sample->next) {
523 ps->sample = ps->sample->next;
524 svg("%d," , ps->sample->pss);
531 static void svg_io_bi_bar(void) {
537 struct list_sample_data *start_sampledata;
538 struct list_sample_data *stop_sampledata;
540 svg("<!-- IO utilization graph - In -->\n");
542 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
545 * calculate rounding range
547 * We need to round IO data since IO block data is not updated on
548 * each poll. Applying a smoothing function loses some burst data,
549 * so keep the smoothing range short.
551 range = 0.25 / (1.0 / arg_hz);
553 range = 2.0; /* no smoothing */
555 /* surrounding box */
558 /* find the max IO first */
560 LIST_FOREACH_BEFORE(link, sampledata, head) {
566 start = MAX(i - ((range / 2) - 1), 0);
567 stop = MIN(i + (range / 2), samples - 1);
568 diff = (stop - start);
570 start_sampledata = sampledata;
571 stop_sampledata = sampledata;
573 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
574 start_sampledata = start_sampledata->link_next;
575 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
576 stop_sampledata = stop_sampledata->link_prev;
578 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
586 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
597 prev_sampledata = head;
598 LIST_FOREACH_BEFORE(link, sampledata, head) {
605 start = MAX(i - ((range / 2) - 1), 0);
606 stop = MIN(i + (range / 2), samples);
607 diff = (stop - start);
609 start_sampledata = sampledata;
610 stop_sampledata = sampledata;
612 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
613 start_sampledata = start_sampledata->link_next;
614 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
615 stop_sampledata = stop_sampledata->link_prev;
617 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
624 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
625 time_to_graph(prev_sampledata->sampletime - graph_start),
626 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
627 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
628 pbi * (arg_scale_y * 5));
630 /* labels around highest value */
632 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
633 time_to_graph(sampledata->sampletime - graph_start) + 5,
634 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
635 max / 1024.0 / (interval / 1000000000.0));
638 prev_sampledata = sampledata;
642 static void svg_io_bo_bar(void) {
648 struct list_sample_data *start_sampledata;
649 struct list_sample_data *stop_sampledata;
651 svg("<!-- IO utilization graph - out -->\n");
653 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
656 * calculate rounding range
658 * We need to round IO data since IO block data is not updated on
659 * each poll. Applying a smoothing function loses some burst data,
660 * so keep the smoothing range short.
662 range = 0.25 / (1.0 / arg_hz);
664 range = 2.0; /* no smoothing */
666 /* surrounding box */
669 /* find the max IO first */
671 LIST_FOREACH_BEFORE(link, sampledata, head) {
677 start = MAX(i - ((range / 2) - 1), 0);
678 stop = MIN(i + (range / 2), samples - 1);
679 diff = (stop - start);
681 start_sampledata = sampledata;
682 stop_sampledata = sampledata;
684 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
685 start_sampledata = start_sampledata->link_next;
686 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
687 stop_sampledata = stop_sampledata->link_prev;
689 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
693 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
703 prev_sampledata = head;
705 LIST_FOREACH_BEFORE(link, sampledata, head) {
714 start = MAX(i - ((range / 2) - 1), 0);
715 stop = MIN(i + (range / 2), samples);
716 diff = (stop - start);
718 start_sampledata = sampledata;
719 stop_sampledata = sampledata;
721 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
722 start_sampledata = start_sampledata->link_next;
723 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
724 stop_sampledata = stop_sampledata->link_prev;
726 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
733 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
734 time_to_graph(prev_sampledata->sampletime - graph_start),
735 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
736 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
737 pbo * (arg_scale_y * 5));
739 /* labels around highest bo value */
741 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
742 time_to_graph(sampledata->sampletime - graph_start) + 5,
743 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
744 max / 1024.0 / (interval / 1000000000.0));
747 prev_sampledata = sampledata;
751 static void svg_cpu_bar(void) {
753 svg("<!-- CPU utilization graph -->\n");
755 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
756 /* surrounding box */
759 /* bars for each sample, proportional to the CPU util. */
760 prev_sampledata = head;
761 LIST_FOREACH_BEFORE(link, sampledata, head) {
768 for (c = 0; c < cpus; c++)
769 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
771 trt = trt / 1000000000.0;
773 trt = trt / (double)cpus;
776 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
782 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
783 time_to_graph(prev_sampledata->sampletime - graph_start),
784 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
785 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
786 ptrt * (arg_scale_y * 5));
788 prev_sampledata = sampledata;
792 static void svg_wait_bar(void) {
794 svg("<!-- Wait time aggregation box -->\n");
796 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
798 /* surrounding box */
801 /* bars for each sample, proportional to the CPU util. */
802 prev_sampledata = head;
803 LIST_FOREACH_BEFORE(link, sampledata, head) {
810 for (c = 0; c < cpus; c++)
811 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
813 twt = twt / 1000000000.0;
815 twt = twt / (double)cpus;
818 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
824 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
825 time_to_graph(prev_sampledata->sampletime - graph_start),
826 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
827 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
828 ptwt * (arg_scale_y * 5));
830 prev_sampledata = sampledata;
835 static void svg_entropy_bar(void) {
837 svg("<!-- entropy pool graph -->\n");
839 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
840 /* surrounding box */
843 /* bars for each sample, scale 0-4096 */
844 prev_sampledata = head;
845 LIST_FOREACH_BEFORE(link, sampledata, head) {
846 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
847 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
848 time_to_graph(prev_sampledata->sampletime - graph_start),
849 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
850 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
851 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
852 prev_sampledata = sampledata;
856 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
858 * walk the list of processes and return the next one to be
872 /* go back for parent siblings */
875 if (ps->parent->next)
876 return ps->parent->next;
885 static bool ps_filter(struct ps_struct *ps) {
889 /* can't draw data when there is only 1 sample (need start + stop) */
890 if (ps->first == ps->last)
893 /* don't filter kthreadd */
897 /* drop stuff that doesn't use any real CPU time */
898 if (ps->total <= 0.001)
904 static void svg_do_initcall(int count_only) {
905 _cleanup_pclose_ FILE *f = NULL;
911 /* can't plot initcall when disabled or in relative mode */
912 if (!initcall || arg_relative) {
918 svg("<!-- initcall -->\n");
920 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
921 /* surrounding box */
922 svg_graph_box(kcount);
928 * Initcall graphing - parses dmesg buffer and displays kernel threads
929 * This somewhat uses the same methods and scaling to show processes
930 * but looks a lot simpler. It's overlaid entirely onto the PS graph
934 f = popen("dmesg", "r");
943 if (fgets(l, sizeof(l) - 1, f) == NULL)
946 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
947 &t, func, &ret, &usecs);
949 /* also parse initcalls done by module loading */
950 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
951 &t, func, &ret, &usecs);
956 /* chop the +0xXX/0xXX stuff */
957 while(func[z] != '+')
962 /* filter out irrelevant stuff */
968 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
969 func, t, usecs, ret);
975 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
976 time_to_graph(t - (usecs / 1000000.0)),
978 time_to_graph(usecs / 1000000.0),
982 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
983 time_to_graph(t - (usecs / 1000000.0)) + 5,
984 ps_to_graph(kcount) + 15,
992 static void svg_ps_bars(void) {
993 struct ps_struct *ps;
999 svg("<!-- Process graph -->\n");
1001 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1003 /* surrounding box */
1004 svg_graph_box(pcount);
1006 /* pass 2 - ps boxes */
1008 while ((ps = get_next_ps(ps))) {
1009 _cleanup_free_ char *enc_name = NULL;
1014 enc_name = xml_comment_encode(ps->name);
1018 /* leave some trace of what we actually filtered etc. */
1019 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1020 ps->ppid, ps->total);
1022 starttime = ps->first->sampledata->sampletime;
1024 if (!ps_filter(ps)) {
1025 /* remember where _to_ our children need to draw a line */
1026 ps->pos_x = time_to_graph(starttime - graph_start);
1027 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1028 } else if (ps->parent){
1029 /* hook children to our parent coords instead */
1030 ps->pos_x = ps->parent->pos_x;
1031 ps->pos_y = ps->parent->pos_y;
1033 /* if this is the last child, we might still need to draw a connecting line */
1034 if ((!ps->next) && (ps->parent))
1035 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1037 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1043 endtime = ps->last->sampledata->sampletime;
1044 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1045 time_to_graph(starttime - graph_start),
1047 time_to_graph(ps->last->sampledata->sampletime - starttime),
1050 /* paint cpu load over these */
1051 ps->sample = ps->first;
1053 while (ps->sample->next) {
1056 struct ps_sched_struct *prev;
1059 ps->sample = ps->sample->next;
1061 /* calculate over interval */
1062 rt = ps->sample->runtime - prev->runtime;
1063 wt = ps->sample->waittime - prev->waittime;
1065 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1066 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1068 /* this can happen if timekeeping isn't accurate enough */
1074 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1077 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1078 time_to_graph(prev->sampledata->sampletime - graph_start),
1080 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1083 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1084 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1085 time_to_graph(prev->sampledata->sampletime - graph_start),
1086 ps_to_graph(j + (1.0 - prt)),
1087 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1092 /* determine where to display the process name */
1093 if ((endtime - starttime) < 1.5)
1094 /* too small to fit label inside the box */
1099 /* text label of process name */
1100 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1101 time_to_graph(w - graph_start) + 5.0,
1102 ps_to_graph(j) + 14.0,
1105 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1106 arg_show_cgroup ? ps->cgroup : "");
1107 /* paint lines to the parent process */
1109 /* horizontal part */
1110 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1111 time_to_graph(starttime - graph_start),
1112 ps_to_graph(j) + 10.0,
1114 ps_to_graph(j) + 10.0);
1116 /* one vertical line connecting all the horizontal ones up */
1118 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1120 ps_to_graph(j) + 10.0,
1125 j++; /* count boxes */
1130 /* last pass - determine when idle */
1132 /* make sure we start counting from the point where we actually have
1133 * data: assume that bootchart's first sample is when data started
1137 while (ps->next_ps) {
1143 /* need to know last node first */
1144 ps->sample = ps->first;
1145 i = ps->sample->next->sampledata->counter;
1147 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1152 struct ps_sched_struct *sample_hz;
1154 ps->sample = ps->sample->next;
1155 sample_hz = ps->sample;
1156 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1157 sample_hz = sample_hz->next;
1159 /* subtract bootchart cpu utilization from total */
1161 for (c = 0; c < cpus; c++)
1162 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1163 brt = sample_hz->runtime - ps->sample->runtime;
1165 * our definition of "idle":
1167 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1168 * defaults to 4.0%, which experimentally, is where atom idles
1170 if ((crt - brt) < (interval / 2.0)) {
1171 idletime = ps->sample->sampledata->sampletime - graph_start;
1172 svg("\n<!-- idle detected at %.03f seconds -->\n",
1174 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1175 time_to_graph(idletime),
1177 time_to_graph(idletime),
1178 ps_to_graph(pcount) + arg_scale_y);
1179 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1180 time_to_graph(idletime) + 5.0,
1181 ps_to_graph(pcount) + arg_scale_y,
1189 static void svg_top_ten_cpu(void) {
1190 struct ps_struct *top[10];
1191 struct ps_struct emptyps = {};
1192 struct ps_struct *ps;
1195 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1198 /* walk all ps's and setup ptrs */
1200 while ((ps = get_next_ps(ps))) {
1201 for (n = 0; n < 10; n++) {
1202 if (ps->total <= top[n]->total)
1204 /* cascade insert */
1205 for (m = 9; m > n; m--)
1212 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1213 for (n = 0; n < 10; n++)
1214 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1221 static void svg_top_ten_pss(void) {
1222 struct ps_struct *top[10];
1223 struct ps_struct emptyps = {};
1224 struct ps_struct *ps;
1227 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1230 /* walk all ps's and setup ptrs */
1232 while ((ps = get_next_ps(ps))) {
1233 for (n = 0; n < 10; n++) {
1234 if (ps->pss_max <= top[n]->pss_max)
1236 /* cascade insert */
1237 for (m = 9; m > n; m--)
1244 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1245 for (n = 0; n < 10; n++)
1246 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1253 void svg_do(const char *build) {
1254 struct ps_struct *ps;
1256 memzero(&str, sizeof(str));
1260 /* count initcall thread count first */
1262 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1264 /* then count processes */
1265 while ((ps = get_next_ps(ps))) {
1271 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1273 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1275 /* after this, we can draw the header with proper sizing */
1277 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1279 svg("<g transform=\"translate(10,400)\">\n");
1283 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1287 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1291 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1296 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1301 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1305 svg("<g transform=\"translate(10, 0)\">\n");
1309 svg("<g transform=\"translate(10,200)\">\n");
1314 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1320 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1324 svg("<g transform=\"translate(410,200)\">\n");