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"
44 #define time_to_graph(t) ((t) * arg_scale_x)
45 #define ps_to_graph(n) ((n) * arg_scale_y)
46 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
47 #define to_color(n) (192.0 - ((n) * 192.0))
49 static char str[8092];
51 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
53 static const char * const colorwheel[12] = {
54 "rgb(255,32,32)", // red
55 "rgb(32,192,192)", // cyan
56 "rgb(255,128,32)", // orange
57 "rgb(128,32,192)", // blue-violet
58 "rgb(255,255,32)", // yellow
59 "rgb(192,32,128)", // red-violet
60 "rgb(32,255,32)", // green
61 "rgb(255,64,32)", // red-orange
62 "rgb(32,32,255)", // blue
63 "rgb(255,192,32)", // yellow-orange
64 "rgb(192,32,192)", // violet
65 "rgb(32,192,32)" // yellow-green
68 static double idletime = -1.0;
69 static int pfiltered = 0;
70 static int pcount = 0;
71 static int kcount = 0;
72 static double psize = 0;
73 static double ksize = 0;
74 static double esize = 0;
75 static struct list_sample_data *sampledata;
76 static struct list_sample_data *prev_sampledata;
77 extern struct list_sample_data *head;
79 static void svg_header(void) {
82 struct list_sample_data *sampledata_last;
87 LIST_FIND_TAIL(link, sampledata, head);
88 sampledata_last = head;
89 LIST_FOREACH_BEFORE(link, sampledata, head) {
90 sampledata_last = sampledata;
93 /* min width is about 1600px due to the label */
94 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
95 w = ((w < 1600.0) ? 1600.0 : w);
97 /* height is variable based on pss, psize, ksize */
98 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
99 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
100 + psize + ksize + esize;
102 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
103 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
104 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
106 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
107 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
109 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
111 /* write some basic info as a comment, including some help */
112 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
113 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
114 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
115 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
116 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
118 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
119 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
120 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
121 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
122 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
123 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
126 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
128 svg(" rect { stroke-width: 1; }\n");
129 svg(" rect.bg { fill: rgb(255,255,255); }\n");
130 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
132 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
133 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
134 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
135 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
136 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
137 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
138 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
139 svg("// line.sec1 { }\n");
140 svg(" line.sec5 { stroke-width: 2; }\n");
141 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
142 svg(" line.dot { stroke-dasharray: 2 4; }\n");
143 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
145 svg(" .run { font-size: 8; font-style: italic; }\n");
146 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
147 svg(" text.sec { font-size: 8; }\n");
148 svg(" text.t1 { font-size: 24; }\n");
149 svg(" text.t2 { font-size: 12; }\n");
150 svg(" text.idle { font-size: 18; }\n");
152 svg(" ]]>\n </style>\n</defs>\n\n");
155 static void svg_title(const char *build) {
156 char cmdline[256] = "";
157 char filename[PATH_MAX];
159 char rootbdev[16] = "Unknown";
160 char model[256] = "Unknown";
161 char date[256] = "Unknown";
162 char cpu[256] = "Unknown";
169 /* grab /proc/cmdline */
170 fd = openat(procfd, "cmdline", O_RDONLY);
173 if (!fgets(cmdline, 255, f))
174 sprintf(cmdline, "Unknown");
178 /* extract root fs so we can find disk model name in sysfs */
179 /* FIXME: this works only in the simple case */
180 c = strstr(cmdline, "root=/dev/");
182 strncpy(rootbdev, &c[10], 3);
184 sprintf(filename, "block/%s/device/model", rootbdev);
185 fd = openat(sysfd, filename, O_RDONLY);
188 if (!fgets(model, 255, f))
189 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
194 /* various utsname parameters */
196 fprintf(stderr, "Error getting uname info\n");
200 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
204 fd = openat(procfd, "cpuinfo", O_RDONLY);
207 while (fgets(buf, 255, f)) {
208 if (strstr(buf, "model name")) {
209 strncpy(cpu, &buf[13], 255);
216 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
218 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
219 uts.sysname, uts.release, uts.version, uts.machine);
220 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
222 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
226 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
228 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
229 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
232 svg("%.03fs", idletime);
236 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",
237 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
240 static void svg_graph_box(int height) {
243 double finalsample = 0.0;
244 struct list_sample_data *sampledata_last;
246 sampledata_last = head;
247 LIST_FOREACH_BEFORE(link, sampledata, head) {
248 sampledata_last = sampledata;
251 finalsample = sampledata_last->sampletime;
253 /* outside box, fill */
254 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
256 time_to_graph(finalsample - graph_start),
257 ps_to_graph(height));
259 for (d = graph_start; d <= finalsample;
260 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
261 /* lines for each second */
263 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
264 time_to_graph(d - graph_start),
265 time_to_graph(d - graph_start),
266 ps_to_graph(height));
267 else if (i % 10 == 0)
268 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
269 time_to_graph(d - graph_start),
270 time_to_graph(d - graph_start),
271 ps_to_graph(height));
273 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
274 time_to_graph(d - graph_start),
275 time_to_graph(d - graph_start),
276 ps_to_graph(height));
280 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
281 time_to_graph(d - graph_start),
289 /* xml comments must not contain "--" */
290 static char* xml_comment_encode(const char* name) {
293 enc_name = strdup(name);
297 for (p = enc_name; *p; p++)
298 if (p[0] == '-' && p[1] == '-')
304 static void svg_pss_graph(void) {
305 struct ps_struct *ps;
307 struct list_sample_data *sampledata_last;
309 sampledata_last = head;
310 LIST_FOREACH_BEFORE(link, sampledata, head) {
311 sampledata_last = sampledata;
315 svg("\n\n<!-- Pss memory size graph -->\n");
317 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
319 /* vsize 1000 == 1000mb */
321 /* draw some hlines for usable memory sizes */
322 for (i = 100000; i < 1000000; i += 100000) {
323 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
326 time_to_graph(sampledata_last->sampletime - graph_start),
328 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
329 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
330 kb_to_graph(i), (1000000 - i) / 1000);
334 /* now plot the graph itself */
336 prev_sampledata = head;
337 LIST_FOREACH_BEFORE(link, sampledata, head) {
340 struct ps_sched_struct *cross_place;
345 /* put all the small pss blocks into the bottom */
347 while (ps->next_ps) {
351 ps->sample = ps->first;
352 while (ps->sample->next) {
353 ps->sample = ps->sample->next;
354 if (ps->sample->sampledata == sampledata)
357 if (ps->sample->sampledata == sampledata) {
358 if (ps->sample->pss <= (100 * arg_scale_y))
359 top += ps->sample->pss;
363 while (ps->sample->cross) {
364 cross_place = ps->sample->cross;
365 ps = ps->sample->cross->ps_new;
366 ps->sample = cross_place;
367 if (ps->sample->pss <= (100 * arg_scale_y))
368 top += ps->sample->pss;
371 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
373 time_to_graph(prev_sampledata->sampletime - graph_start),
374 kb_to_graph(1000000.0 - top),
375 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
376 kb_to_graph(top - bottom));
379 /* now plot the ones that are of significant size */
381 while (ps->next_ps) {
385 ps->sample = ps->first;
386 while (ps->sample->next) {
387 ps->sample = ps->sample->next;
388 if (ps->sample->sampledata == sampledata)
391 /* don't draw anything smaller than 2mb */
392 if (ps->sample->sampledata == sampledata) {
393 if (ps->sample->pss > (100 * arg_scale_y)) {
394 top = bottom + ps->sample->pss;
395 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
396 colorwheel[ps->pid % 12],
397 time_to_graph(prev_sampledata->sampletime - graph_start),
398 kb_to_graph(1000000.0 - top),
399 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
400 kb_to_graph(top - bottom));
406 while ((cross_place = ps->sample->cross)) {
407 ps = ps->sample->cross->ps_new;
408 ps->sample = cross_place;
409 if (ps->sample->pss > (100 * arg_scale_y)) {
410 top = bottom + ps->sample->pss;
411 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
412 colorwheel[ps->pid % 12],
413 time_to_graph(prev_sampledata->sampletime - graph_start),
414 kb_to_graph(1000000.0 - top),
415 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
416 kb_to_graph(top - bottom));
420 prev_sampledata = sampledata;
424 /* overlay all the text labels */
426 LIST_FOREACH_BEFORE(link, sampledata, head) {
429 struct ps_sched_struct *prev_sample;
430 struct ps_sched_struct *cross_place;
432 /* put all the small pss blocks into the bottom */
433 ps = ps_first->next_ps;
434 while (ps->next_ps) {
438 ps->sample = ps->first;
439 while (ps->sample->next) {
440 ps->sample = ps->sample->next;
441 if (ps->sample->sampledata == sampledata)
444 if (ps->sample->sampledata == sampledata) {
445 if (ps->sample->pss <= (100 * arg_scale_y))
446 top += ps->sample->pss;
450 while ((cross_place = ps->sample->cross)) {
451 ps = ps->sample->cross->ps_new;
452 ps->sample = cross_place;
453 if (ps->sample->pss <= (100 * arg_scale_y))
454 top += ps->sample->pss;
458 /* now plot the ones that are of significant size */
460 while (ps->next_ps) {
461 prev_sample = ps->sample;
465 ps->sample = ps->first;
466 while (ps->sample->next) {
467 prev_sample = ps->sample;
468 ps->sample = ps->sample->next;
469 if (ps->sample->sampledata == sampledata)
472 /* don't draw anything smaller than 2mb */
473 if (ps->sample->sampledata == sampledata) {
474 if (ps->sample->pss > (100 * arg_scale_y)) {
475 top = bottom + ps->sample->pss;
476 /* draw a label with the process / PID */
477 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
478 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
479 time_to_graph(sampledata->sampletime - graph_start),
480 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
488 while ((cross_place = ps->sample->cross)) {
489 ps = ps->sample->cross->ps_new;
490 ps->sample = cross_place;
491 prev_sample = ps->sample->prev;
492 if (ps->sample->pss > (100 * arg_scale_y)) {
493 top = bottom + ps->sample->pss;
494 /* draw a label with the process / PID */
495 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
496 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
497 time_to_graph(sampledata->sampletime - graph_start),
498 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
507 /* debug output - full data dump */
508 svg("\n\n<!-- PSS map - csv format -->\n");
510 while (ps->next_ps) {
511 _cleanup_free_ char *enc_name = NULL;
516 enc_name = xml_comment_encode(ps->name);
520 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
522 ps->sample = ps->first;
523 while (ps->sample->next) {
524 ps->sample = ps->sample->next;
525 svg("%d," , ps->sample->pss);
532 static void svg_io_bi_bar(void) {
538 struct list_sample_data *start_sampledata;
539 struct list_sample_data *stop_sampledata;
541 svg("<!-- IO utilization graph - In -->\n");
543 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
546 * calculate rounding range
548 * We need to round IO data since IO block data is not updated on
549 * each poll. Applying a smoothing function loses some burst data,
550 * so keep the smoothing range short.
552 range = 0.25 / (1.0 / arg_hz);
554 range = 2.0; /* no smoothing */
556 /* surrounding box */
559 /* find the max IO first */
561 LIST_FOREACH_BEFORE(link, sampledata, head) {
567 start = MAX(i - ((range / 2) - 1), 0);
568 stop = MIN(i + (range / 2), samples - 1);
569 diff = (stop - start);
571 start_sampledata = sampledata;
572 stop_sampledata = sampledata;
574 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
575 start_sampledata = start_sampledata->link_next;
576 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
577 stop_sampledata = stop_sampledata->link_prev;
579 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
587 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
598 prev_sampledata = head;
599 LIST_FOREACH_BEFORE(link, sampledata, head) {
606 start = MAX(i - ((range / 2) - 1), 0);
607 stop = MIN(i + (range / 2), samples);
608 diff = (stop - start);
610 start_sampledata = sampledata;
611 stop_sampledata = sampledata;
613 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
614 start_sampledata = start_sampledata->link_next;
615 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
616 stop_sampledata = stop_sampledata->link_prev;
618 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
625 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
626 time_to_graph(prev_sampledata->sampletime - graph_start),
627 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
628 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
629 pbi * (arg_scale_y * 5));
631 /* labels around highest value */
633 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
634 time_to_graph(sampledata->sampletime - graph_start) + 5,
635 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
636 max / 1024.0 / (interval / 1000000000.0));
639 prev_sampledata = sampledata;
643 static void svg_io_bo_bar(void) {
649 struct list_sample_data *start_sampledata;
650 struct list_sample_data *stop_sampledata;
652 svg("<!-- IO utilization graph - out -->\n");
654 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
657 * calculate rounding range
659 * We need to round IO data since IO block data is not updated on
660 * each poll. Applying a smoothing function loses some burst data,
661 * so keep the smoothing range short.
663 range = 0.25 / (1.0 / arg_hz);
665 range = 2.0; /* no smoothing */
667 /* surrounding box */
670 /* find the max IO first */
672 LIST_FOREACH_BEFORE(link, sampledata, head) {
678 start = MAX(i - ((range / 2) - 1), 0);
679 stop = MIN(i + (range / 2), samples - 1);
680 diff = (stop - start);
682 start_sampledata = sampledata;
683 stop_sampledata = sampledata;
685 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
686 start_sampledata = start_sampledata->link_next;
687 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
688 stop_sampledata = stop_sampledata->link_prev;
690 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
694 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
704 prev_sampledata = head;
706 LIST_FOREACH_BEFORE(link, sampledata, head) {
715 start = MAX(i - ((range / 2) - 1), 0);
716 stop = MIN(i + (range / 2), samples);
717 diff = (stop - start);
719 start_sampledata = sampledata;
720 stop_sampledata = sampledata;
722 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
723 start_sampledata = start_sampledata->link_next;
724 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
725 stop_sampledata = stop_sampledata->link_prev;
727 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
734 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
735 time_to_graph(prev_sampledata->sampletime - graph_start),
736 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
737 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
738 pbo * (arg_scale_y * 5));
740 /* labels around highest bo value */
742 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
743 time_to_graph(sampledata->sampletime - graph_start) + 5,
744 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
745 max / 1024.0 / (interval / 1000000000.0));
748 prev_sampledata = sampledata;
752 static void svg_cpu_bar(void) {
754 svg("<!-- CPU utilization graph -->\n");
756 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
757 /* surrounding box */
760 /* bars for each sample, proportional to the CPU util. */
761 prev_sampledata = head;
762 LIST_FOREACH_BEFORE(link, sampledata, head) {
769 for (c = 0; c < cpus; c++)
770 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
772 trt = trt / 1000000000.0;
774 trt = trt / (double)cpus;
777 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
783 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
784 time_to_graph(prev_sampledata->sampletime - graph_start),
785 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
786 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
787 ptrt * (arg_scale_y * 5));
789 prev_sampledata = sampledata;
793 static void svg_wait_bar(void) {
795 svg("<!-- Wait time aggregation box -->\n");
797 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
799 /* surrounding box */
802 /* bars for each sample, proportional to the CPU util. */
803 prev_sampledata = head;
804 LIST_FOREACH_BEFORE(link, sampledata, head) {
811 for (c = 0; c < cpus; c++)
812 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
814 twt = twt / 1000000000.0;
816 twt = twt / (double)cpus;
819 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
825 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
826 time_to_graph(prev_sampledata->sampletime - graph_start),
827 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
828 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
829 ptwt * (arg_scale_y * 5));
831 prev_sampledata = sampledata;
836 static void svg_entropy_bar(void) {
838 svg("<!-- entropy pool graph -->\n");
840 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
841 /* surrounding box */
844 /* bars for each sample, scale 0-4096 */
845 prev_sampledata = head;
846 LIST_FOREACH_BEFORE(link, sampledata, head) {
847 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
848 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
849 time_to_graph(prev_sampledata->sampletime - graph_start),
850 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
851 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
852 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
853 prev_sampledata = sampledata;
857 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
859 * walk the list of processes and return the next one to be
873 /* go back for parent siblings */
876 if (ps->parent->next)
877 return ps->parent->next;
886 static bool ps_filter(struct ps_struct *ps) {
890 /* can't draw data when there is only 1 sample (need start + stop) */
891 if (ps->first == ps->last)
894 /* don't filter kthreadd */
898 /* drop stuff that doesn't use any real CPU time */
899 if (ps->total <= 0.001)
905 static void svg_do_initcall(int count_only) {
906 _cleanup_pclose_ FILE *f = NULL;
912 /* can't plot initcall when disabled or in relative mode */
913 if (!initcall || arg_relative) {
919 svg("<!-- initcall -->\n");
921 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
922 /* surrounding box */
923 svg_graph_box(kcount);
929 * Initcall graphing - parses dmesg buffer and displays kernel threads
930 * This somewhat uses the same methods and scaling to show processes
931 * but looks a lot simpler. It's overlaid entirely onto the PS graph
935 f = popen("dmesg", "r");
944 if (fgets(l, sizeof(l) - 1, f) == NULL)
947 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
948 &t, func, &ret, &usecs);
950 /* also parse initcalls done by module loading */
951 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
952 &t, func, &ret, &usecs);
957 /* chop the +0xXX/0xXX stuff */
958 while(func[z] != '+')
963 /* filter out irrelevant stuff */
969 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
970 func, t, usecs, ret);
976 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
977 time_to_graph(t - (usecs / 1000000.0)),
979 time_to_graph(usecs / 1000000.0),
983 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
984 time_to_graph(t - (usecs / 1000000.0)) + 5,
985 ps_to_graph(kcount) + 15,
993 static void svg_ps_bars(void) {
994 struct ps_struct *ps;
1000 svg("<!-- Process graph -->\n");
1002 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1004 /* surrounding box */
1005 svg_graph_box(pcount);
1007 /* pass 2 - ps boxes */
1009 while ((ps = get_next_ps(ps))) {
1010 _cleanup_free_ char *enc_name = NULL, *escaped = NULL;
1015 if (!utf8_is_printable(ps->name, strlen(ps->name)))
1016 escaped = utf8_escape_non_printable(ps->name);
1018 enc_name = xml_comment_encode(escaped ? escaped : ps->name);
1022 /* leave some trace of what we actually filtered etc. */
1023 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1024 ps->ppid, ps->total);
1026 starttime = ps->first->sampledata->sampletime;
1028 if (!ps_filter(ps)) {
1029 /* remember where _to_ our children need to draw a line */
1030 ps->pos_x = time_to_graph(starttime - graph_start);
1031 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1032 } else if (ps->parent){
1033 /* hook children to our parent coords instead */
1034 ps->pos_x = ps->parent->pos_x;
1035 ps->pos_y = ps->parent->pos_y;
1037 /* if this is the last child, we might still need to draw a connecting line */
1038 if ((!ps->next) && (ps->parent))
1039 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1041 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1047 endtime = ps->last->sampledata->sampletime;
1048 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1049 time_to_graph(starttime - graph_start),
1051 time_to_graph(ps->last->sampledata->sampletime - starttime),
1054 /* paint cpu load over these */
1055 ps->sample = ps->first;
1057 while (ps->sample->next) {
1060 struct ps_sched_struct *prev;
1063 ps->sample = ps->sample->next;
1065 /* calculate over interval */
1066 rt = ps->sample->runtime - prev->runtime;
1067 wt = ps->sample->waittime - prev->waittime;
1069 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1070 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1072 /* this can happen if timekeeping isn't accurate enough */
1078 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1081 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1082 time_to_graph(prev->sampledata->sampletime - graph_start),
1084 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1087 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1088 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1089 time_to_graph(prev->sampledata->sampletime - graph_start),
1090 ps_to_graph(j + (1.0 - prt)),
1091 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1096 /* determine where to display the process name */
1097 if ((endtime - starttime) < 1.5)
1098 /* too small to fit label inside the box */
1103 /* text label of process name */
1104 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1105 time_to_graph(w - graph_start) + 5.0,
1106 ps_to_graph(j) + 14.0,
1107 escaped ? escaped : ps->name,
1109 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1110 arg_show_cgroup ? ps->cgroup : "");
1111 /* paint lines to the parent process */
1113 /* horizontal part */
1114 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1115 time_to_graph(starttime - graph_start),
1116 ps_to_graph(j) + 10.0,
1118 ps_to_graph(j) + 10.0);
1120 /* one vertical line connecting all the horizontal ones up */
1122 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1124 ps_to_graph(j) + 10.0,
1129 j++; /* count boxes */
1134 /* last pass - determine when idle */
1136 /* make sure we start counting from the point where we actually have
1137 * data: assume that bootchart's first sample is when data started
1141 while (ps->next_ps) {
1147 /* need to know last node first */
1148 ps->sample = ps->first;
1149 i = ps->sample->next->sampledata->counter;
1151 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1156 struct ps_sched_struct *sample_hz;
1158 ps->sample = ps->sample->next;
1159 sample_hz = ps->sample;
1160 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1161 sample_hz = sample_hz->next;
1163 /* subtract bootchart cpu utilization from total */
1165 for (c = 0; c < cpus; c++)
1166 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1167 brt = sample_hz->runtime - ps->sample->runtime;
1169 * our definition of "idle":
1171 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1172 * defaults to 4.0%, which experimentally, is where atom idles
1174 if ((crt - brt) < (interval / 2.0)) {
1175 idletime = ps->sample->sampledata->sampletime - graph_start;
1176 svg("\n<!-- idle detected at %.03f seconds -->\n",
1178 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1179 time_to_graph(idletime),
1181 time_to_graph(idletime),
1182 ps_to_graph(pcount) + arg_scale_y);
1183 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1184 time_to_graph(idletime) + 5.0,
1185 ps_to_graph(pcount) + arg_scale_y,
1193 static void svg_top_ten_cpu(void) {
1194 struct ps_struct *top[10];
1195 struct ps_struct emptyps = {};
1196 struct ps_struct *ps;
1199 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1202 /* walk all ps's and setup ptrs */
1204 while ((ps = get_next_ps(ps))) {
1205 for (n = 0; n < 10; n++) {
1206 if (ps->total <= top[n]->total)
1208 /* cascade insert */
1209 for (m = 9; m > n; m--)
1216 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1217 for (n = 0; n < 10; n++)
1218 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1225 static void svg_top_ten_pss(void) {
1226 struct ps_struct *top[10];
1227 struct ps_struct emptyps = {};
1228 struct ps_struct *ps;
1231 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1234 /* walk all ps's and setup ptrs */
1236 while ((ps = get_next_ps(ps))) {
1237 for (n = 0; n < 10; n++) {
1238 if (ps->pss_max <= top[n]->pss_max)
1240 /* cascade insert */
1241 for (m = 9; m > n; m--)
1248 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1249 for (n = 0; n < 10; n++)
1250 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1257 void svg_do(const char *build) {
1258 struct ps_struct *ps;
1260 memzero(&str, sizeof(str));
1264 /* count initcall thread count first */
1266 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1268 /* then count processes */
1269 while ((ps = get_next_ps(ps))) {
1275 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1277 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1279 /* after this, we can draw the header with proper sizing */
1281 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1283 svg("<g transform=\"translate(10,400)\">\n");
1287 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1291 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1295 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1300 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1305 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1309 svg("<g transform=\"translate(10, 0)\">\n");
1313 svg("<g transform=\"translate(10,200)\">\n");
1318 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1324 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1328 svg("<g transform=\"translate(410,200)\">\n");