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>
38 #include "bootchart.h"
42 #define time_to_graph(t) ((t) * arg_scale_x)
43 #define ps_to_graph(n) ((n) * arg_scale_y)
44 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
45 #define to_color(n) (192.0 - ((n) * 192.0))
47 static char str[8092];
49 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
51 static const char * const colorwheel[12] = {
52 "rgb(255,32,32)", // red
53 "rgb(32,192,192)", // cyan
54 "rgb(255,128,32)", // orange
55 "rgb(128,32,192)", // blue-violet
56 "rgb(255,255,32)", // yellow
57 "rgb(192,32,128)", // red-violet
58 "rgb(32,255,32)", // green
59 "rgb(255,64,32)", // red-orange
60 "rgb(32,32,255)", // blue
61 "rgb(255,192,32)", // yellow-orange
62 "rgb(192,32,192)", // violet
63 "rgb(32,192,32)" // yellow-green
66 static double idletime = -1.0;
67 static int pfiltered = 0;
68 static int pcount = 0;
69 static int kcount = 0;
70 static double psize = 0;
71 static double ksize = 0;
72 static double esize = 0;
73 static struct list_sample_data *sampledata;
74 static struct list_sample_data *prev_sampledata;
75 extern struct list_sample_data *head;
77 static void svg_header(void) {
80 struct list_sample_data *sampledata_last;
85 LIST_FIND_TAIL(link, sampledata, head);
86 sampledata_last = head;
87 LIST_FOREACH_BEFORE(link, sampledata, head) {
88 sampledata_last = sampledata;
91 /* min width is about 1600px due to the label */
92 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
93 w = ((w < 1600.0) ? 1600.0 : w);
95 /* height is variable based on pss, psize, ksize */
96 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
97 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
98 + psize + ksize + esize;
100 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
101 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
102 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
104 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
105 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
107 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
109 /* write some basic info as a comment, including some help */
110 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
111 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
112 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
113 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
114 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
116 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
117 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
118 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
119 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
120 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
121 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
124 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
126 svg(" rect { stroke-width: 1; }\n");
127 svg(" rect.bg { fill: rgb(255,255,255); }\n");
128 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
129 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
130 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
132 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
133 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
134 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
135 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
136 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
137 svg("// line.sec1 { }\n");
138 svg(" line.sec5 { stroke-width: 2; }\n");
139 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
140 svg(" line.dot { stroke-dasharray: 2 4; }\n");
141 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
143 svg(" .run { font-size: 8; font-style: italic; }\n");
144 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
145 svg(" text.sec { font-size: 8; }\n");
146 svg(" text.t1 { font-size: 24; }\n");
147 svg(" text.t2 { font-size: 12; }\n");
148 svg(" text.idle { font-size: 18; }\n");
150 svg(" ]]>\n </style>\n</defs>\n\n");
153 static int svg_title(const char *build) {
154 _cleanup_free_ char *cmdline = NULL;
155 _cleanup_free_ char *model = NULL;
156 _cleanup_free_ char *buf = NULL;
157 char date[256] = "Unknown";
164 r = read_one_line_file("/proc/cmdline", &cmdline);
166 log_error_errno(r, "Unable to read cmdline: %m\n");
170 /* extract root fs so we can find disk model name in sysfs */
171 /* FIXME: this works only in the simple case */
172 c = strstr(cmdline, "root=/dev/");
177 strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
179 snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
181 r = read_one_line_file(filename, &model);
183 log_error("Error reading disk model for %s: %m\n", rootbdev);
188 /* various utsname parameters */
191 log_error("Error getting uname info\n");
197 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
201 r = read_full_file("/proc/cpuinfo", &buf, NULL);
203 log_error_errno(r, "Unable to read cpuinfo: %m\n");
207 cpu = strstr(buf, "model name");
209 log_error("Unable to read module name from cpuinfo.\n");
214 c = strchr(cpu, '\n');
218 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
220 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
221 uts.sysname, uts.release, uts.version, uts.machine);
222 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
226 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
228 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
230 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
231 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
234 svg("%.03fs", idletime);
238 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",
239 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
244 static void svg_graph_box(int height) {
247 double finalsample = 0.0;
248 struct list_sample_data *sampledata_last;
250 sampledata_last = head;
251 LIST_FOREACH_BEFORE(link, sampledata, head) {
252 sampledata_last = sampledata;
255 finalsample = sampledata_last->sampletime;
257 /* outside box, fill */
258 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
260 time_to_graph(finalsample - graph_start),
261 ps_to_graph(height));
263 for (d = graph_start; d <= finalsample;
264 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
265 /* lines for each second */
267 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
268 time_to_graph(d - graph_start),
269 time_to_graph(d - graph_start),
270 ps_to_graph(height));
271 else if (i % 10 == 0)
272 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
273 time_to_graph(d - graph_start),
274 time_to_graph(d - graph_start),
275 ps_to_graph(height));
277 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
278 time_to_graph(d - graph_start),
279 time_to_graph(d - graph_start),
280 ps_to_graph(height));
284 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
285 time_to_graph(d - graph_start),
293 /* xml comments must not contain "--" */
294 static char* xml_comment_encode(const char* name) {
297 enc_name = strdup(name);
301 for (p = enc_name; *p; p++)
302 if (p[0] == '-' && p[1] == '-')
308 static void svg_pss_graph(void) {
309 struct ps_struct *ps;
311 struct list_sample_data *sampledata_last;
313 sampledata_last = head;
314 LIST_FOREACH_BEFORE(link, sampledata, head) {
315 sampledata_last = sampledata;
319 svg("\n\n<!-- Pss memory size graph -->\n");
321 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
323 /* vsize 1000 == 1000mb */
325 /* draw some hlines for usable memory sizes */
326 for (i = 100000; i < 1000000; i += 100000) {
327 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
330 time_to_graph(sampledata_last->sampletime - graph_start),
332 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
333 time_to_graph(sampledata_last->sampletime - graph_start) + 5,
334 kb_to_graph(i), (1000000 - i) / 1000);
338 /* now plot the graph itself */
340 prev_sampledata = head;
341 LIST_FOREACH_BEFORE(link, sampledata, head) {
344 struct ps_sched_struct *cross_place;
349 /* put all the small pss blocks into the bottom */
351 while (ps->next_ps) {
355 ps->sample = ps->first;
356 while (ps->sample->next) {
357 ps->sample = ps->sample->next;
358 if (ps->sample->sampledata == sampledata)
361 if (ps->sample->sampledata == sampledata) {
362 if (ps->sample->pss <= (100 * arg_scale_y))
363 top += ps->sample->pss;
367 while (ps->sample->cross) {
368 cross_place = ps->sample->cross;
369 ps = ps->sample->cross->ps_new;
370 ps->sample = cross_place;
371 if (ps->sample->pss <= (100 * arg_scale_y))
372 top += ps->sample->pss;
375 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
377 time_to_graph(prev_sampledata->sampletime - graph_start),
378 kb_to_graph(1000000.0 - top),
379 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
380 kb_to_graph(top - bottom));
383 /* now plot the ones that are of significant size */
385 while (ps->next_ps) {
389 ps->sample = ps->first;
390 while (ps->sample->next) {
391 ps->sample = ps->sample->next;
392 if (ps->sample->sampledata == sampledata)
395 /* don't draw anything smaller than 2mb */
396 if (ps->sample->sampledata == sampledata) {
397 if (ps->sample->pss > (100 * arg_scale_y)) {
398 top = bottom + ps->sample->pss;
399 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
400 colorwheel[ps->pid % 12],
401 time_to_graph(prev_sampledata->sampletime - graph_start),
402 kb_to_graph(1000000.0 - top),
403 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
404 kb_to_graph(top - bottom));
410 while ((cross_place = ps->sample->cross)) {
411 ps = ps->sample->cross->ps_new;
412 ps->sample = cross_place;
413 if (ps->sample->pss > (100 * arg_scale_y)) {
414 top = bottom + ps->sample->pss;
415 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
416 colorwheel[ps->pid % 12],
417 time_to_graph(prev_sampledata->sampletime - graph_start),
418 kb_to_graph(1000000.0 - top),
419 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
420 kb_to_graph(top - bottom));
424 prev_sampledata = sampledata;
428 /* overlay all the text labels */
430 LIST_FOREACH_BEFORE(link, sampledata, head) {
433 struct ps_sched_struct *prev_sample;
434 struct ps_sched_struct *cross_place;
436 /* put all the small pss blocks into the bottom */
437 ps = ps_first->next_ps;
438 while (ps->next_ps) {
442 ps->sample = ps->first;
443 while (ps->sample->next) {
444 ps->sample = ps->sample->next;
445 if (ps->sample->sampledata == sampledata)
448 if (ps->sample->sampledata == sampledata) {
449 if (ps->sample->pss <= (100 * arg_scale_y))
450 top += ps->sample->pss;
454 while ((cross_place = ps->sample->cross)) {
455 ps = ps->sample->cross->ps_new;
456 ps->sample = cross_place;
457 if (ps->sample->pss <= (100 * arg_scale_y))
458 top += ps->sample->pss;
462 /* now plot the ones that are of significant size */
464 while (ps->next_ps) {
465 prev_sample = ps->sample;
469 ps->sample = ps->first;
470 while (ps->sample->next) {
471 prev_sample = ps->sample;
472 ps->sample = ps->sample->next;
473 if (ps->sample->sampledata == sampledata)
476 /* don't draw anything smaller than 2mb */
477 if (ps->sample->sampledata == sampledata) {
478 if (ps->sample->pss > (100 * arg_scale_y)) {
479 top = bottom + ps->sample->pss;
480 /* draw a label with the process / PID */
481 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
482 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
483 time_to_graph(sampledata->sampletime - graph_start),
484 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
492 while ((cross_place = ps->sample->cross)) {
493 ps = ps->sample->cross->ps_new;
494 ps->sample = cross_place;
495 prev_sample = ps->sample->prev;
496 if (ps->sample->pss > (100 * arg_scale_y)) {
497 top = bottom + ps->sample->pss;
498 /* draw a label with the process / PID */
499 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
500 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
501 time_to_graph(sampledata->sampletime - graph_start),
502 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
511 /* debug output - full data dump */
512 svg("\n\n<!-- PSS map - csv format -->\n");
514 while (ps->next_ps) {
515 _cleanup_free_ char *enc_name = NULL;
520 enc_name = xml_comment_encode(ps->name);
524 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
526 ps->sample = ps->first;
527 while (ps->sample->next) {
528 ps->sample = ps->sample->next;
529 svg("%d," , ps->sample->pss);
536 static void svg_io_bi_bar(void) {
542 struct list_sample_data *start_sampledata;
543 struct list_sample_data *stop_sampledata;
545 svg("<!-- IO utilization graph - In -->\n");
547 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
550 * calculate rounding range
552 * We need to round IO data since IO block data is not updated on
553 * each poll. Applying a smoothing function loses some burst data,
554 * so keep the smoothing range short.
556 range = 0.25 / (1.0 / arg_hz);
558 range = 2.0; /* no smoothing */
560 /* surrounding box */
563 /* find the max IO first */
565 LIST_FOREACH_BEFORE(link, sampledata, head) {
571 start = MAX(i - ((range / 2) - 1), 0);
572 stop = MIN(i + (range / 2), samples - 1);
573 diff = (stop - start);
575 start_sampledata = sampledata;
576 stop_sampledata = sampledata;
578 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
579 start_sampledata = start_sampledata->link_next;
580 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
581 stop_sampledata = stop_sampledata->link_prev;
583 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
591 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
602 prev_sampledata = head;
603 LIST_FOREACH_BEFORE(link, sampledata, head) {
610 start = MAX(i - ((range / 2) - 1), 0);
611 stop = MIN(i + (range / 2), samples);
612 diff = (stop - start);
614 start_sampledata = sampledata;
615 stop_sampledata = sampledata;
617 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
618 start_sampledata = start_sampledata->link_next;
619 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
620 stop_sampledata = stop_sampledata->link_prev;
622 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
629 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
630 time_to_graph(prev_sampledata->sampletime - graph_start),
631 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
632 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
633 pbi * (arg_scale_y * 5));
635 /* labels around highest value */
637 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
638 time_to_graph(sampledata->sampletime - graph_start) + 5,
639 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
640 max / 1024.0 / (interval / 1000000000.0));
643 prev_sampledata = sampledata;
647 static void svg_io_bo_bar(void) {
653 struct list_sample_data *start_sampledata;
654 struct list_sample_data *stop_sampledata;
656 svg("<!-- IO utilization graph - out -->\n");
658 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
661 * calculate rounding range
663 * We need to round IO data since IO block data is not updated on
664 * each poll. Applying a smoothing function loses some burst data,
665 * so keep the smoothing range short.
667 range = 0.25 / (1.0 / arg_hz);
669 range = 2.0; /* no smoothing */
671 /* surrounding box */
674 /* find the max IO first */
676 LIST_FOREACH_BEFORE(link, sampledata, head) {
682 start = MAX(i - ((range / 2) - 1), 0);
683 stop = MIN(i + (range / 2), samples - 1);
684 diff = (stop - start);
686 start_sampledata = sampledata;
687 stop_sampledata = sampledata;
689 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
690 start_sampledata = start_sampledata->link_next;
691 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
692 stop_sampledata = stop_sampledata->link_prev;
694 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
698 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
708 prev_sampledata = head;
710 LIST_FOREACH_BEFORE(link, sampledata, head) {
719 start = MAX(i - ((range / 2) - 1), 0);
720 stop = MIN(i + (range / 2), samples);
721 diff = (stop - start);
723 start_sampledata = sampledata;
724 stop_sampledata = sampledata;
726 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
727 start_sampledata = start_sampledata->link_next;
728 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
729 stop_sampledata = stop_sampledata->link_prev;
731 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
738 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
739 time_to_graph(prev_sampledata->sampletime - graph_start),
740 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
741 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
742 pbo * (arg_scale_y * 5));
744 /* labels around highest bo value */
746 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
747 time_to_graph(sampledata->sampletime - graph_start) + 5,
748 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
749 max / 1024.0 / (interval / 1000000000.0));
752 prev_sampledata = sampledata;
756 static void svg_cpu_bar(int cpu_num) {
758 svg("<!-- CPU utilization graph -->\n");
761 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
763 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
764 /* surrounding box */
767 /* bars for each sample, proportional to the CPU util. */
768 prev_sampledata = head;
769 LIST_FOREACH_BEFORE(link, sampledata, head) {
777 for (c = 0; c < cpus; c++)
778 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
780 trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num];
782 trt = trt / 1000000000.0;
785 trt = trt / (double)cpus;
788 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
794 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
795 time_to_graph(prev_sampledata->sampletime - graph_start),
796 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
797 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
798 ptrt * (arg_scale_y * 5));
800 prev_sampledata = sampledata;
804 static void svg_wait_bar(int cpu_num) {
806 svg("<!-- Wait time aggregation box -->\n");
809 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
811 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
813 /* surrounding box */
816 /* bars for each sample, proportional to the CPU util. */
817 prev_sampledata = head;
818 LIST_FOREACH_BEFORE(link, sampledata, head) {
826 for (c = 0; c < cpus; c++)
827 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
829 twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num];
831 twt = twt / 1000000000.0;
834 twt = twt / (double)cpus;
837 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
843 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
844 time_to_graph(prev_sampledata->sampletime - graph_start),
845 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
846 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
847 ptwt * (arg_scale_y * 5));
849 prev_sampledata = sampledata;
853 static void svg_entropy_bar(void) {
855 svg("<!-- entropy pool graph -->\n");
857 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
858 /* surrounding box */
861 /* bars for each sample, scale 0-4096 */
862 prev_sampledata = head;
863 LIST_FOREACH_BEFORE(link, sampledata, head) {
864 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
865 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
866 time_to_graph(prev_sampledata->sampletime - graph_start),
867 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
868 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
869 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
870 prev_sampledata = sampledata;
874 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
876 * walk the list of processes and return the next one to be
890 /* go back for parent siblings */
893 if (ps->parent->next)
894 return ps->parent->next;
903 static bool ps_filter(struct ps_struct *ps) {
907 /* can't draw data when there is only 1 sample (need start + stop) */
908 if (ps->first == ps->last)
911 /* don't filter kthreadd */
915 /* drop stuff that doesn't use any real CPU time */
916 if (ps->total <= 0.001)
922 static void svg_do_initcall(int count_only) {
923 _cleanup_pclose_ FILE *f = NULL;
929 /* can't plot initcall when disabled or in relative mode */
930 if (!initcall || arg_relative) {
936 svg("<!-- initcall -->\n");
938 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
939 /* surrounding box */
940 svg_graph_box(kcount);
946 * Initcall graphing - parses dmesg buffer and displays kernel threads
947 * This somewhat uses the same methods and scaling to show processes
948 * but looks a lot simpler. It's overlaid entirely onto the PS graph
952 f = popen("dmesg", "r");
961 if (fgets(l, sizeof(l) - 1, f) == NULL)
964 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
965 &t, func, &ret, &usecs);
967 /* also parse initcalls done by module loading */
968 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
969 &t, func, &ret, &usecs);
974 /* chop the +0xXX/0xXX stuff */
975 while(func[z] != '+')
980 /* filter out irrelevant stuff */
986 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
987 func, t, usecs, ret);
993 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
994 time_to_graph(t - (usecs / 1000000.0)),
996 time_to_graph(usecs / 1000000.0),
1000 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
1001 time_to_graph(t - (usecs / 1000000.0)) + 5,
1002 ps_to_graph(kcount) + 15,
1010 static void svg_ps_bars(void) {
1011 struct ps_struct *ps;
1017 svg("<!-- Process graph -->\n");
1019 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1021 /* surrounding box */
1022 svg_graph_box(pcount);
1024 /* pass 2 - ps boxes */
1026 while ((ps = get_next_ps(ps))) {
1027 _cleanup_free_ char *enc_name = NULL, *escaped = NULL;
1032 if (!utf8_is_printable(ps->name, strlen(ps->name)))
1033 escaped = utf8_escape_non_printable(ps->name);
1035 enc_name = xml_comment_encode(escaped ? escaped : ps->name);
1039 /* leave some trace of what we actually filtered etc. */
1040 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1041 ps->ppid, ps->total);
1043 starttime = ps->first->sampledata->sampletime;
1045 if (!ps_filter(ps)) {
1046 /* remember where _to_ our children need to draw a line */
1047 ps->pos_x = time_to_graph(starttime - graph_start);
1048 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1049 } else if (ps->parent){
1050 /* hook children to our parent coords instead */
1051 ps->pos_x = ps->parent->pos_x;
1052 ps->pos_y = ps->parent->pos_y;
1054 /* if this is the last child, we might still need to draw a connecting line */
1055 if ((!ps->next) && (ps->parent))
1056 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1058 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1064 endtime = ps->last->sampledata->sampletime;
1065 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1066 time_to_graph(starttime - graph_start),
1068 time_to_graph(ps->last->sampledata->sampletime - starttime),
1071 /* paint cpu load over these */
1072 ps->sample = ps->first;
1074 while (ps->sample->next) {
1077 struct ps_sched_struct *prev;
1080 ps->sample = ps->sample->next;
1082 /* calculate over interval */
1083 rt = ps->sample->runtime - prev->runtime;
1084 wt = ps->sample->waittime - prev->waittime;
1086 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1087 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1089 /* this can happen if timekeeping isn't accurate enough */
1095 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1098 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1099 time_to_graph(prev->sampledata->sampletime - graph_start),
1101 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1104 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1105 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1106 time_to_graph(prev->sampledata->sampletime - graph_start),
1107 ps_to_graph(j + (1.0 - prt)),
1108 time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1113 /* determine where to display the process name */
1114 if ((endtime - starttime) < 1.5)
1115 /* too small to fit label inside the box */
1120 /* text label of process name */
1121 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1122 time_to_graph(w - graph_start) + 5.0,
1123 ps_to_graph(j) + 14.0,
1124 escaped ? escaped : ps->name,
1126 (ps->last->runtime - ps->first->runtime) / 1000000000.0,
1127 arg_show_cgroup ? ps->cgroup : "");
1128 /* paint lines to the parent process */
1130 /* horizontal part */
1131 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1132 time_to_graph(starttime - graph_start),
1133 ps_to_graph(j) + 10.0,
1135 ps_to_graph(j) + 10.0);
1137 /* one vertical line connecting all the horizontal ones up */
1139 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1141 ps_to_graph(j) + 10.0,
1146 j++; /* count boxes */
1151 /* last pass - determine when idle */
1153 /* make sure we start counting from the point where we actually have
1154 * data: assume that bootchart's first sample is when data started
1158 while (ps->next_ps) {
1164 /* need to know last node first */
1165 ps->sample = ps->first;
1166 i = ps->sample->next->sampledata->counter;
1168 while (ps->sample->next && i<(samples-(arg_hz/2))) {
1173 struct ps_sched_struct *sample_hz;
1175 ps->sample = ps->sample->next;
1176 sample_hz = ps->sample;
1177 for (ii=0;((ii<(int)arg_hz/2)&&(sample_hz->next));ii++)
1178 sample_hz = sample_hz->next;
1180 /* subtract bootchart cpu utilization from total */
1182 for (c = 0; c < cpus; c++)
1183 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1184 brt = sample_hz->runtime - ps->sample->runtime;
1186 * our definition of "idle":
1188 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1189 * defaults to 4.0%, which experimentally, is where atom idles
1191 if ((crt - brt) < (interval / 2.0)) {
1192 idletime = ps->sample->sampledata->sampletime - graph_start;
1193 svg("\n<!-- idle detected at %.03f seconds -->\n",
1195 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1196 time_to_graph(idletime),
1198 time_to_graph(idletime),
1199 ps_to_graph(pcount) + arg_scale_y);
1200 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1201 time_to_graph(idletime) + 5.0,
1202 ps_to_graph(pcount) + arg_scale_y,
1210 static void svg_top_ten_cpu(void) {
1211 struct ps_struct *top[10];
1212 struct ps_struct emptyps = {};
1213 struct ps_struct *ps;
1216 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1219 /* walk all ps's and setup ptrs */
1221 while ((ps = get_next_ps(ps))) {
1222 for (n = 0; n < 10; n++) {
1223 if (ps->total <= top[n]->total)
1225 /* cascade insert */
1226 for (m = 9; m > n; m--)
1233 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1234 for (n = 0; n < 10; n++)
1235 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1242 static void svg_top_ten_pss(void) {
1243 struct ps_struct *top[10];
1244 struct ps_struct emptyps = {};
1245 struct ps_struct *ps;
1248 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1251 /* walk all ps's and setup ptrs */
1253 while ((ps = get_next_ps(ps))) {
1254 for (n = 0; n < 10; n++) {
1255 if (ps->pss_max <= top[n]->pss_max)
1257 /* cascade insert */
1258 for (m = 9; m > n; m--)
1265 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1266 for (n = 0; n < 10; n++)
1267 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1274 int svg_do(const char *build) {
1275 struct ps_struct *ps;
1279 memzero(&str, sizeof(str));
1283 /* count initcall thread count first */
1285 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1287 /* then count processes */
1288 while ((ps = get_next_ps(ps))) {
1294 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1296 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1298 /* after this, we can draw the header with proper sizing */
1300 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1302 svg("<g transform=\"translate(10,400)\">\n");
1306 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1310 for (c = -1; c < (arg_percpu ? cpus : 0); c++) {
1312 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1317 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1324 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1330 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize);
1334 svg("<g transform=\"translate(10, 0)\">\n");
1335 r = svg_title(build);
1341 svg("<g transform=\"translate(10,200)\">\n");
1346 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize);
1352 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize);
1356 svg("<g transform=\"translate(410,200)\">\n");