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 #define max(x, y) (((x) > (y)) ? (x) : (y))
48 #define min(x, y) (((x) < (y)) ? (x) : (y))
50 static char str[8092];
52 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
54 static const char * const colorwheel[12] = {
55 "rgb(255,32,32)", // red
56 "rgb(32,192,192)", // cyan
57 "rgb(255,128,32)", // orange
58 "rgb(128,32,192)", // blue-violet
59 "rgb(255,255,32)", // yellow
60 "rgb(192,32,128)", // red-violet
61 "rgb(32,255,32)", // green
62 "rgb(255,64,32)", // red-orange
63 "rgb(32,32,255)", // blue
64 "rgb(255,192,32)", // yellow-orange
65 "rgb(192,32,192)", // violet
66 "rgb(32,192,32)" // yellow-green
69 static double idletime = -1.0;
70 static int pfiltered = 0;
71 static int pcount = 0;
72 static int kcount = 0;
73 static float psize = 0;
74 static float ksize = 0;
75 static float esize = 0;
77 static void svg_header(void) {
81 /* min width is about 1600px due to the label */
82 w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
83 w = ((w < 1600.0) ? 1600.0 : w);
85 /* height is variable based on pss, psize, ksize */
86 h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
87 + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
88 + psize + ksize + esize;
90 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
91 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
92 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
94 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
95 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
97 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
99 /* write some basic info as a comment, including some help */
100 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
101 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
102 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
103 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
104 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
106 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
107 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
108 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
109 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
110 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
111 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
114 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
116 svg(" rect { stroke-width: 1; }\n");
117 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
118 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
119 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
120 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
121 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
122 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
123 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
124 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
125 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
126 svg("// line.sec1 { }\n");
127 svg(" line.sec5 { stroke-width: 2; }\n");
128 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
129 svg(" line.dot { stroke-dasharray: 2 4; }\n");
130 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
132 svg(" .run { font-size: 8; font-style: italic; }\n");
133 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
134 svg(" text.sec { font-size: 8; }\n");
135 svg(" text.t1 { font-size: 24; }\n");
136 svg(" text.t2 { font-size: 12; }\n");
137 svg(" text.idle { font-size: 18; }\n");
139 svg(" ]]>\n </style>\n</defs>\n\n");
142 static void svg_title(const char *build) {
143 char cmdline[256] = "";
144 char filename[PATH_MAX];
146 char rootbdev[16] = "Unknown";
147 char model[256] = "Unknown";
148 char date[256] = "Unknown";
149 char cpu[256] = "Unknown";
156 /* grab /proc/cmdline */
157 fd = openat(procfd, "cmdline", O_RDONLY);
160 if (!fgets(cmdline, 255, f))
161 sprintf(cmdline, "Unknown");
165 /* extract root fs so we can find disk model name in sysfs */
166 /* FIXME: this works only in the simple case */
167 c = strstr(cmdline, "root=/dev/");
169 strncpy(rootbdev, &c[10], 3);
171 sprintf(filename, "block/%s/device/model", rootbdev);
172 fd = openat(sysfd, filename, O_RDONLY);
175 if (!fgets(model, 255, f))
176 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
181 /* various utsname parameters */
183 fprintf(stderr, "Error getting uname info\n");
187 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
190 fd = openat(procfd, "cpuinfo", O_RDONLY);
193 while (fgets(buf, 255, f)) {
194 if (strstr(buf, "model name")) {
195 strncpy(cpu, &buf[13], 255);
202 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
204 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
205 uts.sysname, uts.release, uts.version, uts.machine);
206 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
208 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
210 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
212 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
215 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
218 svg("%.03fs", idletime);
222 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",
223 arg_hz, arg_samples_len, overrun, pscount, pfiltered);
226 static void svg_graph_box(int height) {
230 /* outside box, fill */
231 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
233 time_to_graph(sampletime[samples-1] - graph_start),
234 ps_to_graph(height));
236 for (d = graph_start; d <= sampletime[samples-1];
237 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
238 /* lines for each second */
240 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
241 time_to_graph(d - graph_start),
242 time_to_graph(d - graph_start),
243 ps_to_graph(height));
244 else if (i % 10 == 0)
245 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
246 time_to_graph(d - graph_start),
247 time_to_graph(d - graph_start),
248 ps_to_graph(height));
250 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
251 time_to_graph(d - graph_start),
252 time_to_graph(d - graph_start),
253 ps_to_graph(height));
257 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
258 time_to_graph(d - graph_start),
266 /* xml comments must not contain "--" */
267 static char* xml_comment_encode(const char* name) {
270 enc_name = strdup(name);
274 for (p = enc_name; *p; p++)
275 if (p[0] == '-' && p[1] == '-')
281 static void svg_pss_graph(void) {
282 struct ps_struct *ps;
285 svg("\n\n<!-- Pss memory size graph -->\n");
287 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
289 /* vsize 1000 == 1000mb */
291 /* draw some hlines for usable memory sizes */
292 for (i = 100000; i < 1000000; i += 100000) {
293 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
296 time_to_graph(sampletime[samples-1] - graph_start),
298 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
299 time_to_graph(sampletime[samples-1] - graph_start) + 5,
300 kb_to_graph(i), (1000000 - i) / 1000);
304 /* now plot the graph itself */
305 for (i = 1; i < samples ; i++) {
312 /* put all the small pss blocks into the bottom */
314 while (ps->next_ps) {
318 if (ps->sample[i].pss <= (100 * arg_scale_y))
319 top += ps->sample[i].pss;
321 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
323 time_to_graph(sampletime[i - 1] - graph_start),
324 kb_to_graph(1000000.0 - top),
325 time_to_graph(sampletime[i] - sampletime[i - 1]),
326 kb_to_graph(top - bottom));
330 /* now plot the ones that are of significant size */
332 while (ps->next_ps) {
336 /* don't draw anything smaller than 2mb */
337 if (ps->sample[i].pss > (100 * arg_scale_y)) {
338 top = bottom + ps->sample[i].pss;
339 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
340 colorwheel[ps->pid % 12],
341 time_to_graph(sampletime[i - 1] - graph_start),
342 kb_to_graph(1000000.0 - top),
343 time_to_graph(sampletime[i] - sampletime[i - 1]),
344 kb_to_graph(top - bottom));
350 /* overlay all the text labels */
351 for (i = 1; i < samples ; i++) {
358 /* put all the small pss blocks into the bottom */
360 while (ps->next_ps) {
364 if (ps->sample[i].pss <= (100 * arg_scale_y))
365 top += ps->sample[i].pss;
370 /* now plot the ones that are of significant size */
372 while (ps->next_ps) {
376 /* don't draw anything smaller than 2mb */
377 if (ps->sample[i].pss > (100 * arg_scale_y)) {
378 top = bottom + ps->sample[i].pss;
379 /* draw a label with the process / PID */
380 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * arg_scale_y)))
381 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
382 time_to_graph(sampletime[i] - graph_start),
383 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
391 /* debug output - full data dump */
392 svg("\n\n<!-- PSS map - csv format -->\n");
394 while (ps->next_ps) {
395 char _cleanup_free_ *enc_name = NULL;
400 enc_name = xml_comment_encode(ps->name);
404 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
406 for (i = 0; i < samples ; i++) {
407 svg("%d," , ps->sample[i].pss);
414 static void svg_io_bi_bar(void) {
420 svg("<!-- IO utilization graph - In -->\n");
422 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
425 * calculate rounding range
427 * We need to round IO data since IO block data is not updated on
428 * each poll. Applying a smoothing function loses some burst data,
429 * so keep the smoothing range short.
431 range = 0.25 / (1.0 / arg_hz);
433 range = 2.0; /* no smoothing */
435 /* surrounding box */
438 /* find the max IO first */
439 for (i = 1; i < samples; i++) {
444 start = max(i - ((range / 2) - 1), 0);
445 stop = min(i + (range / 2), samples - 1);
447 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
453 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
460 for (i = 1; i < samples; i++) {
466 start = max(i - ((range / 2) - 1), 0);
467 stop = min(i + (range / 2), samples);
469 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
474 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
475 time_to_graph(sampletime[i - 1] - graph_start),
476 (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
477 time_to_graph(sampletime[i] - sampletime[i - 1]),
478 pbi * (arg_scale_y * 5));
480 /* labels around highest value */
482 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
483 time_to_graph(sampletime[i] - graph_start) + 5,
484 ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
485 max / 1024.0 / (interval / 1000000000.0));
490 static void svg_io_bo_bar(void) {
496 svg("<!-- IO utilization graph - out -->\n");
498 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
501 * calculate rounding range
503 * We need to round IO data since IO block data is not updated on
504 * each poll. Applying a smoothing function loses some burst data,
505 * so keep the smoothing range short.
507 range = 0.25 / (1.0 / arg_hz);
509 range = 2.0; /* no smoothing */
511 /* surrounding box */
514 /* find the max IO first */
515 for (i = 1; i < samples; i++) {
520 start = max(i - ((range / 2) - 1), 0);
521 stop = min(i + (range / 2), samples - 1);
523 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
527 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
536 for (i = 1; i < samples; i++) {
542 start = max(i - ((range / 2) - 1), 0);
543 stop = min(i + (range / 2), samples);
545 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
550 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
551 time_to_graph(sampletime[i - 1] - graph_start),
552 (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
553 time_to_graph(sampletime[i] - sampletime[i - 1]),
554 pbo * (arg_scale_y * 5));
556 /* labels around highest bo value */
558 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
559 time_to_graph(sampletime[i] - graph_start) + 5,
560 ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
561 max / 1024.0 / (interval / 1000000000.0));
566 static void svg_cpu_bar(void) {
569 svg("<!-- CPU utilization graph -->\n");
571 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
572 /* surrounding box */
575 /* bars for each sample, proportional to the CPU util. */
576 for (i = 1; i < samples; i++) {
583 for (c = 0; c < cpus; c++)
584 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
586 trt = trt / 1000000000.0;
588 trt = trt / (double)cpus;
591 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
597 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
598 time_to_graph(sampletime[i - 1] - graph_start),
599 (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
600 time_to_graph(sampletime[i] - sampletime[i - 1]),
601 ptrt * (arg_scale_y * 5));
606 static void svg_wait_bar(void) {
609 svg("<!-- Wait time aggregation box -->\n");
611 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
613 /* surrounding box */
616 /* bars for each sample, proportional to the CPU util. */
617 for (i = 1; i < samples; i++) {
624 for (c = 0; c < cpus; c++)
625 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
627 twt = twt / 1000000000.0;
629 twt = twt / (double)cpus;
632 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
638 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
639 time_to_graph(sampletime[i - 1] - graph_start),
640 ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
641 time_to_graph(sampletime[i] - sampletime[i - 1]),
642 ptwt * (arg_scale_y * 5));
648 static void svg_entropy_bar(void) {
651 svg("<!-- entropy pool graph -->\n");
653 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
654 /* surrounding box */
657 /* bars for each sample, scale 0-4096 */
658 for (i = 1; i < samples; i++) {
659 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
660 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
661 time_to_graph(sampletime[i - 1] - graph_start),
662 ((arg_scale_y * 5) - ((entropy_avail[i] / 4096.) * (arg_scale_y * 5))),
663 time_to_graph(sampletime[i] - sampletime[i - 1]),
664 (entropy_avail[i] / 4096.) * (arg_scale_y * 5));
668 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
670 * walk the list of processes and return the next one to be
684 /* go back for parent siblings */
687 if (ps->parent->next)
688 return ps->parent->next;
697 static int ps_filter(struct ps_struct *ps) {
701 /* can't draw data when there is only 1 sample (need start + stop) */
702 if (ps->first == ps->last)
705 /* don't filter kthreadd */
709 /* drop stuff that doesn't use any real CPU time */
710 if (ps->total <= 0.001)
716 static void svg_do_initcall(int count_only) {
717 FILE _cleanup_pclose_ *f = NULL;
723 /* can't plot initcall when disabled or in relative mode */
724 if (!initcall || arg_relative) {
730 svg("<!-- initcall -->\n");
732 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
733 /* surrounding box */
734 svg_graph_box(kcount);
740 * Initcall graphing - parses dmesg buffer and displays kernel threads
741 * This somewhat uses the same methods and scaling to show processes
742 * but looks a lot simpler. It's overlaid entirely onto the PS graph
746 f = popen("dmesg", "r");
755 if (fgets(l, sizeof(l) - 1, f) == NULL)
758 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
759 &t, func, &ret, &usecs);
761 /* also parse initcalls done by module loading */
762 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
763 &t, func, &ret, &usecs);
768 /* chop the +0xXX/0xXX stuff */
769 while(func[z] != '+')
774 /* filter out irrelevant stuff */
780 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
781 func, t, usecs, ret);
787 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
788 time_to_graph(t - (usecs / 1000000.0)),
790 time_to_graph(usecs / 1000000.0),
794 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
795 time_to_graph(t - (usecs / 1000000.0)) + 5,
796 ps_to_graph(kcount) + 15,
804 static void svg_ps_bars(void) {
805 struct ps_struct *ps;
811 svg("<!-- Process graph -->\n");
813 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
815 /* surrounding box */
816 svg_graph_box(pcount);
818 /* pass 2 - ps boxes */
820 while ((ps = get_next_ps(ps))) {
821 char _cleanup_free_ *enc_name = NULL;
826 enc_name = xml_comment_encode(ps->name);
830 /* leave some trace of what we actually filtered etc. */
831 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
832 ps->ppid, ps->total);
834 /* it would be nice if we could use exec_start from /proc/pid/sched,
835 * but it's unreliable and gives bogus numbers */
836 starttime = sampletime[ps->first];
838 if (!ps_filter(ps)) {
839 /* remember where _to_ our children need to draw a line */
840 ps->pos_x = time_to_graph(starttime - graph_start);
841 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
843 /* hook children to our parent coords instead */
844 ps->pos_x = ps->parent->pos_x;
845 ps->pos_y = ps->parent->pos_y;
847 /* if this is the last child, we might still need to draw a connecting line */
848 if ((!ps->next) && (ps->parent))
849 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
851 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
857 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
858 time_to_graph(starttime - graph_start),
860 time_to_graph(sampletime[ps->last] - starttime),
863 /* paint cpu load over these */
864 for (t = ps->first + 1; t < ps->last; t++) {
868 /* calculate over interval */
869 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
870 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
872 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
873 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
875 /* this can happen if timekeeping isn't accurate enough */
881 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
884 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
885 time_to_graph(sampletime[t - 1] - graph_start),
887 time_to_graph(sampletime[t] - sampletime[t - 1]),
890 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
891 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
892 time_to_graph(sampletime[t - 1] - graph_start),
893 ps_to_graph(j + (1.0 - prt)),
894 time_to_graph(sampletime[t] - sampletime[t - 1]),
898 /* determine where to display the process name */
899 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
900 /* too small to fit label inside the box */
905 /* text label of process name */
906 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
907 time_to_graph(sampletime[w] - graph_start) + 5.0,
908 ps_to_graph(j) + 14.0,
911 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
912 /* paint lines to the parent process */
914 /* horizontal part */
915 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
916 time_to_graph(starttime - graph_start),
917 ps_to_graph(j) + 10.0,
919 ps_to_graph(j) + 10.0);
921 /* one vertical line connecting all the horizontal ones up */
923 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
925 ps_to_graph(j) + 10.0,
930 j++; /* count boxes */
935 /* last pass - determine when idle */
937 /* make sure we start counting from the point where we actually have
938 * data: assume that bootchart's first sample is when data started
941 while (ps->next_ps) {
947 for (i = ps->first; i < samples - (arg_hz / 2); i++) {
952 /* subtract bootchart cpu utilization from total */
954 for (c = 0; c < cpus; c++)
955 crt += cpustat[c].sample[i + ((int)arg_hz / 2)].runtime - cpustat[c].sample[i].runtime;
956 brt = ps->sample[i + ((int)arg_hz / 2)].runtime - ps->sample[i].runtime;
959 * our definition of "idle":
961 * if for (hz / 2) we've used less CPU than (interval / 2) ...
962 * defaults to 4.0%, which experimentally, is where atom idles
964 if ((crt - brt) < (interval / 2.0)) {
965 idletime = sampletime[i] - graph_start;
966 svg("\n<!-- idle detected at %.03f seconds -->\n",
968 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
969 time_to_graph(idletime),
971 time_to_graph(idletime),
972 ps_to_graph(pcount) + arg_scale_y);
973 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
974 time_to_graph(idletime) + 5.0,
975 ps_to_graph(pcount) + arg_scale_y,
982 static void svg_top_ten_cpu(void) {
983 struct ps_struct *top[10];
984 struct ps_struct emptyps;
985 struct ps_struct *ps;
988 memset(&emptyps, 0, sizeof(struct ps_struct));
989 for (n=0; n < 10; n++)
992 /* walk all ps's and setup ptrs */
994 while ((ps = get_next_ps(ps))) {
995 for (n = 0; n < 10; n++) {
996 if (ps->total <= top[n]->total)
999 for (m = 9; m > n; m--)
1006 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1007 for (n = 0; n < 10; n++)
1008 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1015 static void svg_top_ten_pss(void) {
1016 struct ps_struct *top[10];
1017 struct ps_struct emptyps;
1018 struct ps_struct *ps;
1021 memset(&emptyps, 0, sizeof(struct ps_struct));
1022 for (n=0; n < 10; n++)
1025 /* walk all ps's and setup ptrs */
1027 while ((ps = get_next_ps(ps))) {
1028 for (n = 0; n < 10; n++) {
1029 if (ps->pss_max <= top[n]->pss_max)
1031 /* cascade insert */
1032 for (m = 9; m > n; m--)
1039 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1040 for (n = 0; n < 10; n++)
1041 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1048 void svg_do(const char *build) {
1049 struct ps_struct *ps;
1051 memset(&str, 0, sizeof(str));
1055 /* count initcall thread count first */
1057 ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1059 /* then count processes */
1060 while ((ps = get_next_ps(ps))) {
1066 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1068 esize = (arg_entropy ? arg_scale_y * 7 : 0);
1070 /* after this, we can draw the header with proper sizing */
1073 svg("<g transform=\"translate(10,400)\">\n");
1077 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1081 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1085 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1090 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1095 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1099 svg("<g transform=\"translate(10, 0)\">\n");
1103 svg("<g transform=\"translate(10,200)\">\n");
1108 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1114 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1118 svg("<g transform=\"translate(410,200)\">\n");