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/>.
30 #include <sys/utsname.h>
37 #include "bootchart.h"
41 #define time_to_graph(t) ((t) * arg_scale_x)
42 #define ps_to_graph(n) ((n) * arg_scale_y)
43 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
44 #define to_color(n) (192.0 - ((n) * 192.0))
46 static char str[8092];
48 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
50 static const char * const colorwheel[12] = {
51 "rgb(255,32,32)", // red
52 "rgb(32,192,192)", // cyan
53 "rgb(255,128,32)", // orange
54 "rgb(128,32,192)", // blue-violet
55 "rgb(255,255,32)", // yellow
56 "rgb(192,32,128)", // red-violet
57 "rgb(32,255,32)", // green
58 "rgb(255,64,32)", // red-orange
59 "rgb(32,32,255)", // blue
60 "rgb(255,192,32)", // yellow-orange
61 "rgb(192,32,192)", // violet
62 "rgb(32,192,32)" // yellow-green
65 static double idletime = -1.0;
66 static int pfiltered = 0;
67 static int pcount = 0;
68 static int kcount = 0;
69 static double psize = 0;
70 static double ksize = 0;
71 static double esize = 0;
72 static struct list_sample_data *sampledata;
73 static struct list_sample_data *prev_sampledata;
74 extern struct list_sample_data *head;
76 static void svg_header(void) {
79 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.bg { fill: rgb(255,255,255); }\n");
127 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
128 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
129 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
130 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
132 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
133 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
134 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
135 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
136 svg("// line.sec1 { }\n");
137 svg(" line.sec5 { stroke-width: 2; }\n");
138 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
139 svg(" line.dot { stroke-dasharray: 2 4; }\n");
140 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
142 svg(" .run { font-size: 8; font-style: italic; }\n");
143 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
144 svg(" text.sec { font-size: 8; }\n");
145 svg(" text.t1 { font-size: 24; }\n");
146 svg(" text.t2 { font-size: 12; }\n");
147 svg(" text.idle { font-size: 18; }\n");
149 svg(" ]]>\n </style>\n</defs>\n\n");
152 static void svg_title(const char *build) {
153 char cmdline[256] = "";
154 char filename[PATH_MAX];
156 char rootbdev[16] = "Unknown";
157 char model[256] = "Unknown";
158 char date[256] = "Unknown";
159 char cpu[256] = "Unknown";
166 /* grab /proc/cmdline */
167 fd = openat(procfd, "cmdline", O_RDONLY);
170 if (!fgets(cmdline, 255, f))
171 sprintf(cmdline, "Unknown");
178 /* extract root fs so we can find disk model name in sysfs */
179 /* FIXME: this works only in the simple case */
180 c = strstr(cmdline, "root=/dev/");
182 strncpy(rootbdev, &c[10], 3);
184 sprintf(filename, "block/%s/device/model", rootbdev);
185 fd = openat(sysfd, filename, O_RDONLY);
188 if (!fgets(model, 255, f))
189 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
197 /* various utsname parameters */
199 fprintf(stderr, "Error getting uname info\n");
203 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
207 fd = openat(procfd, "cpuinfo", O_RDONLY);
210 while (fgets(buf, 255, f)) {
211 if (strstr(buf, "model name")) {
212 strncpy(cpu, &buf[13], 255);
222 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
225 uts.sysname, uts.release, uts.version, uts.machine);
226 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
228 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
230 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
232 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
234 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
235 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
238 svg("%.03fs", idletime);
242 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",
243 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
246 static void svg_graph_box(int height) {
249 double finalsample = 0.0;
250 struct list_sample_data *sampledata_last;
252 sampledata_last = head;
253 LIST_FOREACH_BEFORE(link, sampledata, head) {
254 sampledata_last = sampledata;
257 finalsample = sampledata_last->sampletime;
259 /* outside box, fill */
260 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
262 time_to_graph(finalsample - graph_start),
263 ps_to_graph(height));
265 for (d = graph_start; d <= finalsample;
266 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
267 /* lines for each second */
269 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
270 time_to_graph(d - graph_start),
271 time_to_graph(d - graph_start),
272 ps_to_graph(height));
273 else if (i % 10 == 0)
274 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
275 time_to_graph(d - graph_start),
276 time_to_graph(d - graph_start),
277 ps_to_graph(height));
279 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
280 time_to_graph(d - graph_start),
281 time_to_graph(d - graph_start),
282 ps_to_graph(height));
286 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
287 time_to_graph(d - graph_start),
295 /* xml comments must not contain "--" */
296 static char* xml_comment_encode(const char* name) {
299 enc_name = strdup(name);
303 for (p = enc_name; *p; p++)
304 if (p[0] == '-' && p[1] == '-')
310 static void svg_pss_graph(void) {
311 struct ps_struct *ps;
313 struct list_sample_data *sampledata_last;
315 sampledata_last = head;
316 LIST_FOREACH_BEFORE(link, sampledata, head) {
317 sampledata_last = sampledata;
321 svg("\n\n<!-- Pss memory size graph -->\n");
323 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
325 /* vsize 1000 == 1000mb */
327 /* draw some hlines for usable memory sizes */
328 for (i = 100000; i < 1000000; i += 100000) {
329 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
332 time_to_graph(sampledata_last->sampletime - graph_start),
334 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
335 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
336 kb_to_graph(i), (1000000 - i) / 1000);
340 /* now plot the graph itself */
342 prev_sampledata = head;
343 LIST_FOREACH_BEFORE(link, sampledata, head) {
346 struct ps_sched_struct *cross_place;
351 /* put all the small pss blocks into the bottom */
353 while (ps->next_ps) {
357 ps->sample = ps->first;
358 while (ps->sample->next) {
359 ps->sample = ps->sample->next;
360 if (ps->sample->sampledata == sampledata)
363 if (ps->sample->sampledata == sampledata) {
364 if (ps->sample->pss <= (100 * arg_scale_y))
365 top += ps->sample->pss;
369 while (ps->sample->cross) {
370 cross_place = ps->sample->cross;
371 ps = ps->sample->cross->ps_new;
372 ps->sample = cross_place;
373 if (ps->sample->pss <= (100 * arg_scale_y))
374 top += ps->sample->pss;
377 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
379 time_to_graph(prev_sampledata->sampletime - graph_start),
380 kb_to_graph(1000000.0 - top),
381 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
382 kb_to_graph(top - bottom));
385 /* now plot the ones that are of significant size */
387 while (ps->next_ps) {
391 ps->sample = ps->first;
392 while (ps->sample->next) {
393 ps->sample = ps->sample->next;
394 if (ps->sample->sampledata == sampledata)
397 /* don't draw anything smaller than 2mb */
398 if (ps->sample->sampledata == sampledata) {
399 if (ps->sample->pss > (100 * arg_scale_y)) {
400 top = bottom + ps->sample->pss;
401 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
402 colorwheel[ps->pid % 12],
403 time_to_graph(prev_sampledata->sampletime - graph_start),
404 kb_to_graph(1000000.0 - top),
405 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
406 kb_to_graph(top - bottom));
412 while ((cross_place = ps->sample->cross)) {
413 ps = ps->sample->cross->ps_new;
414 ps->sample = cross_place;
415 if (ps->sample->pss > (100 * arg_scale_y)) {
416 top = bottom + ps->sample->pss;
417 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
418 colorwheel[ps->pid % 12],
419 time_to_graph(prev_sampledata->sampletime - graph_start),
420 kb_to_graph(1000000.0 - top),
421 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
422 kb_to_graph(top - bottom));
426 prev_sampledata = sampledata;
430 /* overlay all the text labels */
432 LIST_FOREACH_BEFORE(link, sampledata, head) {
435 struct ps_sched_struct *prev_sample;
436 struct ps_sched_struct *cross_place;
438 /* put all the small pss blocks into the bottom */
439 ps = ps_first->next_ps;
440 while (ps->next_ps) {
444 ps->sample = ps->first;
445 while (ps->sample->next) {
446 ps->sample = ps->sample->next;
447 if (ps->sample->sampledata == sampledata)
450 if (ps->sample->sampledata == sampledata) {
451 if (ps->sample->pss <= (100 * arg_scale_y))
452 top += ps->sample->pss;
456 while ((cross_place = ps->sample->cross)) {
457 ps = ps->sample->cross->ps_new;
458 ps->sample = cross_place;
459 if (ps->sample->pss <= (100 * arg_scale_y))
460 top += ps->sample->pss;
464 /* now plot the ones that are of significant size */
466 while (ps->next_ps) {
467 prev_sample = ps->sample;
471 ps->sample = ps->first;
472 while (ps->sample->next) {
473 prev_sample = ps->sample;
474 ps->sample = ps->sample->next;
475 if (ps->sample->sampledata == sampledata)
478 /* don't draw anything smaller than 2mb */
479 if (ps->sample->sampledata == sampledata) {
480 if (ps->sample->pss > (100 * arg_scale_y)) {
481 top = bottom + ps->sample->pss;
482 /* draw a label with the process / PID */
483 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
484 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
485 time_to_graph(sampledata->sampletime - graph_start),
486 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
494 while ((cross_place = ps->sample->cross)) {
495 ps = ps->sample->cross->ps_new;
496 ps->sample = cross_place;
497 prev_sample = ps->sample->prev;
498 if (ps->sample->pss > (100 * arg_scale_y)) {
499 top = bottom + ps->sample->pss;
500 /* draw a label with the process / PID */
501 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
502 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
503 time_to_graph(sampledata->sampletime - graph_start),
504 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
513 /* debug output - full data dump */
514 svg("\n\n<!-- PSS map - csv format -->\n");
516 while (ps->next_ps) {
517 _cleanup_free_ char *enc_name = NULL;
522 enc_name = xml_comment_encode(ps->name);
526 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
528 ps->sample = ps->first;
529 while (ps->sample->next) {
530 ps->sample = ps->sample->next;
531 svg("%d," , ps->sample->pss);
538 static void svg_io_bi_bar(void) {
544 struct list_sample_data *start_sampledata;
545 struct list_sample_data *stop_sampledata;
547 svg("<!-- IO utilization graph - In -->\n");
549 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
552 * calculate rounding range
554 * We need to round IO data since IO block data is not updated on
555 * each poll. Applying a smoothing function loses some burst data,
556 * so keep the smoothing range short.
558 range = 0.25 / (1.0 / arg_hz);
560 range = 2.0; /* no smoothing */
562 /* surrounding box */
565 /* find the max IO first */
567 LIST_FOREACH_BEFORE(link, sampledata, head) {
573 start = MAX(i - ((range / 2) - 1), 0);
574 stop = MIN(i + (range / 2), samples - 1);
575 diff = (stop - start);
577 start_sampledata = sampledata;
578 stop_sampledata = sampledata;
580 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
581 start_sampledata = start_sampledata->link_next;
582 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
583 stop_sampledata = stop_sampledata->link_prev;
585 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
593 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
604 prev_sampledata = head;
605 LIST_FOREACH_BEFORE(link, sampledata, head) {
612 start = MAX(i - ((range / 2) - 1), 0);
613 stop = MIN(i + (range / 2), samples);
614 diff = (stop - start);
616 start_sampledata = sampledata;
617 stop_sampledata = sampledata;
619 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
620 start_sampledata = start_sampledata->link_next;
621 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
622 stop_sampledata = stop_sampledata->link_prev;
624 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
631 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
632 time_to_graph(prev_sampledata->sampletime - graph_start),
633 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
634 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
635 pbi * (arg_scale_y * 5));
637 /* labels around highest value */
639 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
640 time_to_graph(sampledata->sampletime - graph_start) + 5,
641 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
642 max / 1024.0 / (interval / 1000000000.0));
645 prev_sampledata = sampledata;
649 static void svg_io_bo_bar(void) {
655 struct list_sample_data *start_sampledata;
656 struct list_sample_data *stop_sampledata;
658 svg("<!-- IO utilization graph - out -->\n");
660 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
663 * calculate rounding range
665 * We need to round IO data since IO block data is not updated on
666 * each poll. Applying a smoothing function loses some burst data,
667 * so keep the smoothing range short.
669 range = 0.25 / (1.0 / arg_hz);
671 range = 2.0; /* no smoothing */
673 /* surrounding box */
676 /* find the max IO first */
678 LIST_FOREACH_BEFORE(link, sampledata, head) {
684 start = MAX(i - ((range / 2) - 1), 0);
685 stop = MIN(i + (range / 2), samples - 1);
686 diff = (stop - start);
688 start_sampledata = sampledata;
689 stop_sampledata = sampledata;
691 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
692 start_sampledata = start_sampledata->link_next;
693 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
694 stop_sampledata = stop_sampledata->link_prev;
696 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
700 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
710 prev_sampledata = head;
712 LIST_FOREACH_BEFORE(link, sampledata, head) {
721 start = MAX(i - ((range / 2) - 1), 0);
722 stop = MIN(i + (range / 2), samples);
723 diff = (stop - start);
725 start_sampledata = sampledata;
726 stop_sampledata = sampledata;
728 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
729 start_sampledata = start_sampledata->link_next;
730 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
731 stop_sampledata = stop_sampledata->link_prev;
733 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
740 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
741 time_to_graph(prev_sampledata->sampletime - graph_start),
742 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
743 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
744 pbo * (arg_scale_y * 5));
746 /* labels around highest bo value */
748 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
749 time_to_graph(sampledata->sampletime - graph_start) + 5,
750 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
751 max / 1024.0 / (interval / 1000000000.0));
754 prev_sampledata = sampledata;
758 static void svg_cpu_bar(int cpu_num) {
760 svg("<!-- CPU utilization graph -->\n");
763 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
765 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
766 /* surrounding box */
769 /* bars for each sample, proportional to the CPU util. */
770 prev_sampledata = head;
771 LIST_FOREACH_BEFORE(link, sampledata, head) {
779 for (c = 0; c < cpus; c++)
780 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
782 trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num];
784 trt = trt / 1000000000.0;
787 trt = trt / (double)cpus;
790 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
796 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
797 time_to_graph(prev_sampledata->sampletime - graph_start),
798 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
799 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
800 ptrt * (arg_scale_y * 5));
802 prev_sampledata = sampledata;
806 static void svg_wait_bar(int cpu_num) {
808 svg("<!-- Wait time aggregation box -->\n");
811 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
813 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
815 /* surrounding box */
818 /* bars for each sample, proportional to the CPU util. */
819 prev_sampledata = head;
820 LIST_FOREACH_BEFORE(link, sampledata, head) {
828 for (c = 0; c < cpus; c++)
829 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
831 twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num];
833 twt = twt / 1000000000.0;
836 twt = twt / (double)cpus;
839 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
845 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
846 time_to_graph(prev_sampledata->sampletime - graph_start),
847 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
848 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
849 ptwt * (arg_scale_y * 5));
851 prev_sampledata = sampledata;
855 static void svg_entropy_bar(void) {
857 svg("<!-- entropy pool graph -->\n");
859 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
860 /* surrounding box */
863 /* bars for each sample, scale 0-4096 */
864 prev_sampledata = head;
865 LIST_FOREACH_BEFORE(link, sampledata, head) {
866 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
867 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
868 time_to_graph(prev_sampledata->sampletime - graph_start),
869 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
870 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
871 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
872 prev_sampledata = sampledata;
876 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
878 * walk the list of processes and return the next one to be
892 /* go back for parent siblings */
895 if (ps->parent->next)
896 return ps->parent->next;
905 static bool ps_filter(struct ps_struct *ps) {
909 /* can't draw data when there is only 1 sample (need start + stop) */
910 if (ps->first == ps->last)
913 /* don't filter kthreadd */
917 /* drop stuff that doesn't use any real CPU time */
918 if (ps->total <= 0.001)
924 static void svg_do_initcall(int count_only) {
925 _cleanup_pclose_ FILE *f = NULL;
931 /* can't plot initcall when disabled or in relative mode */
932 if (!initcall || arg_relative) {
938 svg("<!-- initcall -->\n");
940 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
941 /* surrounding box */
942 svg_graph_box(kcount);
948 * Initcall graphing - parses dmesg buffer and displays kernel threads
949 * This somewhat uses the same methods and scaling to show processes
950 * but looks a lot simpler. It's overlaid entirely onto the PS graph
954 f = popen("dmesg", "r");
963 if (fgets(l, sizeof(l) - 1, f) == NULL)
966 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
967 &t, func, &ret, &usecs);
969 /* also parse initcalls done by module loading */
970 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
971 &t, func, &ret, &usecs);
976 /* chop the +0xXX/0xXX stuff */
977 while(func[z] != '+')
982 /* filter out irrelevant stuff */
988 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
989 func, t, usecs, ret);
995 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
996 time_to_graph(t - (usecs / 1000000.0)),
998 time_to_graph(usecs / 1000000.0),
1002 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
1003 time_to_graph(t - (usecs / 1000000.0)) + 5,
1004 ps_to_graph(kcount) + 15,
1012 static void svg_ps_bars(void) {
1013 struct ps_struct *ps;
1019 svg("<!-- Process graph -->\n");
1021 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1023 /* surrounding box */
1024 svg_graph_box(pcount);
1026 /* pass 2 - ps boxes */
1028 while ((ps = get_next_ps(ps))) {
1029 _cleanup_free_ char *enc_name = NULL, *escaped = NULL;
1034 if (!utf8_is_printable(ps->name, strlen(ps->name)))
1035 escaped = utf8_escape_non_printable(ps->name);
1037 enc_name = xml_comment_encode(escaped ? escaped : ps->name);
1041 /* leave some trace of what we actually filtered etc. */
1042 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1043 ps->ppid, ps->total);
1045 starttime = ps->first->sampledata->sampletime;
1047 if (!ps_filter(ps)) {
1048 /* remember where _to_ our children need to draw a line */
1049 ps->pos_x = time_to_graph(starttime - graph_start);
1050 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1051 } else if (ps->parent){
1052 /* hook children to our parent coords instead */
1053 ps->pos_x = ps->parent->pos_x;
1054 ps->pos_y = ps->parent->pos_y;
1056 /* if this is the last child, we might still need to draw a connecting line */
1057 if ((!ps->next) && (ps->parent))
1058 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1060 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1066 endtime = ps->last->sampledata->sampletime;
1067 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1068 time_to_graph(starttime - graph_start),
1070 time_to_graph(ps->last->sampledata->sampletime - starttime),
1073 /* paint cpu load over these */
1074 ps->sample = ps->first;
1076 while (ps->sample->next) {
1079 struct ps_sched_struct *prev;
1082 ps->sample = ps->sample->next;
1084 /* calculate over interval */
1085 rt = ps->sample->runtime - prev->runtime;
1086 wt = ps->sample->waittime - prev->waittime;
1088 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1089 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1091 /* this can happen if timekeeping isn't accurate enough */
1097 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1100 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1101 time_to_graph(prev->sampledata->sampletime - graph_start),
1103 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1106 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1107 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1108 time_to_graph(prev->sampledata->sampletime - graph_start),
1109 ps_to_graph(j + (1.0 - prt)),
1110 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1115 /* determine where to display the process name */
1116 if ((endtime - starttime) < 1.5)
1117 /* too small to fit label inside the box */
1122 /* text label of process name */
1123 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1124 time_to_graph(w - graph_start) + 5.0,
1125 ps_to_graph(j) + 14.0,
1126 escaped ? escaped : ps->name,
1128 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1129 arg_show_cgroup ? ps->cgroup : "");
1130 /* paint lines to the parent process */
1132 /* horizontal part */
1133 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1134 time_to_graph(starttime - graph_start),
1135 ps_to_graph(j) + 10.0,
1137 ps_to_graph(j) + 10.0);
1139 /* one vertical line connecting all the horizontal ones up */
1141 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1143 ps_to_graph(j) + 10.0,
1148 j++; /* count boxes */
1153 /* last pass - determine when idle */
1155 /* make sure we start counting from the point where we actually have
1156 * data: assume that bootchart's first sample is when data started
1160 while (ps->next_ps) {
1166 /* need to know last node first */
1167 ps->sample = ps->first;
1168 i = ps->sample->next->sampledata->counter;
1170 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1175 struct ps_sched_struct *sample_hz;
1177 ps->sample = ps->sample->next;
1178 sample_hz = ps->sample;
1179 for (ii=0;((ii<(int)arg_hz/2)&&(sample_hz->next));ii++)
1180 sample_hz = sample_hz->next;
1182 /* subtract bootchart cpu utilization from total */
1184 for (c = 0; c < cpus; c++)
1185 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1186 brt = sample_hz->runtime - ps->sample->runtime;
1188 * our definition of "idle":
1190 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1191 * defaults to 4.0%, which experimentally, is where atom idles
1193 if ((crt - brt) < (interval / 2.0)) {
1194 idletime = ps->sample->sampledata->sampletime - graph_start;
1195 svg("\n<!-- idle detected at %.03f seconds -->\n",
1197 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1198 time_to_graph(idletime),
1200 time_to_graph(idletime),
1201 ps_to_graph(pcount) + arg_scale_y);
1202 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1203 time_to_graph(idletime) + 5.0,
1204 ps_to_graph(pcount) + arg_scale_y,
1212 static void svg_top_ten_cpu(void) {
1213 struct ps_struct *top[10];
1214 struct ps_struct emptyps = {};
1215 struct ps_struct *ps;
1218 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1221 /* walk all ps's and setup ptrs */
1223 while ((ps = get_next_ps(ps))) {
1224 for (n = 0; n < 10; n++) {
1225 if (ps->total <= top[n]->total)
1227 /* cascade insert */
1228 for (m = 9; m > n; m--)
1235 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1236 for (n = 0; n < 10; n++)
1237 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1244 static void svg_top_ten_pss(void) {
1245 struct ps_struct *top[10];
1246 struct ps_struct emptyps = {};
1247 struct ps_struct *ps;
1250 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1253 /* walk all ps's and setup ptrs */
1255 while ((ps = get_next_ps(ps))) {
1256 for (n = 0; n < 10; n++) {
1257 if (ps->pss_max <= top[n]->pss_max)
1259 /* cascade insert */
1260 for (m = 9; m > n; m--)
1267 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1268 for (n = 0; n < 10; n++)
1269 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1276 void svg_do(const char *build) {
1277 struct ps_struct *ps;
1281 memzero(&str, sizeof(str));
1285 /* count initcall thread count first */
1287 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1289 /* then count processes */
1290 while ((ps = get_next_ps(ps))) {
1296 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1298 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1300 /* after this, we can draw the header with proper sizing */
1302 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1304 svg("<g transform=\"translate(10,400)\">\n");
1308 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1312 for (c = -1; c < (arg_percpu ? cpus : 0); c++) {
1314 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1319 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1326 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1332 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize);
1336 svg("<g transform=\"translate(10, 0)\">\n");
1340 svg("<g transform=\"translate(10,200)\">\n");
1345 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize);
1351 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize);
1355 svg("<g transform=\"translate(410,200)\">\n");