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 float psize = 0;
72 static float ksize = 0;
73 static float esize = 0;
74 static struct list_sample_data *sampledata;
75 static struct list_sample_data *prev_sampledata;
76 extern struct list_sample_data *head;
78 static void svg_header(void) {
81 struct list_sample_data *sampledata_last;
84 LIST_FIND_TAIL(struct list_sample_data, link, sampledata, head);
85 sampledata_last = head;
86 LIST_FOREACH_BEFORE(link, sampledata, head) {
87 sampledata_last = sampledata;
90 /* min width is about 1600px due to the label */
91 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
92 w = ((w < 1600.0) ? 1600.0 : w);
94 /* height is variable based on pss, psize, ksize */
95 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
96 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
97 + psize + ksize + esize;
99 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
100 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
101 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
103 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
104 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
106 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
108 /* write some basic info as a comment, including some help */
109 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
110 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
111 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
112 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
113 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
115 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
116 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
117 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
118 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
119 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
120 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
123 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
125 svg(" rect { stroke-width: 1; }\n");
126 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
127 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
128 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
129 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
130 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
131 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
132 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
133 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
134 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
135 svg("// line.sec1 { }\n");
136 svg(" line.sec5 { stroke-width: 2; }\n");
137 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
138 svg(" line.dot { stroke-dasharray: 2 4; }\n");
139 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
141 svg(" .run { font-size: 8; font-style: italic; }\n");
142 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
143 svg(" text.sec { font-size: 8; }\n");
144 svg(" text.t1 { font-size: 24; }\n");
145 svg(" text.t2 { font-size: 12; }\n");
146 svg(" text.idle { font-size: 18; }\n");
148 svg(" ]]>\n </style>\n</defs>\n\n");
151 static void svg_title(const char *build) {
152 char cmdline[256] = "";
153 char filename[PATH_MAX];
155 char rootbdev[16] = "Unknown";
156 char model[256] = "Unknown";
157 char date[256] = "Unknown";
158 char cpu[256] = "Unknown";
165 /* grab /proc/cmdline */
166 fd = openat(procfd, "cmdline", O_RDONLY);
169 if (!fgets(cmdline, 255, f))
170 sprintf(cmdline, "Unknown");
174 /* extract root fs so we can find disk model name in sysfs */
175 /* FIXME: this works only in the simple case */
176 c = strstr(cmdline, "root=/dev/");
178 strncpy(rootbdev, &c[10], 3);
180 sprintf(filename, "block/%s/device/model", rootbdev);
181 fd = openat(sysfd, filename, O_RDONLY);
184 if (!fgets(model, 255, f))
185 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
190 /* various utsname parameters */
192 fprintf(stderr, "Error getting uname info\n");
196 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
199 fd = openat(procfd, "cpuinfo", O_RDONLY);
202 while (fgets(buf, 255, f)) {
203 if (strstr(buf, "model name")) {
204 strncpy(cpu, &buf[13], 255);
211 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
213 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
214 uts.sysname, uts.release, uts.version, uts.machine);
215 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
217 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
219 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
221 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
223 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
224 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
227 svg("%.03fs", idletime);
231 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",
232 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
235 static void svg_graph_box(int height) {
238 double finalsample = 0.0;
239 struct list_sample_data *sampledata_last;
241 sampledata_last = head;
242 LIST_FOREACH_BEFORE(link, sampledata, head) {
243 sampledata_last = sampledata;
246 finalsample = sampledata_last->sampletime;
248 /* outside box, fill */
249 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
251 time_to_graph(finalsample - graph_start),
252 ps_to_graph(height));
254 for (d = graph_start; d <= finalsample;
255 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
256 /* lines for each second */
258 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
259 time_to_graph(d - graph_start),
260 time_to_graph(d - graph_start),
261 ps_to_graph(height));
262 else if (i % 10 == 0)
263 svg(" <line class=\"sec1\" 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));
268 svg(" <line class=\"sec01\" 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));
275 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
276 time_to_graph(d - graph_start),
284 /* xml comments must not contain "--" */
285 static char* xml_comment_encode(const char* name) {
288 enc_name = strdup(name);
292 for (p = enc_name; *p; p++)
293 if (p[0] == '-' && p[1] == '-')
299 static void svg_pss_graph(void) {
300 struct ps_struct *ps;
302 struct list_sample_data *sampledata_last;
304 sampledata_last = head;
305 LIST_FOREACH_BEFORE(link, sampledata, head) {
306 sampledata_last = sampledata;
310 svg("\n\n<!-- Pss memory size graph -->\n");
312 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
314 /* vsize 1000 == 1000mb */
316 /* draw some hlines for usable memory sizes */
317 for (i = 100000; i < 1000000; i += 100000) {
318 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
321 time_to_graph(sampledata_last->sampletime - graph_start),
323 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
324 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
325 kb_to_graph(i), (1000000 - i) / 1000);
329 /* now plot the graph itself */
331 prev_sampledata = head;
332 LIST_FOREACH_BEFORE(link, sampledata, head) {
335 struct ps_sched_struct *cross_place;
340 /* put all the small pss blocks into the bottom */
342 while (ps->next_ps) {
346 ps->sample = ps->first;
347 while (ps->sample->next) {
348 ps->sample = ps->sample->next;
349 if (ps->sample->sampledata == sampledata)
352 if (ps->sample->sampledata == sampledata) {
353 if (ps->sample->pss <= (100 * arg_scale_y))
354 top += ps->sample->pss;
358 while (ps->sample->cross) {
359 cross_place = ps->sample->cross;
360 ps = ps->sample->cross->ps_new;
361 ps->sample = cross_place;
362 if (ps->sample->pss <= (100 * arg_scale_y))
363 top += ps->sample->pss;
366 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
368 time_to_graph(prev_sampledata->sampletime - graph_start),
369 kb_to_graph(1000000.0 - top),
370 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
371 kb_to_graph(top - bottom));
374 /* now plot the ones that are of significant size */
376 while (ps->next_ps) {
380 ps->sample = ps->first;
381 while (ps->sample->next) {
382 ps->sample = ps->sample->next;
383 if (ps->sample->sampledata == sampledata)
386 /* don't draw anything smaller than 2mb */
387 if (ps->sample->sampledata == sampledata) {
388 if (ps->sample->pss > (100 * arg_scale_y)) {
389 top = bottom + ps->sample->pss;
390 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
391 colorwheel[ps->pid % 12],
392 time_to_graph(prev_sampledata->sampletime - graph_start),
393 kb_to_graph(1000000.0 - top),
394 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
395 kb_to_graph(top - bottom));
401 while ((cross_place = ps->sample->cross)) {
402 ps = ps->sample->cross->ps_new;
403 ps->sample = cross_place;
404 if (ps->sample->pss > (100 * arg_scale_y)) {
405 top = bottom + ps->sample->pss;
406 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
407 colorwheel[ps->pid % 12],
408 time_to_graph(prev_sampledata->sampletime - graph_start),
409 kb_to_graph(1000000.0 - top),
410 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
411 kb_to_graph(top - bottom));
415 prev_sampledata = sampledata;
419 /* overlay all the text labels */
421 LIST_FOREACH_BEFORE(link, sampledata, head) {
424 struct ps_sched_struct *prev_sample;
425 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 = sampledata;
537 struct list_sample_data *stop_sampledata = 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) {
607 start = MAX(i - ((range / 2) - 1), 0);
608 stop = MIN(i + (range / 2), samples);
609 diff = (stop - start);
611 start_sampledata = sampledata;
612 stop_sampledata = sampledata;
614 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
615 start_sampledata = start_sampledata->link_next;
616 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
617 stop_sampledata = stop_sampledata->link_prev;
619 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
626 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
627 time_to_graph(prev_sampledata->sampletime - graph_start),
628 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
629 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
630 pbi * (arg_scale_y * 5));
632 /* labels around highest value */
634 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
635 time_to_graph(sampledata->sampletime - graph_start) + 5,
636 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
637 max / 1024.0 / (interval / 1000000000.0));
640 prev_sampledata = sampledata;
644 static void svg_io_bo_bar(void) {
650 struct list_sample_data *start_sampledata = sampledata;
651 struct list_sample_data *stop_sampledata = sampledata;
653 svg("<!-- IO utilization graph - out -->\n");
655 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
658 * calculate rounding range
660 * We need to round IO data since IO block data is not updated on
661 * each poll. Applying a smoothing function loses some burst data,
662 * so keep the smoothing range short.
664 range = 0.25 / (1.0 / arg_hz);
666 range = 2.0; /* no smoothing */
668 /* surrounding box */
671 /* find the max IO first */
673 LIST_FOREACH_BEFORE(link, sampledata, head) {
679 start = MAX(i - ((range / 2) - 1), 0);
680 stop = MIN(i + (range / 2), samples - 1);
681 diff = (stop - start);
683 start_sampledata = sampledata;
684 stop_sampledata = sampledata;
686 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
687 start_sampledata = start_sampledata->link_next;
688 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
689 stop_sampledata = stop_sampledata->link_prev;
691 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
695 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
705 prev_sampledata = head;
707 LIST_FOREACH_BEFORE(link, sampledata, head) {
717 start = MAX(i - ((range / 2) - 1), 0);
718 stop = MIN(i + (range / 2), samples);
719 diff = (stop - start);
721 start_sampledata = sampledata;
722 stop_sampledata = sampledata;
724 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
725 start_sampledata = start_sampledata->link_next;
726 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
727 stop_sampledata = stop_sampledata->link_prev;
729 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
736 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
737 time_to_graph(prev_sampledata->sampletime - graph_start),
738 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
739 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
740 pbo * (arg_scale_y * 5));
742 /* labels around highest bo value */
744 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
745 time_to_graph(sampledata->sampletime - graph_start) + 5,
746 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
747 max / 1024.0 / (interval / 1000000000.0));
750 prev_sampledata = sampledata;
754 static void svg_cpu_bar(void) {
756 svg("<!-- CPU utilization graph -->\n");
758 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
759 /* surrounding box */
762 /* bars for each sample, proportional to the CPU util. */
763 prev_sampledata = head;
764 LIST_FOREACH_BEFORE(link, sampledata, head) {
771 for (c = 0; c < cpus; c++)
772 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
774 trt = trt / 1000000000.0;
776 trt = trt / (double)cpus;
779 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
785 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
786 time_to_graph(prev_sampledata->sampletime - graph_start),
787 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
788 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
789 ptrt * (arg_scale_y * 5));
791 prev_sampledata = sampledata;
795 static void svg_wait_bar(void) {
797 svg("<!-- Wait time aggregation box -->\n");
799 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
801 /* surrounding box */
804 /* bars for each sample, proportional to the CPU util. */
805 prev_sampledata = head;
806 LIST_FOREACH_BEFORE(link, sampledata, head) {
813 for (c = 0; c < cpus; c++)
814 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
816 twt = twt / 1000000000.0;
818 twt = twt / (double)cpus;
821 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
827 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
828 time_to_graph(prev_sampledata->sampletime - graph_start),
829 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
830 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
831 ptwt * (arg_scale_y * 5));
833 prev_sampledata = sampledata;
838 static void svg_entropy_bar(void) {
840 svg("<!-- entropy pool graph -->\n");
842 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
843 /* surrounding box */
846 /* bars for each sample, scale 0-4096 */
847 prev_sampledata = head;
848 LIST_FOREACH_BEFORE(link, sampledata, head) {
849 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
850 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
851 time_to_graph(prev_sampledata->sampletime - graph_start),
852 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
853 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
854 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
855 prev_sampledata = sampledata;
859 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
861 * walk the list of processes and return the next one to be
875 /* go back for parent siblings */
878 if (ps->parent->next)
879 return ps->parent->next;
888 static int ps_filter(struct ps_struct *ps) {
892 /* can't draw data when there is only 1 sample (need start + stop) */
893 if (ps->first == ps->last)
896 /* don't filter kthreadd */
900 /* drop stuff that doesn't use any real CPU time */
901 if (ps->total <= 0.001)
907 static void svg_do_initcall(int count_only) {
908 _cleanup_pclose_ FILE *f = NULL;
914 /* can't plot initcall when disabled or in relative mode */
915 if (!initcall || arg_relative) {
921 svg("<!-- initcall -->\n");
923 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
924 /* surrounding box */
925 svg_graph_box(kcount);
931 * Initcall graphing - parses dmesg buffer and displays kernel threads
932 * This somewhat uses the same methods and scaling to show processes
933 * but looks a lot simpler. It's overlaid entirely onto the PS graph
937 f = popen("dmesg", "r");
946 if (fgets(l, sizeof(l) - 1, f) == NULL)
949 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
950 &t, func, &ret, &usecs);
952 /* also parse initcalls done by module loading */
953 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
954 &t, func, &ret, &usecs);
959 /* chop the +0xXX/0xXX stuff */
960 while(func[z] != '+')
965 /* filter out irrelevant stuff */
971 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
972 func, t, usecs, ret);
978 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
979 time_to_graph(t - (usecs / 1000000.0)),
981 time_to_graph(usecs / 1000000.0),
985 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
986 time_to_graph(t - (usecs / 1000000.0)) + 5,
987 ps_to_graph(kcount) + 15,
995 static void svg_ps_bars(void) {
996 struct ps_struct *ps;
1002 svg("<!-- Process graph -->\n");
1004 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1006 /* surrounding box */
1007 svg_graph_box(pcount);
1009 /* pass 2 - ps boxes */
1011 while ((ps = get_next_ps(ps))) {
1012 _cleanup_free_ char *enc_name = NULL;
1017 enc_name = xml_comment_encode(ps->name);
1021 /* leave some trace of what we actually filtered etc. */
1022 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1023 ps->ppid, ps->total);
1025 starttime = ps->first->sampledata->sampletime;
1027 if (!ps_filter(ps)) {
1028 /* remember where _to_ our children need to draw a line */
1029 ps->pos_x = time_to_graph(starttime - graph_start);
1030 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1031 } else if (ps->parent){
1032 /* hook children to our parent coords instead */
1033 ps->pos_x = ps->parent->pos_x;
1034 ps->pos_y = ps->parent->pos_y;
1036 /* if this is the last child, we might still need to draw a connecting line */
1037 if ((!ps->next) && (ps->parent))
1038 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1040 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1046 endtime = ps->last->sampledata->sampletime;
1047 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1048 time_to_graph(starttime - graph_start),
1050 time_to_graph(ps->last->sampledata->sampletime - starttime),
1053 /* paint cpu load over these */
1054 ps->sample = ps->first;
1056 while (ps->sample->next) {
1059 struct ps_sched_struct *prev;
1062 ps->sample = ps->sample->next;
1064 /* calculate over interval */
1065 rt = ps->sample->runtime - prev->runtime;
1066 wt = ps->sample->waittime - prev->waittime;
1068 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1069 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1071 /* this can happen if timekeeping isn't accurate enough */
1077 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1080 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1081 time_to_graph(prev->sampledata->sampletime - graph_start),
1083 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1086 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1087 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1088 time_to_graph(prev->sampledata->sampletime - graph_start),
1089 ps_to_graph(j + (1.0 - prt)),
1090 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1095 /* determine where to display the process name */
1096 if ((endtime - starttime) < 1.5)
1097 /* too small to fit label inside the box */
1102 /* text label of process name */
1103 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
1104 time_to_graph(w - graph_start) + 5.0,
1105 ps_to_graph(j) + 14.0,
1108 (ps->last->runtime - ps->first->runtime) / 1000000000.0);
1109 /* paint lines to the parent process */
1111 /* horizontal part */
1112 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1113 time_to_graph(starttime - graph_start),
1114 ps_to_graph(j) + 10.0,
1116 ps_to_graph(j) + 10.0);
1118 /* one vertical line connecting all the horizontal ones up */
1120 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1122 ps_to_graph(j) + 10.0,
1127 j++; /* count boxes */
1132 /* last pass - determine when idle */
1134 /* make sure we start counting from the point where we actually have
1135 * data: assume that bootchart's first sample is when data started
1139 while (ps->next_ps) {
1145 /* need to know last node first */
1146 ps->sample = ps->first;
1147 i = ps->sample->next->sampledata->counter;
1149 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1154 struct ps_sched_struct *sample_hz;
1156 ps->sample = ps->sample->next;
1157 sample_hz = ps->sample;
1158 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1159 sample_hz = sample_hz->next;
1161 /* subtract bootchart cpu utilization from total */
1163 for (c = 0; c < cpus; c++)
1164 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1165 brt = sample_hz->runtime - ps->sample->runtime;
1167 * our definition of "idle":
1169 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1170 * defaults to 4.0%, which experimentally, is where atom idles
1172 if ((crt - brt) < (interval / 2.0)) {
1173 idletime = ps->sample->sampledata->sampletime - graph_start;
1174 svg("\n<!-- idle detected at %.03f seconds -->\n",
1176 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1177 time_to_graph(idletime),
1179 time_to_graph(idletime),
1180 ps_to_graph(pcount) + arg_scale_y);
1181 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1182 time_to_graph(idletime) + 5.0,
1183 ps_to_graph(pcount) + arg_scale_y,
1191 static void svg_top_ten_cpu(void) {
1192 struct ps_struct *top[10];
1193 struct ps_struct emptyps = {};
1194 struct ps_struct *ps;
1197 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1200 /* walk all ps's and setup ptrs */
1202 while ((ps = get_next_ps(ps))) {
1203 for (n = 0; n < 10; n++) {
1204 if (ps->total <= top[n]->total)
1206 /* cascade insert */
1207 for (m = 9; m > n; m--)
1214 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1215 for (n = 0; n < 10; n++)
1216 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1223 static void svg_top_ten_pss(void) {
1224 struct ps_struct *top[10];
1225 struct ps_struct emptyps = {};
1226 struct ps_struct *ps;
1229 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1232 /* walk all ps's and setup ptrs */
1234 while ((ps = get_next_ps(ps))) {
1235 for (n = 0; n < 10; n++) {
1236 if (ps->pss_max <= top[n]->pss_max)
1238 /* cascade insert */
1239 for (m = 9; m > n; m--)
1246 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1247 for (n = 0; n < 10; n++)
1248 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1255 void svg_do(const char *build) {
1256 struct ps_struct *ps;
1258 memset(&str, 0, sizeof(str));
1262 /* count initcall thread count first */
1264 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1266 /* then count processes */
1267 while ((ps = get_next_ps(ps))) {
1273 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1275 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1277 /* after this, we can draw the header with proper sizing */
1280 svg("<g transform=\"translate(10,400)\">\n");
1284 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1288 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1292 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1297 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1302 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1306 svg("<g transform=\"translate(10, 0)\">\n");
1310 svg("<g transform=\"translate(10,200)\">\n");
1315 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1321 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1325 svg("<g transform=\"translate(410,200)\">\n");