1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Coproration
9 Auke Kok <auke-jan.h.kok@intel.com>
11 systemd is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2.1 of the License, or
14 (at your option) any later version.
16 systemd is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include <sys/utsname.h>
40 #include "bootchart.h"
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 float psize = 0;
71 static float ksize = 0;
72 static float esize = 0;
74 static void svg_header(void) {
78 /* min width is about 1600px due to the label */
79 w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
80 w = ((w < 1600.0) ? 1600.0 : w);
82 /* height is variable based on pss, psize, ksize */
83 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
84 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
85 + psize + ksize + esize;
87 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
88 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
89 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
91 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
92 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
94 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
96 /* write some basic info as a comment, including some help */
97 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
98 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
99 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
100 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
101 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
103 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
104 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
105 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
106 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
107 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
108 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
111 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
113 svg(" rect { stroke-width: 1; }\n");
114 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
115 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
116 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
117 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
118 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
119 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
120 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
121 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
122 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
123 svg("// line.sec1 { }\n");
124 svg(" line.sec5 { stroke-width: 2; }\n");
125 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
126 svg(" line.dot { stroke-dasharray: 2 4; }\n");
127 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
129 svg(" .run { font-size: 8; font-style: italic; }\n");
130 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
131 svg(" text.sec { font-size: 8; }\n");
132 svg(" text.t1 { font-size: 24; }\n");
133 svg(" text.t2 { font-size: 12; }\n");
134 svg(" text.idle { font-size: 18; }\n");
136 svg(" ]]>\n </style>\n</defs>\n\n");
139 static void svg_title(const char *build) {
140 char cmdline[256] = "";
141 char filename[PATH_MAX];
143 char rootbdev[16] = "Unknown";
144 char model[256] = "Unknown";
145 char date[256] = "Unknown";
146 char cpu[256] = "Unknown";
153 /* grab /proc/cmdline */
154 fd = openat(procfd, "cmdline", O_RDONLY);
157 if (!fgets(cmdline, 255, f))
158 sprintf(cmdline, "Unknown");
162 /* extract root fs so we can find disk model name in sysfs */
163 /* FIXME: this works only in the simple case */
164 c = strstr(cmdline, "root=/dev/");
166 strncpy(rootbdev, &c[10], 3);
168 sprintf(filename, "block/%s/device/model", rootbdev);
169 fd = openat(sysfd, filename, O_RDONLY);
172 if (!fgets(model, 255, f))
173 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
178 /* various utsname parameters */
180 fprintf(stderr, "Error getting uname info\n");
184 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
187 fd = openat(procfd, "cpuinfo", O_RDONLY);
190 while (fgets(buf, 255, f)) {
191 if (strstr(buf, "model name")) {
192 strncpy(cpu, &buf[13], 255);
199 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
201 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
202 uts.sysname, uts.release, uts.version, uts.machine);
203 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
205 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
207 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
209 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
211 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
212 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
215 svg("%.03fs", idletime);
219 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",
220 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
223 static void svg_graph_box(int height) {
227 /* outside box, fill */
228 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
230 time_to_graph(sampletime[samples-1] - graph_start),
231 ps_to_graph(height));
233 for (d = graph_start; d <= sampletime[samples-1];
234 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
235 /* lines for each second */
237 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
238 time_to_graph(d - graph_start),
239 time_to_graph(d - graph_start),
240 ps_to_graph(height));
241 else if (i % 10 == 0)
242 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
243 time_to_graph(d - graph_start),
244 time_to_graph(d - graph_start),
245 ps_to_graph(height));
247 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
248 time_to_graph(d - graph_start),
249 time_to_graph(d - graph_start),
250 ps_to_graph(height));
254 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
255 time_to_graph(d - graph_start),
263 /* xml comments must not contain "--" */
264 static char* xml_comment_encode(const char* name) {
267 enc_name = strdup(name);
271 for (p = enc_name; *p; p++)
272 if (p[0] == '-' && p[1] == '-')
278 static void svg_pss_graph(void) {
279 struct ps_struct *ps;
282 svg("\n\n<!-- Pss memory size graph -->\n");
284 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
286 /* vsize 1000 == 1000mb */
288 /* draw some hlines for usable memory sizes */
289 for (i = 100000; i < 1000000; i += 100000) {
290 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
293 time_to_graph(sampletime[samples-1] - graph_start),
295 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
296 time_to_graph(sampletime[samples-1] - graph_start) + 5,
297 kb_to_graph(i), (1000000 - i) / 1000);
301 /* now plot the graph itself */
302 for (i = 1; i < samples ; i++) {
309 /* put all the small pss blocks into the bottom */
311 while (ps->next_ps) {
315 if (ps->sample[i].pss <= (100 * arg_scale_y))
316 top += ps->sample[i].pss;
318 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
320 time_to_graph(sampletime[i - 1] - graph_start),
321 kb_to_graph(1000000.0 - top),
322 time_to_graph(sampletime[i] - sampletime[i - 1]),
323 kb_to_graph(top - bottom));
327 /* now plot the ones that are of significant size */
329 while (ps->next_ps) {
333 /* don't draw anything smaller than 2mb */
334 if (ps->sample[i].pss > (100 * arg_scale_y)) {
335 top = bottom + ps->sample[i].pss;
336 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
337 colorwheel[ps->pid % 12],
338 time_to_graph(sampletime[i - 1] - graph_start),
339 kb_to_graph(1000000.0 - top),
340 time_to_graph(sampletime[i] - sampletime[i - 1]),
341 kb_to_graph(top - bottom));
347 /* overlay all the text labels */
348 for (i = 1; i < samples ; i++) {
355 /* put all the small pss blocks into the bottom */
357 while (ps->next_ps) {
361 if (ps->sample[i].pss <= (100 * arg_scale_y))
362 top += ps->sample[i].pss;
367 /* now plot the ones that are of significant size */
369 while (ps->next_ps) {
373 /* don't draw anything smaller than 2mb */
374 if (ps->sample[i].pss > (100 * arg_scale_y)) {
375 top = bottom + ps->sample[i].pss;
376 /* draw a label with the process / PID */
377 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * arg_scale_y)))
378 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
379 time_to_graph(sampletime[i] - graph_start),
380 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
388 /* debug output - full data dump */
389 svg("\n\n<!-- PSS map - csv format -->\n");
391 while (ps->next_ps) {
392 char _cleanup_free_ *enc_name = NULL;
397 enc_name = xml_comment_encode(ps->name);
401 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
403 for (i = 0; i < samples ; i++) {
404 svg("%d," , ps->sample[i].pss);
411 static void svg_io_bi_bar(void) {
417 svg("<!-- IO utilization graph - In -->\n");
419 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
422 * calculate rounding range
424 * We need to round IO data since IO block data is not updated on
425 * each poll. Applying a smoothing function loses some burst data,
426 * so keep the smoothing range short.
428 range = 0.25 / (1.0 / arg_hz);
430 range = 2.0; /* no smoothing */
432 /* surrounding box */
435 /* find the max IO first */
436 for (i = 1; i < samples; i++) {
441 start = MAX(i - ((range / 2) - 1), 0);
442 stop = MIN(i + (range / 2), samples - 1);
444 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
450 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
457 for (i = 1; i < samples; i++) {
463 start = MAX(i - ((range / 2) - 1), 0);
464 stop = MIN(i + (range / 2), samples);
466 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
471 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
472 time_to_graph(sampletime[i - 1] - graph_start),
473 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
474 time_to_graph(sampletime[i] - sampletime[i - 1]),
475 pbi * (arg_scale_y * 5));
477 /* labels around highest value */
479 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
480 time_to_graph(sampletime[i] - graph_start) + 5,
481 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
482 max / 1024.0 / (interval / 1000000000.0));
487 static void svg_io_bo_bar(void) {
493 svg("<!-- IO utilization graph - out -->\n");
495 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
498 * calculate rounding range
500 * We need to round IO data since IO block data is not updated on
501 * each poll. Applying a smoothing function loses some burst data,
502 * so keep the smoothing range short.
504 range = 0.25 / (1.0 / arg_hz);
506 range = 2.0; /* no smoothing */
508 /* surrounding box */
511 /* find the max IO first */
512 for (i = 1; i < samples; i++) {
517 start = MAX(i - ((range / 2) - 1), 0);
518 stop = MIN(i + (range / 2), samples - 1);
520 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
524 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
533 for (i = 1; i < samples; i++) {
539 start = MAX(i - ((range / 2) - 1), 0);
540 stop = MIN(i + (range / 2), samples);
542 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
547 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
548 time_to_graph(sampletime[i - 1] - graph_start),
549 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
550 time_to_graph(sampletime[i] - sampletime[i - 1]),
551 pbo * (arg_scale_y * 5));
553 /* labels around highest bo value */
555 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
556 time_to_graph(sampletime[i] - graph_start) + 5,
557 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
558 max / 1024.0 / (interval / 1000000000.0));
563 static void svg_cpu_bar(void) {
566 svg("<!-- CPU utilization graph -->\n");
568 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
569 /* surrounding box */
572 /* bars for each sample, proportional to the CPU util. */
573 for (i = 1; i < samples; i++) {
580 for (c = 0; c < cpus; c++)
581 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
583 trt = trt / 1000000000.0;
585 trt = trt / (double)cpus;
588 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
594 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
595 time_to_graph(sampletime[i - 1] - graph_start),
596 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
597 time_to_graph(sampletime[i] - sampletime[i - 1]),
598 ptrt * (arg_scale_y * 5));
603 static void svg_wait_bar(void) {
606 svg("<!-- Wait time aggregation box -->\n");
608 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
610 /* surrounding box */
613 /* bars for each sample, proportional to the CPU util. */
614 for (i = 1; i < samples; i++) {
621 for (c = 0; c < cpus; c++)
622 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
624 twt = twt / 1000000000.0;
626 twt = twt / (double)cpus;
629 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
635 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
636 time_to_graph(sampletime[i - 1] - graph_start),
637 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
638 time_to_graph(sampletime[i] - sampletime[i - 1]),
639 ptwt * (arg_scale_y * 5));
645 static void svg_entropy_bar(void) {
648 svg("<!-- entropy pool graph -->\n");
650 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
651 /* surrounding box */
654 /* bars for each sample, scale 0-4096 */
655 for (i = 1; i < samples; i++) {
656 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
657 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
658 time_to_graph(sampletime[i - 1] - graph_start),
659 ((arg_scale_y * 5) - ((entropy_avail[i] / 4096.) * (arg_scale_y * 5))),
660 time_to_graph(sampletime[i] - sampletime[i - 1]),
661 (entropy_avail[i] / 4096.) * (arg_scale_y * 5));
665 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
667 * walk the list of processes and return the next one to be
681 /* go back for parent siblings */
684 if (ps->parent->next)
685 return ps->parent->next;
694 static int ps_filter(struct ps_struct *ps) {
698 /* can't draw data when there is only 1 sample (need start + stop) */
699 if (ps->first == ps->last)
702 /* don't filter kthreadd */
706 /* drop stuff that doesn't use any real CPU time */
707 if (ps->total <= 0.001)
713 static void svg_do_initcall(int count_only) {
714 FILE _cleanup_pclose_ *f = NULL;
720 /* can't plot initcall when disabled or in relative mode */
721 if (!initcall || arg_relative) {
727 svg("<!-- initcall -->\n");
729 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
730 /* surrounding box */
731 svg_graph_box(kcount);
737 * Initcall graphing - parses dmesg buffer and displays kernel threads
738 * This somewhat uses the same methods and scaling to show processes
739 * but looks a lot simpler. It's overlaid entirely onto the PS graph
743 f = popen("dmesg", "r");
752 if (fgets(l, sizeof(l) - 1, f) == NULL)
755 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
756 &t, func, &ret, &usecs);
758 /* also parse initcalls done by module loading */
759 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
760 &t, func, &ret, &usecs);
765 /* chop the +0xXX/0xXX stuff */
766 while(func[z] != '+')
771 /* filter out irrelevant stuff */
777 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
778 func, t, usecs, ret);
784 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
785 time_to_graph(t - (usecs / 1000000.0)),
787 time_to_graph(usecs / 1000000.0),
791 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
792 time_to_graph(t - (usecs / 1000000.0)) + 5,
793 ps_to_graph(kcount) + 15,
801 static void svg_ps_bars(void) {
802 struct ps_struct *ps;
808 svg("<!-- Process graph -->\n");
810 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
812 /* surrounding box */
813 svg_graph_box(pcount);
815 /* pass 2 - ps boxes */
817 while ((ps = get_next_ps(ps))) {
818 char _cleanup_free_ *enc_name = NULL;
823 enc_name = xml_comment_encode(ps->name);
827 /* leave some trace of what we actually filtered etc. */
828 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
829 ps->ppid, ps->total);
831 /* it would be nice if we could use exec_start from /proc/pid/sched,
832 * but it's unreliable and gives bogus numbers */
833 starttime = sampletime[ps->first];
835 if (!ps_filter(ps)) {
836 /* remember where _to_ our children need to draw a line */
837 ps->pos_x = time_to_graph(starttime - graph_start);
838 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
840 /* hook children to our parent coords instead */
841 ps->pos_x = ps->parent->pos_x;
842 ps->pos_y = ps->parent->pos_y;
844 /* if this is the last child, we might still need to draw a connecting line */
845 if ((!ps->next) && (ps->parent))
846 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
848 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
854 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
855 time_to_graph(starttime - graph_start),
857 time_to_graph(sampletime[ps->last] - starttime),
860 /* paint cpu load over these */
861 for (t = ps->first + 1; t < ps->last; t++) {
865 /* calculate over interval */
866 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
867 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
869 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
870 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
872 /* this can happen if timekeeping isn't accurate enough */
878 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
881 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
882 time_to_graph(sampletime[t - 1] - graph_start),
884 time_to_graph(sampletime[t] - sampletime[t - 1]),
887 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
888 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
889 time_to_graph(sampletime[t - 1] - graph_start),
890 ps_to_graph(j + (1.0 - prt)),
891 time_to_graph(sampletime[t] - sampletime[t - 1]),
895 /* determine where to display the process name */
896 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
897 /* too small to fit label inside the box */
902 /* text label of process name */
903 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
904 time_to_graph(sampletime[w] - graph_start) + 5.0,
905 ps_to_graph(j) + 14.0,
908 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
909 /* paint lines to the parent process */
911 /* horizontal part */
912 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
913 time_to_graph(starttime - graph_start),
914 ps_to_graph(j) + 10.0,
916 ps_to_graph(j) + 10.0);
918 /* one vertical line connecting all the horizontal ones up */
920 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
922 ps_to_graph(j) + 10.0,
927 j++; /* count boxes */
932 /* last pass - determine when idle */
934 /* make sure we start counting from the point where we actually have
935 * data: assume that bootchart's first sample is when data started
938 while (ps->next_ps) {
944 for (i = ps->first; i < samples - (arg_hz / 2); i++) {
949 /* subtract bootchart cpu utilization from total */
951 for (c = 0; c < cpus; c++)
952 crt += cpustat[c].sample[i + ((int)arg_hz / 2)].runtime - cpustat[c].sample[i].runtime;
953 brt = ps->sample[i + ((int)arg_hz / 2)].runtime - ps->sample[i].runtime;
956 * our definition of "idle":
958 * if for (hz / 2) we've used less CPU than (interval / 2) ...
959 * defaults to 4.0%, which experimentally, is where atom idles
961 if ((crt - brt) < (interval / 2.0)) {
962 idletime = sampletime[i] - graph_start;
963 svg("\n<!-- idle detected at %.03f seconds -->\n",
965 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
966 time_to_graph(idletime),
968 time_to_graph(idletime),
969 ps_to_graph(pcount) + arg_scale_y);
970 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
971 time_to_graph(idletime) + 5.0,
972 ps_to_graph(pcount) + arg_scale_y,
979 static void svg_top_ten_cpu(void) {
980 struct ps_struct *top[10];
981 struct ps_struct emptyps;
982 struct ps_struct *ps;
985 memset(&emptyps, 0, sizeof(struct ps_struct));
986 for (n=0; n < 10; n++)
989 /* walk all ps's and setup ptrs */
991 while ((ps = get_next_ps(ps))) {
992 for (n = 0; n < 10; n++) {
993 if (ps->total <= top[n]->total)
996 for (m = 9; m > n; m--)
1003 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1004 for (n = 0; n < 10; n++)
1005 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1012 static void svg_top_ten_pss(void) {
1013 struct ps_struct *top[10];
1014 struct ps_struct emptyps;
1015 struct ps_struct *ps;
1018 memset(&emptyps, 0, sizeof(struct ps_struct));
1019 for (n=0; n < 10; n++)
1022 /* walk all ps's and setup ptrs */
1024 while ((ps = get_next_ps(ps))) {
1025 for (n = 0; n < 10; n++) {
1026 if (ps->pss_max <= top[n]->pss_max)
1028 /* cascade insert */
1029 for (m = 9; m > n; m--)
1036 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1037 for (n = 0; n < 10; n++)
1038 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1045 void svg_do(const char *build) {
1046 struct ps_struct *ps;
1048 memset(&str, 0, sizeof(str));
1052 /* count initcall thread count first */
1054 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1056 /* then count processes */
1057 while ((ps = get_next_ps(ps))) {
1063 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1065 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1067 /* after this, we can draw the header with proper sizing */
1070 svg("<g transform=\"translate(10,400)\">\n");
1074 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1078 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1082 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1087 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1092 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1096 svg("<g transform=\"translate(10, 0)\">\n");
1100 svg("<g transform=\"translate(10,200)\">\n");
1105 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1111 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1115 svg("<g transform=\"translate(410,200)\">\n");