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 const char * const colorwheel[12] = {
48 "rgb(255,32,32)", // red
49 "rgb(32,192,192)", // cyan
50 "rgb(255,128,32)", // orange
51 "rgb(128,32,192)", // blue-violet
52 "rgb(255,255,32)", // yellow
53 "rgb(192,32,128)", // red-violet
54 "rgb(32,255,32)", // green
55 "rgb(255,64,32)", // red-orange
56 "rgb(32,32,255)", // blue
57 "rgb(255,192,32)", // yellow-orange
58 "rgb(192,32,192)", // violet
59 "rgb(32,192,32)" // yellow-green
62 static double idletime = -1.0;
63 static int pfiltered = 0;
64 static int pcount = 0;
65 static int kcount = 0;
66 static double psize = 0;
67 static double ksize = 0;
68 static double esize = 0;
69 static struct list_sample_data *sampledata;
70 static struct list_sample_data *prev_sampledata;
72 static void svg_header(FILE *of, struct list_sample_data *head, double graph_start) {
75 struct list_sample_data *sampledata_last;
80 LIST_FIND_TAIL(link, sampledata, head);
81 sampledata_last = head;
82 LIST_FOREACH_BEFORE(link, sampledata, head) {
83 sampledata_last = sampledata;
86 /* min width is about 1600px due to the label */
87 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
88 w = ((w < 1600.0) ? 1600.0 : w);
90 /* height is variable based on pss, psize, ksize */
91 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
92 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
93 + psize + ksize + esize;
95 fprintf(of, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
96 fprintf(of, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
97 fprintf(of, "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
99 //fprintf(of, "<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
100 fprintf(of, "<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ", w, h);
101 fprintf(of, "xmlns=\"http://www.w3.org/2000/svg\">\n\n");
103 /* write some basic info as a comment, including some help */
104 fprintf(of, "<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
105 fprintf(of, "<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
106 fprintf(of, "<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
107 fprintf(of, "<!-- inkscape, etc. To display the files on your system, just point -->\n");
108 fprintf(of, "<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
110 fprintf(of, "<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
111 fprintf(of, "<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
112 fprintf(of, "<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
113 fprintf(of, "<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
114 fprintf(of, "<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
115 fprintf(of, "<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
118 fprintf(of, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
120 fprintf(of, " rect { stroke-width: 1; }\n");
121 fprintf(of, " rect.bg { fill: rgb(255,255,255); }\n");
122 fprintf(of, " rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
123 fprintf(of, " rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
124 fprintf(of, " rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
125 fprintf(of, " rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
126 fprintf(of, " rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
127 fprintf(of, " rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
128 fprintf(of, " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
129 fprintf(of, " rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
130 fprintf(of, " line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
131 fprintf(of, "// line.sec1 { }\n");
132 fprintf(of, " line.sec5 { stroke-width: 2; }\n");
133 fprintf(of, " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
134 fprintf(of, " line.dot { stroke-dasharray: 2 4; }\n");
135 fprintf(of, " line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
137 fprintf(of, " .run { font-size: 8; font-style: italic; }\n");
138 fprintf(of, " text { font-family: Verdana, Helvetica; font-size: 10; }\n");
139 fprintf(of, " text.sec { font-size: 8; }\n");
140 fprintf(of, " text.t1 { font-size: 24; }\n");
141 fprintf(of, " text.t2 { font-size: 12; }\n");
142 fprintf(of, " text.idle { font-size: 18; }\n");
144 fprintf(of, " ]]>\n </style>\n</defs>\n\n");
147 static int svg_title(FILE *of, const char *build, int pscount, double log_start, int overrun) {
148 _cleanup_free_ char *cmdline = NULL;
149 _cleanup_free_ char *model = NULL;
150 _cleanup_free_ char *buf = NULL;
151 char date[256] = "Unknown";
158 r = read_one_line_file("/proc/cmdline", &cmdline);
160 log_error_errno(r, "Unable to read cmdline: %m\n");
164 /* extract root fs so we can find disk model name in sysfs */
165 /* FIXME: this works only in the simple case */
166 c = strstr(cmdline, "root=/dev/");
171 strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
173 snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
175 r = read_one_line_file(filename, &model);
177 log_warning("Error reading disk model for %s: %m\n", rootbdev);
180 /* various utsname parameters */
183 log_error("Error getting uname info\n");
189 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
193 r = read_full_file("/proc/cpuinfo", &buf, NULL);
195 log_error_errno(r, "Unable to read cpuinfo: %m\n");
199 cpu = strstr(buf, "model name");
201 log_error("Unable to read module name from cpuinfo.\n");
206 c = strchr(cpu, '\n');
210 fprintf(of, "<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
212 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
213 uts.sysname, uts.release, uts.version, uts.machine);
214 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n", cpu);
215 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n", model);
216 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n", cmdline);
217 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n", build);
218 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
219 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
222 fprintf(of, "%.03fs", idletime);
224 fprintf(of, "Not detected");
226 fprintf(of, "</text>\n");
227 fprintf(of, "<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
228 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
233 static void svg_graph_box(FILE *of, struct list_sample_data *head, int height, double graph_start) {
236 double finalsample = 0.0;
237 struct list_sample_data *sampledata_last;
239 sampledata_last = head;
240 LIST_FOREACH_BEFORE(link, sampledata, head) {
241 sampledata_last = sampledata;
244 finalsample = sampledata_last->sampletime;
246 /* outside box, fill */
247 fprintf(of, "<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
249 time_to_graph(finalsample - graph_start),
250 ps_to_graph(height));
252 for (d = graph_start; d <= finalsample;
253 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
254 /* lines for each second */
256 fprintf(of, " <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
257 time_to_graph(d - graph_start),
258 time_to_graph(d - graph_start),
259 ps_to_graph(height));
260 else if (i % 10 == 0)
261 fprintf(of, " <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
262 time_to_graph(d - graph_start),
263 time_to_graph(d - graph_start),
264 ps_to_graph(height));
266 fprintf(of, " <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
267 time_to_graph(d - graph_start),
268 time_to_graph(d - graph_start),
269 ps_to_graph(height));
273 fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
274 time_to_graph(d - graph_start),
275 -5.0, d - graph_start);
281 /* xml comments must not contain "--" */
282 static char* xml_comment_encode(const char* name) {
285 enc_name = strdup(name);
289 for (p = enc_name; *p; p++)
290 if (p[0] == '-' && p[1] == '-')
296 static void svg_pss_graph(FILE *of,
297 struct list_sample_data *head,
298 struct ps_struct *ps_first,
299 double graph_start) {
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 fprintf(of, "\n\n<!-- Pss memory size graph -->\n");
312 fprintf(of, "\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
314 /* vsize 1000 == 1000mb */
315 svg_graph_box(of, head, 100, graph_start);
316 /* draw some hlines for usable memory sizes */
317 for (i = 100000; i < 1000000; i += 100000) {
318 fprintf(of, " <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
321 time_to_graph(sampledata_last->sampletime - graph_start),
323 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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)),
501 /* debug output - full data dump */
502 fprintf(of, "\n\n<!-- PSS map - csv format -->\n");
504 while (ps->next_ps) {
505 _cleanup_free_ char *enc_name = NULL;
510 enc_name = xml_comment_encode(ps->name);
514 fprintf(of, "<!-- %s [%d] pss=", enc_name, ps->pid);
516 ps->sample = ps->first;
517 while (ps->sample->next) {
518 ps->sample = ps->sample->next;
519 fprintf(of, "%d," , ps->sample->pss);
521 fprintf(of, " -->\n");
526 static void svg_io_bi_bar(FILE *of,
527 struct list_sample_data *head,
537 struct list_sample_data *start_sampledata;
538 struct list_sample_data *stop_sampledata;
540 fprintf(of, "<!-- IO utilization graph - In -->\n");
541 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
544 * calculate rounding range
546 * We need to round IO data since IO block data is not updated on
547 * each poll. Applying a smoothing function loses some burst data,
548 * so keep the smoothing range short.
550 range = 0.25 / (1.0 / arg_hz);
552 range = 2.0; /* no smoothing */
554 /* surrounding box */
555 svg_graph_box(of, head, 5, graph_start);
557 /* find the max IO first */
559 LIST_FOREACH_BEFORE(link, sampledata, head) {
565 start = MAX(i - ((range / 2) - 1), 0);
566 stop = MIN(i + (range / 2), n_samples - 1);
567 diff = (stop - start);
569 start_sampledata = sampledata;
570 stop_sampledata = sampledata;
572 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
573 start_sampledata = start_sampledata->link_next;
574 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
575 stop_sampledata = stop_sampledata->link_prev;
577 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
585 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
596 prev_sampledata = head;
597 LIST_FOREACH_BEFORE(link, sampledata, head) {
604 start = MAX(i - ((range / 2) - 1), 0);
605 stop = MIN(i + (range / 2), n_samples);
606 diff = (stop - start);
608 start_sampledata = sampledata;
609 stop_sampledata = sampledata;
611 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
612 start_sampledata = start_sampledata->link_next;
613 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
614 stop_sampledata = stop_sampledata->link_prev;
616 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
623 fprintf(of, "<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
624 time_to_graph(prev_sampledata->sampletime - graph_start),
625 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
626 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
627 pbi * (arg_scale_y * 5));
629 /* labels around highest value */
631 fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
632 time_to_graph(sampledata->sampletime - graph_start) + 5,
633 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
634 max / 1024.0 / (interval / 1000000000.0));
637 prev_sampledata = sampledata;
641 static void svg_io_bo_bar(FILE *of,
642 struct list_sample_data *head,
651 struct list_sample_data *start_sampledata;
652 struct list_sample_data *stop_sampledata;
654 fprintf(of, "<!-- IO utilization graph - out -->\n");
655 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
658 * calculate rounding range
660 * We need to round IO data since IO block data is not updated on
661 * each poll. Applying a smoothing function loses some burst data,
662 * so keep the smoothing range short.
664 range = 0.25 / (1.0 / arg_hz);
666 range = 2.0; /* no smoothing */
668 /* surrounding box */
669 svg_graph_box(of, head, 5, graph_start);
671 /* find the max IO first */
673 LIST_FOREACH_BEFORE(link, sampledata, head) {
679 start = MAX(i - ((range / 2) - 1), 0);
680 stop = MIN(i + (range / 2), n_samples - 1);
681 diff = (stop - start);
683 start_sampledata = sampledata;
684 stop_sampledata = sampledata;
686 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
687 start_sampledata = start_sampledata->link_next;
688 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
689 stop_sampledata = stop_sampledata->link_prev;
691 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
695 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
705 prev_sampledata = head;
707 LIST_FOREACH_BEFORE(link, sampledata, head) {
716 start = MAX(i - ((range / 2) - 1), 0);
717 stop = MIN(i + (range / 2), n_samples);
718 diff = (stop - start);
720 start_sampledata = sampledata;
721 stop_sampledata = sampledata;
723 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
724 start_sampledata = start_sampledata->link_next;
725 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
726 stop_sampledata = stop_sampledata->link_prev;
728 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
735 fprintf(of, "<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
736 time_to_graph(prev_sampledata->sampletime - graph_start),
737 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
738 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
739 pbo * (arg_scale_y * 5));
741 /* labels around highest bo value */
743 fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
744 time_to_graph(sampledata->sampletime - graph_start) + 5,
745 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
746 max / 1024.0 / (interval / 1000000000.0));
749 prev_sampledata = sampledata;
753 static void svg_cpu_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
755 fprintf(of, "<!-- CPU utilization graph -->\n");
758 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
760 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
761 /* surrounding box */
762 svg_graph_box(of, head, 5, graph_start);
764 /* bars for each sample, proportional to the CPU util. */
765 prev_sampledata = head;
766 LIST_FOREACH_BEFORE(link, sampledata, head) {
774 for (c = 0; c < n_cpus; c++)
775 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
777 trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num];
779 trt = trt / 1000000000.0;
782 trt = trt / (double)n_cpus;
785 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
791 fprintf(of, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
792 time_to_graph(prev_sampledata->sampletime - graph_start),
793 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
794 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
795 ptrt * (arg_scale_y * 5));
797 prev_sampledata = sampledata;
801 static void svg_wait_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
803 fprintf(of, "<!-- Wait time aggregation box -->\n");
806 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
808 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
810 /* surrounding box */
811 svg_graph_box(of, head, 5, graph_start);
813 /* bars for each sample, proportional to the CPU util. */
814 prev_sampledata = head;
815 LIST_FOREACH_BEFORE(link, sampledata, head) {
823 for (c = 0; c < n_cpus; c++)
824 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
826 twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num];
828 twt = twt / 1000000000.0;
831 twt = twt / (double)n_cpus;
834 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
840 fprintf(of, "<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
841 time_to_graph(prev_sampledata->sampletime - graph_start),
842 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
843 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
844 ptwt * (arg_scale_y * 5));
846 prev_sampledata = sampledata;
850 static void svg_entropy_bar(FILE *of, struct list_sample_data *head, double graph_start) {
852 fprintf(of, "<!-- entropy pool graph -->\n");
854 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
855 /* surrounding box */
856 svg_graph_box(of, head, 5, graph_start);
858 /* bars for each sample, scale 0-4096 */
859 prev_sampledata = head;
860 LIST_FOREACH_BEFORE(link, sampledata, head) {
861 fprintf(of, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
862 time_to_graph(prev_sampledata->sampletime - graph_start),
863 ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
864 time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
865 (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
866 prev_sampledata = sampledata;
870 static struct ps_struct *get_next_ps(struct ps_struct *ps, struct ps_struct *ps_first) {
872 * walk the list of processes and return the next one to be
886 /* go back for parent siblings */
889 if (ps->parent->next)
890 return ps->parent->next;
899 static bool ps_filter(struct ps_struct *ps) {
903 /* can't draw data when there is only 1 sample (need start + stop) */
904 if (ps->first == ps->last)
907 /* don't filter kthreadd */
911 /* drop stuff that doesn't use any real CPU time */
912 if (ps->total <= 0.001)
918 static void svg_do_initcall(FILE *of, struct list_sample_data *head, int count_only, double graph_start) {
919 _cleanup_pclose_ FILE *f = NULL;
925 /* can't plot initcall when disabled or in relative mode */
926 if (!arg_initcall || arg_relative) {
932 fprintf(of, "<!-- initcall -->\n");
933 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
934 /* surrounding box */
935 svg_graph_box(of, head, kcount, graph_start);
941 * Initcall graphing - parses dmesg buffer and displays kernel threads
942 * This somewhat uses the same methods and scaling to show processes
943 * but looks a lot simpler. It's overlaid entirely onto the PS graph
947 f = popen("dmesg", "r");
956 if (fgets(l, sizeof(l) - 1, f) == NULL)
959 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
960 &t, func, &ret, &usecs);
962 /* also parse initcalls done by module loading */
963 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
964 &t, func, &ret, &usecs);
969 /* chop the +0xXX/0xXX stuff */
970 while(func[z] != '+')
975 /* filter out irrelevant stuff */
981 fprintf(of, "<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
982 func, t, usecs, ret);
988 fprintf(of, " <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
989 time_to_graph(t - (usecs / 1000000.0)),
991 time_to_graph(usecs / 1000000.0),
995 fprintf(of, " <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
996 time_to_graph(t - (usecs / 1000000.0)) + 5,
997 ps_to_graph(kcount) + 15,
998 func, usecs / 1000000.0);
1004 static void svg_ps_bars(FILE *of,
1005 struct list_sample_data *head,
1008 struct ps_struct *ps_first,
1012 struct ps_struct *ps;
1018 fprintf(of, "<!-- Process graph -->\n");
1019 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1021 /* surrounding box */
1022 svg_graph_box(of, head, pcount, graph_start);
1024 /* pass 2 - ps boxes */
1026 while ((ps = get_next_ps(ps, ps_first))) {
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 fprintf(of, "<!-- %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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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 fprintf(of, " <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<(n_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 < n_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 fprintf(of, "\n<!-- idle detected at %.03f seconds -->\n", idletime);
1194 fprintf(of, "<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1195 time_to_graph(idletime),
1197 time_to_graph(idletime),
1198 ps_to_graph(pcount) + arg_scale_y);
1199 fprintf(of, "<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1200 time_to_graph(idletime) + 5.0,
1201 ps_to_graph(pcount) + arg_scale_y,
1209 static void svg_top_ten_cpu(FILE *of, struct ps_struct *ps_first) {
1210 struct ps_struct *top[10];
1211 struct ps_struct emptyps = {};
1212 struct ps_struct *ps;
1215 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1218 /* walk all ps's and setup ptrs */
1220 while ((ps = get_next_ps(ps, ps_first))) {
1221 for (n = 0; n < 10; n++) {
1222 if (ps->total <= top[n]->total)
1224 /* cascade insert */
1225 for (m = 9; m > n; m--)
1232 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1233 for (n = 0; n < 10; n++)
1234 fprintf(of, "<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1241 static void svg_top_ten_pss(FILE *of, struct ps_struct *ps_first) {
1242 struct ps_struct *top[10];
1243 struct ps_struct emptyps = {};
1244 struct ps_struct *ps;
1247 for (n = 0; n < (int) ELEMENTSOF(top); n++)
1250 /* walk all ps's and setup ptrs */
1252 while ((ps = get_next_ps(ps, ps_first))) {
1253 for (n = 0; n < 10; n++) {
1254 if (ps->pss_max <= top[n]->pss_max)
1256 /* cascade insert */
1257 for (m = 9; m > n; m--)
1264 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1265 for (n = 0; n < 10; n++)
1266 fprintf(of, "<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1273 int svg_do(FILE *of,
1275 struct list_sample_data *head,
1276 struct ps_struct *ps_first,
1285 struct ps_struct *ps;
1291 /* count initcall thread count first */
1292 svg_do_initcall(of, head, 1, graph_start);
1293 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1295 /* then count processes */
1296 while ((ps = get_next_ps(ps, ps_first))) {
1302 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1304 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1306 /* after this, we can draw the header with proper sizing */
1307 svg_header(of, head, graph_start);
1308 fprintf(of, "<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1310 fprintf(of, "<g transform=\"translate(10,400)\">\n");
1311 svg_io_bi_bar(of, head, n_samples, graph_start, interval);
1312 fprintf(of, "</g>\n\n");
1314 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1315 svg_io_bo_bar(of, head, n_samples, graph_start, interval);
1316 fprintf(of, "</g>\n\n");
1318 for (c = -1; c < (arg_percpu ? n_cpus : 0); c++) {
1320 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1321 svg_cpu_bar(of, head, n_cpus, c, graph_start);
1322 fprintf(of, "</g>\n\n");
1325 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1326 svg_wait_bar(of, head, n_cpus, c, graph_start);
1327 fprintf(of, "</g>\n\n");
1332 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset));
1333 svg_do_initcall(of, head, 0, graph_start);
1334 fprintf(of, "</g>\n\n");
1338 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize);
1339 svg_ps_bars(of, head, n_samples, n_cpus, ps_first, graph_start, interval);
1340 fprintf(of, "</g>\n\n");
1342 fprintf(of, "<g transform=\"translate(10, 0)\">\n");
1343 r = svg_title(of, build, pscount, log_start, overrun);
1344 fprintf(of, "</g>\n\n");
1349 fprintf(of, "<g transform=\"translate(10,200)\">\n");
1350 svg_top_ten_cpu(of, ps_first);
1351 fprintf(of, "</g>\n\n");
1354 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize);
1355 svg_entropy_bar(of, head, graph_start);
1356 fprintf(of, "</g>\n\n");
1360 fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize);
1361 svg_pss_graph(of, head, ps_first, graph_start);
1362 fprintf(of, "</g>\n\n");
1364 fprintf(of, "<g transform=\"translate(410,200)\">\n");
1365 svg_top_ten_pss(of, ps_first);
1366 fprintf(of, "</g>\n\n");
1369 /* fprintf footer */
1370 fprintf(of, "\n</svg>\n");