1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Corporation
9 Auke Kok <auke-jan.h.kok@intel.com>
11 systemd is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2.1 of the License, or
14 (at your option) any later version.
16 systemd is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include <sys/utsname.h>
40 #include "bootchart.h"
43 #define time_to_graph(t) ((t) * arg_scale_x)
44 #define ps_to_graph(n) ((n) * arg_scale_y)
45 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
46 #define to_color(n) (192.0 - ((n) * 192.0))
48 static char str[8092];
50 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
52 static const char * const colorwheel[12] = {
53 "rgb(255,32,32)", // red
54 "rgb(32,192,192)", // cyan
55 "rgb(255,128,32)", // orange
56 "rgb(128,32,192)", // blue-violet
57 "rgb(255,255,32)", // yellow
58 "rgb(192,32,128)", // red-violet
59 "rgb(32,255,32)", // green
60 "rgb(255,64,32)", // red-orange
61 "rgb(32,32,255)", // blue
62 "rgb(255,192,32)", // yellow-orange
63 "rgb(192,32,192)", // violet
64 "rgb(32,192,32)" // yellow-green
67 static double idletime = -1.0;
68 static int pfiltered = 0;
69 static int pcount = 0;
70 static int kcount = 0;
71 static double psize = 0;
72 static double ksize = 0;
73 static double esize = 0;
74 static struct list_sample_data *sampledata;
75 static struct list_sample_data *prev_sampledata;
76 extern struct list_sample_data *head;
78 static void svg_header(void) {
81 struct list_sample_data *sampledata_last;
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) {
710 start = MAX(i - ((range / 2) - 1), 0);
711 stop = MIN(i + (range / 2), samples);
712 diff = (stop - start);
714 start_sampledata = sampledata;
715 stop_sampledata = sampledata;
717 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
718 start_sampledata = start_sampledata->link_next;
719 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
720 stop_sampledata = stop_sampledata->link_prev;
722 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
729 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
730 time_to_graph(prev_sampledata->sampletime - graph_start),
731 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
732 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
733 pbo * (arg_scale_y * 5));
735 /* labels around highest bo value */
737 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
738 time_to_graph(sampledata->sampletime - graph_start) + 5,
739 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
740 max / 1024.0 / (interval / 1000000000.0));
743 prev_sampledata = sampledata;
747 static void svg_cpu_bar(void) {
749 svg("<!-- CPU utilization graph -->\n");
751 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
752 /* surrounding box */
755 /* bars for each sample, proportional to the CPU util. */
756 prev_sampledata = head;
757 LIST_FOREACH_BEFORE(link, sampledata, head) {
764 for (c = 0; c < cpus; c++)
765 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
767 trt = trt / 1000000000.0;
769 trt = trt / (double)cpus;
772 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
778 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
779 time_to_graph(prev_sampledata->sampletime - graph_start),
780 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
781 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
782 ptrt * (arg_scale_y * 5));
784 prev_sampledata = sampledata;
788 static void svg_wait_bar(void) {
790 svg("<!-- Wait time aggregation box -->\n");
792 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
794 /* surrounding box */
797 /* bars for each sample, proportional to the CPU util. */
798 prev_sampledata = head;
799 LIST_FOREACH_BEFORE(link, sampledata, head) {
806 for (c = 0; c < cpus; c++)
807 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
809 twt = twt / 1000000000.0;
811 twt = twt / (double)cpus;
814 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
820 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
821 time_to_graph(prev_sampledata->sampletime - graph_start),
822 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
823 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
824 ptwt * (arg_scale_y * 5));
826 prev_sampledata = sampledata;
831 static void svg_entropy_bar(void) {
833 svg("<!-- entropy pool graph -->\n");
835 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
836 /* surrounding box */
839 /* bars for each sample, scale 0-4096 */
840 prev_sampledata = head;
841 LIST_FOREACH_BEFORE(link, sampledata, head) {
842 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
843 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
844 time_to_graph(prev_sampledata->sampletime - graph_start),
845 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
846 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
847 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
848 prev_sampledata = sampledata;
852 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
854 * walk the list of processes and return the next one to be
868 /* go back for parent siblings */
871 if (ps->parent->next)
872 return ps->parent->next;
881 static int ps_filter(struct ps_struct *ps) {
885 /* can't draw data when there is only 1 sample (need start + stop) */
886 if (ps->first == ps->last)
889 /* don't filter kthreadd */
893 /* drop stuff that doesn't use any real CPU time */
894 if (ps->total <= 0.001)
900 static void svg_do_initcall(int count_only) {
901 _cleanup_pclose_ FILE *f = NULL;
907 /* can't plot initcall when disabled or in relative mode */
908 if (!initcall || arg_relative) {
914 svg("<!-- initcall -->\n");
916 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
917 /* surrounding box */
918 svg_graph_box(kcount);
924 * Initcall graphing - parses dmesg buffer and displays kernel threads
925 * This somewhat uses the same methods and scaling to show processes
926 * but looks a lot simpler. It's overlaid entirely onto the PS graph
930 f = popen("dmesg", "r");
939 if (fgets(l, sizeof(l) - 1, f) == NULL)
942 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
943 &t, func, &ret, &usecs);
945 /* also parse initcalls done by module loading */
946 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
947 &t, func, &ret, &usecs);
952 /* chop the +0xXX/0xXX stuff */
953 while(func[z] != '+')
958 /* filter out irrelevant stuff */
964 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
965 func, t, usecs, ret);
971 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
972 time_to_graph(t - (usecs / 1000000.0)),
974 time_to_graph(usecs / 1000000.0),
978 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
979 time_to_graph(t - (usecs / 1000000.0)) + 5,
980 ps_to_graph(kcount) + 15,
988 static void svg_ps_bars(void) {
989 struct ps_struct *ps;
995 svg("<!-- Process graph -->\n");
997 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
999 /* surrounding box */
1000 svg_graph_box(pcount);
1002 /* pass 2 - ps boxes */
1004 while ((ps = get_next_ps(ps))) {
1005 _cleanup_free_ char *enc_name = NULL;
1010 enc_name = xml_comment_encode(ps->name);
1014 /* leave some trace of what we actually filtered etc. */
1015 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1016 ps->ppid, ps->total);
1018 starttime = ps->first->sampledata->sampletime;
1020 if (!ps_filter(ps)) {
1021 /* remember where _to_ our children need to draw a line */
1022 ps->pos_x = time_to_graph(starttime - graph_start);
1023 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1024 } else if (ps->parent){
1025 /* hook children to our parent coords instead */
1026 ps->pos_x = ps->parent->pos_x;
1027 ps->pos_y = ps->parent->pos_y;
1029 /* if this is the last child, we might still need to draw a connecting line */
1030 if ((!ps->next) && (ps->parent))
1031 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1033 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1039 endtime = ps->last->sampledata->sampletime;
1040 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1041 time_to_graph(starttime - graph_start),
1043 time_to_graph(ps->last->sampledata->sampletime - starttime),
1046 /* paint cpu load over these */
1047 ps->sample = ps->first;
1049 while (ps->sample->next) {
1052 struct ps_sched_struct *prev;
1055 ps->sample = ps->sample->next;
1057 /* calculate over interval */
1058 rt = ps->sample->runtime - prev->runtime;
1059 wt = ps->sample->waittime - prev->waittime;
1061 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1062 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1064 /* this can happen if timekeeping isn't accurate enough */
1070 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1073 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1074 time_to_graph(prev->sampledata->sampletime - graph_start),
1076 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1079 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1080 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1081 time_to_graph(prev->sampledata->sampletime - graph_start),
1082 ps_to_graph(j + (1.0 - prt)),
1083 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1088 /* determine where to display the process name */
1089 if ((endtime - starttime) < 1.5)
1090 /* too small to fit label inside the box */
1095 /* text label of process name */
1096 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
1097 time_to_graph(w - graph_start) + 5.0,
1098 ps_to_graph(j) + 14.0,
1101 (ps->last->runtime - ps->first->runtime) / 1000000000.0);
1102 /* paint lines to the parent process */
1104 /* horizontal part */
1105 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1106 time_to_graph(starttime - graph_start),
1107 ps_to_graph(j) + 10.0,
1109 ps_to_graph(j) + 10.0);
1111 /* one vertical line connecting all the horizontal ones up */
1113 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1115 ps_to_graph(j) + 10.0,
1120 j++; /* count boxes */
1125 /* last pass - determine when idle */
1127 /* make sure we start counting from the point where we actually have
1128 * data: assume that bootchart's first sample is when data started
1132 while (ps->next_ps) {
1138 /* need to know last node first */
1139 ps->sample = ps->first;
1140 i = ps->sample->next->sampledata->counter;
1142 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1147 struct ps_sched_struct *sample_hz;
1149 ps->sample = ps->sample->next;
1150 sample_hz = ps->sample;
1151 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1152 sample_hz = sample_hz->next;
1154 /* subtract bootchart cpu utilization from total */
1156 for (c = 0; c < cpus; c++)
1157 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1158 brt = sample_hz->runtime - ps->sample->runtime;
1160 * our definition of "idle":
1162 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1163 * defaults to 4.0%, which experimentally, is where atom idles
1165 if ((crt - brt) < (interval / 2.0)) {
1166 idletime = ps->sample->sampledata->sampletime - graph_start;
1167 svg("\n<!-- idle detected at %.03f seconds -->\n",
1169 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1170 time_to_graph(idletime),
1172 time_to_graph(idletime),
1173 ps_to_graph(pcount) + arg_scale_y);
1174 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1175 time_to_graph(idletime) + 5.0,
1176 ps_to_graph(pcount) + arg_scale_y,
1184 static void svg_top_ten_cpu(void) {
1185 struct ps_struct *top[10];
1186 struct ps_struct emptyps = {};
1187 struct ps_struct *ps;
1190 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1193 /* walk all ps's and setup ptrs */
1195 while ((ps = get_next_ps(ps))) {
1196 for (n = 0; n < 10; n++) {
1197 if (ps->total <= top[n]->total)
1199 /* cascade insert */
1200 for (m = 9; m > n; m--)
1207 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1208 for (n = 0; n < 10; n++)
1209 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1216 static void svg_top_ten_pss(void) {
1217 struct ps_struct *top[10];
1218 struct ps_struct emptyps = {};
1219 struct ps_struct *ps;
1222 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1225 /* walk all ps's and setup ptrs */
1227 while ((ps = get_next_ps(ps))) {
1228 for (n = 0; n < 10; n++) {
1229 if (ps->pss_max <= top[n]->pss_max)
1231 /* cascade insert */
1232 for (m = 9; m > n; m--)
1239 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1240 for (n = 0; n < 10; n++)
1241 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1248 void svg_do(const char *build) {
1249 struct ps_struct *ps;
1251 memset(&str, 0, sizeof(str));
1255 /* count initcall thread count first */
1257 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1259 /* then count processes */
1260 while ((ps = get_next_ps(ps))) {
1266 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1268 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1270 /* after this, we can draw the header with proper sizing */
1273 svg("<g transform=\"translate(10,400)\">\n");
1277 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1281 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1285 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1290 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1295 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1299 svg("<g transform=\"translate(10, 0)\">\n");
1303 svg("<g transform=\"translate(10,200)\">\n");
1308 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1314 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1318 svg("<g transform=\"translate(410,200)\">\n");