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(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;
427 /* put all the small pss blocks into the bottom */
428 ps = ps_first->next_ps;
429 while (ps->next_ps) {
433 ps->sample = ps->first;
434 while (ps->sample->next) {
435 ps->sample = ps->sample->next;
436 if (ps->sample->sampledata == sampledata)
439 if (ps->sample->sampledata == sampledata) {
440 if (ps->sample->pss <= (100 * arg_scale_y))
441 top += ps->sample->pss;
445 while ((cross_place = ps->sample->cross)) {
446 ps = ps->sample->cross->ps_new;
447 ps->sample = cross_place;
448 if (ps->sample->pss <= (100 * arg_scale_y))
449 top += ps->sample->pss;
453 /* now plot the ones that are of significant size */
455 while (ps->next_ps) {
456 prev_sample = ps->sample;
460 ps->sample = ps->first;
461 while (ps->sample->next) {
462 prev_sample = ps->sample;
463 ps->sample = ps->sample->next;
464 if (ps->sample->sampledata == sampledata)
467 /* don't draw anything smaller than 2mb */
468 if (ps->sample->sampledata == sampledata) {
469 if (ps->sample->pss > (100 * arg_scale_y)) {
470 top = bottom + ps->sample->pss;
471 /* draw a label with the process / PID */
472 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
473 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
474 time_to_graph(sampledata->sampletime - graph_start),
475 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
483 while ((cross_place = ps->sample->cross)) {
484 ps = ps->sample->cross->ps_new;
485 ps->sample = cross_place;
486 prev_sample = ps->sample->prev;
487 if (ps->sample->pss > (100 * arg_scale_y)) {
488 top = bottom + ps->sample->pss;
489 /* draw a label with the process / PID */
490 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
491 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
492 time_to_graph(sampledata->sampletime - graph_start),
493 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
502 /* debug output - full data dump */
503 svg("\n\n<!-- PSS map - csv format -->\n");
505 while (ps->next_ps) {
506 _cleanup_free_ char *enc_name = NULL;
511 enc_name = xml_comment_encode(ps->name);
515 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
517 ps->sample = ps->first;
518 while (ps->sample->next) {
519 ps->sample = ps->sample->next;
520 svg("%d," , ps->sample->pss);
527 static void svg_io_bi_bar(void) {
533 struct list_sample_data *start_sampledata;
534 struct list_sample_data *stop_sampledata;
536 svg("<!-- IO utilization graph - In -->\n");
538 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
541 * calculate rounding range
543 * We need to round IO data since IO block data is not updated on
544 * each poll. Applying a smoothing function loses some burst data,
545 * so keep the smoothing range short.
547 range = 0.25 / (1.0 / arg_hz);
549 range = 2.0; /* no smoothing */
551 /* surrounding box */
554 /* find the max IO first */
556 LIST_FOREACH_BEFORE(link, sampledata, head) {
562 start = MAX(i - ((range / 2) - 1), 0);
563 stop = MIN(i + (range / 2), samples - 1);
564 diff = (stop - start);
566 start_sampledata = sampledata;
567 stop_sampledata = sampledata;
569 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
570 start_sampledata = start_sampledata->link_next;
571 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
572 stop_sampledata = stop_sampledata->link_prev;
574 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
582 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
593 prev_sampledata = head;
594 LIST_FOREACH_BEFORE(link, sampledata, head) {
601 start = MAX(i - ((range / 2) - 1), 0);
602 stop = MIN(i + (range / 2), samples);
603 diff = (stop - start);
605 start_sampledata = sampledata;
606 stop_sampledata = sampledata;
608 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
609 start_sampledata = start_sampledata->link_next;
610 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
611 stop_sampledata = stop_sampledata->link_prev;
613 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
620 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
621 time_to_graph(prev_sampledata->sampletime - graph_start),
622 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
623 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
624 pbi * (arg_scale_y * 5));
626 /* labels around highest value */
628 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
629 time_to_graph(sampledata->sampletime - graph_start) + 5,
630 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
631 max / 1024.0 / (interval / 1000000000.0));
634 prev_sampledata = sampledata;
638 static void svg_io_bo_bar(void) {
644 struct list_sample_data *start_sampledata;
645 struct list_sample_data *stop_sampledata;
647 svg("<!-- IO utilization graph - out -->\n");
649 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
652 * calculate rounding range
654 * We need to round IO data since IO block data is not updated on
655 * each poll. Applying a smoothing function loses some burst data,
656 * so keep the smoothing range short.
658 range = 0.25 / (1.0 / arg_hz);
660 range = 2.0; /* no smoothing */
662 /* surrounding box */
665 /* find the max IO first */
667 LIST_FOREACH_BEFORE(link, sampledata, head) {
673 start = MAX(i - ((range / 2) - 1), 0);
674 stop = MIN(i + (range / 2), samples - 1);
675 diff = (stop - start);
677 start_sampledata = sampledata;
678 stop_sampledata = sampledata;
680 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
681 start_sampledata = start_sampledata->link_next;
682 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
683 stop_sampledata = stop_sampledata->link_prev;
685 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
689 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
699 prev_sampledata = head;
701 LIST_FOREACH_BEFORE(link, sampledata, head) {
711 start = MAX(i - ((range / 2) - 1), 0);
712 stop = MIN(i + (range / 2), samples);
713 diff = (stop - start);
715 start_sampledata = sampledata;
716 stop_sampledata = sampledata;
718 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
719 start_sampledata = start_sampledata->link_next;
720 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
721 stop_sampledata = stop_sampledata->link_prev;
723 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
730 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
731 time_to_graph(prev_sampledata->sampletime - graph_start),
732 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
733 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
734 pbo * (arg_scale_y * 5));
736 /* labels around highest bo value */
738 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
739 time_to_graph(sampledata->sampletime - graph_start) + 5,
740 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
741 max / 1024.0 / (interval / 1000000000.0));
744 prev_sampledata = sampledata;
748 static void svg_cpu_bar(void) {
750 svg("<!-- CPU utilization graph -->\n");
752 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
753 /* surrounding box */
756 /* bars for each sample, proportional to the CPU util. */
757 prev_sampledata = head;
758 LIST_FOREACH_BEFORE(link, sampledata, head) {
765 for (c = 0; c < cpus; c++)
766 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
768 trt = trt / 1000000000.0;
770 trt = trt / (double)cpus;
773 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
779 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
780 time_to_graph(prev_sampledata->sampletime - graph_start),
781 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
782 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
783 ptrt * (arg_scale_y * 5));
785 prev_sampledata = sampledata;
789 static void svg_wait_bar(void) {
791 svg("<!-- Wait time aggregation box -->\n");
793 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
795 /* surrounding box */
798 /* bars for each sample, proportional to the CPU util. */
799 prev_sampledata = head;
800 LIST_FOREACH_BEFORE(link, sampledata, head) {
807 for (c = 0; c < cpus; c++)
808 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
810 twt = twt / 1000000000.0;
812 twt = twt / (double)cpus;
815 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
821 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
822 time_to_graph(prev_sampledata->sampletime - graph_start),
823 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
824 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
825 ptwt * (arg_scale_y * 5));
827 prev_sampledata = sampledata;
832 static void svg_entropy_bar(void) {
834 svg("<!-- entropy pool graph -->\n");
836 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
837 /* surrounding box */
840 /* bars for each sample, scale 0-4096 */
841 prev_sampledata = head;
842 LIST_FOREACH_BEFORE(link, sampledata, head) {
843 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
844 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
845 time_to_graph(prev_sampledata->sampletime - graph_start),
846 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
847 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
848 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
849 prev_sampledata = sampledata;
853 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
855 * walk the list of processes and return the next one to be
869 /* go back for parent siblings */
872 if (ps->parent->next)
873 return ps->parent->next;
882 static int ps_filter(struct ps_struct *ps) {
886 /* can't draw data when there is only 1 sample (need start + stop) */
887 if (ps->first == ps->last)
890 /* don't filter kthreadd */
894 /* drop stuff that doesn't use any real CPU time */
895 if (ps->total <= 0.001)
901 static void svg_do_initcall(int count_only) {
902 _cleanup_pclose_ FILE *f = NULL;
908 /* can't plot initcall when disabled or in relative mode */
909 if (!initcall || arg_relative) {
915 svg("<!-- initcall -->\n");
917 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
918 /* surrounding box */
919 svg_graph_box(kcount);
925 * Initcall graphing - parses dmesg buffer and displays kernel threads
926 * This somewhat uses the same methods and scaling to show processes
927 * but looks a lot simpler. It's overlaid entirely onto the PS graph
931 f = popen("dmesg", "r");
940 if (fgets(l, sizeof(l) - 1, f) == NULL)
943 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
944 &t, func, &ret, &usecs);
946 /* also parse initcalls done by module loading */
947 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
948 &t, func, &ret, &usecs);
953 /* chop the +0xXX/0xXX stuff */
954 while(func[z] != '+')
959 /* filter out irrelevant stuff */
965 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
966 func, t, usecs, ret);
972 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
973 time_to_graph(t - (usecs / 1000000.0)),
975 time_to_graph(usecs / 1000000.0),
979 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
980 time_to_graph(t - (usecs / 1000000.0)) + 5,
981 ps_to_graph(kcount) + 15,
989 static void svg_ps_bars(void) {
990 struct ps_struct *ps;
996 svg("<!-- Process graph -->\n");
998 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1000 /* surrounding box */
1001 svg_graph_box(pcount);
1003 /* pass 2 - ps boxes */
1005 while ((ps = get_next_ps(ps))) {
1006 _cleanup_free_ char *enc_name = NULL;
1011 enc_name = xml_comment_encode(ps->name);
1015 /* leave some trace of what we actually filtered etc. */
1016 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1017 ps->ppid, ps->total);
1019 starttime = ps->first->sampledata->sampletime;
1021 if (!ps_filter(ps)) {
1022 /* remember where _to_ our children need to draw a line */
1023 ps->pos_x = time_to_graph(starttime - graph_start);
1024 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1025 } else if (ps->parent){
1026 /* hook children to our parent coords instead */
1027 ps->pos_x = ps->parent->pos_x;
1028 ps->pos_y = ps->parent->pos_y;
1030 /* if this is the last child, we might still need to draw a connecting line */
1031 if ((!ps->next) && (ps->parent))
1032 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1034 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1040 endtime = ps->last->sampledata->sampletime;
1041 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1042 time_to_graph(starttime - graph_start),
1044 time_to_graph(ps->last->sampledata->sampletime - starttime),
1047 /* paint cpu load over these */
1048 ps->sample = ps->first;
1050 while (ps->sample->next) {
1053 struct ps_sched_struct *prev;
1056 ps->sample = ps->sample->next;
1058 /* calculate over interval */
1059 rt = ps->sample->runtime - prev->runtime;
1060 wt = ps->sample->waittime - prev->waittime;
1062 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1063 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1065 /* this can happen if timekeeping isn't accurate enough */
1071 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1074 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1075 time_to_graph(prev->sampledata->sampletime - graph_start),
1077 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1080 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1081 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1082 time_to_graph(prev->sampledata->sampletime - graph_start),
1083 ps_to_graph(j + (1.0 - prt)),
1084 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1089 /* determine where to display the process name */
1090 if ((endtime - starttime) < 1.5)
1091 /* too small to fit label inside the box */
1096 /* text label of process name */
1097 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
1098 time_to_graph(w - graph_start) + 5.0,
1099 ps_to_graph(j) + 14.0,
1102 (ps->last->runtime - ps->first->runtime) / 1000000000.0);
1103 /* paint lines to the parent process */
1105 /* horizontal part */
1106 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1107 time_to_graph(starttime - graph_start),
1108 ps_to_graph(j) + 10.0,
1110 ps_to_graph(j) + 10.0);
1112 /* one vertical line connecting all the horizontal ones up */
1114 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1116 ps_to_graph(j) + 10.0,
1121 j++; /* count boxes */
1126 /* last pass - determine when idle */
1128 /* make sure we start counting from the point where we actually have
1129 * data: assume that bootchart's first sample is when data started
1133 while (ps->next_ps) {
1139 /* need to know last node first */
1140 ps->sample = ps->first;
1141 i = ps->sample->next->sampledata->counter;
1143 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1148 struct ps_sched_struct *sample_hz;
1150 ps->sample = ps->sample->next;
1151 sample_hz = ps->sample;
1152 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1153 sample_hz = sample_hz->next;
1155 /* subtract bootchart cpu utilization from total */
1157 for (c = 0; c < cpus; c++)
1158 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1159 brt = sample_hz->runtime - ps->sample->runtime;
1161 * our definition of "idle":
1163 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1164 * defaults to 4.0%, which experimentally, is where atom idles
1166 if ((crt - brt) < (interval / 2.0)) {
1167 idletime = ps->sample->sampledata->sampletime - graph_start;
1168 svg("\n<!-- idle detected at %.03f seconds -->\n",
1170 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1171 time_to_graph(idletime),
1173 time_to_graph(idletime),
1174 ps_to_graph(pcount) + arg_scale_y);
1175 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1176 time_to_graph(idletime) + 5.0,
1177 ps_to_graph(pcount) + arg_scale_y,
1185 static void svg_top_ten_cpu(void) {
1186 struct ps_struct *top[10];
1187 struct ps_struct emptyps = {};
1188 struct ps_struct *ps;
1191 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1194 /* walk all ps's and setup ptrs */
1196 while ((ps = get_next_ps(ps))) {
1197 for (n = 0; n < 10; n++) {
1198 if (ps->total <= top[n]->total)
1200 /* cascade insert */
1201 for (m = 9; m > n; m--)
1208 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1209 for (n = 0; n < 10; n++)
1210 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1217 static void svg_top_ten_pss(void) {
1218 struct ps_struct *top[10];
1219 struct ps_struct emptyps = {};
1220 struct ps_struct *ps;
1223 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1226 /* walk all ps's and setup ptrs */
1228 while ((ps = get_next_ps(ps))) {
1229 for (n = 0; n < 10; n++) {
1230 if (ps->pss_max <= top[n]->pss_max)
1232 /* cascade insert */
1233 for (m = 9; m > n; m--)
1240 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1241 for (n = 0; n < 10; n++)
1242 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1249 void svg_do(const char *build) {
1250 struct ps_struct *ps;
1252 memset(&str, 0, sizeof(str));
1256 /* count initcall thread count first */
1258 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1260 /* then count processes */
1261 while ((ps = get_next_ps(ps))) {
1267 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1269 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1271 /* after this, we can draw the header with proper sizing */
1274 svg("<g transform=\"translate(10,400)\">\n");
1278 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1282 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1286 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1291 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1296 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1300 svg("<g transform=\"translate(10, 0)\">\n");
1304 svg("<g transform=\"translate(10,200)\">\n");
1309 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1315 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1319 svg("<g transform=\"translate(410,200)\">\n");