2 bootchart.c - This file is part of systemd-bootchart
4 Copyright (C) 2009-2013 Intel Coproration
7 Auke Kok <auke-jan.h.kok@intel.com>
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include <sys/utsname.h>
34 #include "bootchart.h"
39 #define time_to_graph(t) ((t) * scale_x)
40 #define ps_to_graph(n) ((n) * scale_y)
41 #define kb_to_graph(m) ((m) * scale_y * 0.0001)
42 #define to_color(n) (192.0 - ((n) * 192.0))
44 #define max(x, y) (((x) > (y)) ? (x) : (y))
45 #define min(x, y) (((x) < (y)) ? (x) : (y))
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 *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;
75 static void svg_header(void)
80 /* min width is about 1600px due to the label */
81 w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
82 w = ((w < 1600.0) ? 1600.0 : w);
84 /* height is variable based on pss, psize, ksize */
85 h = 400.0 + (scale_y * 30.0) /* base graphs and title */
86 + (pss ? (100.0 * scale_y) + (scale_y * 7.0) : 0.0) /* pss estimate */
87 + psize + ksize + esize;
89 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
90 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
91 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
93 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
94 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
96 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
98 /* write some basic info as a comment, including some help */
99 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
100 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
101 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
102 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
103 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
105 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
106 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", hz, len);
107 svg("<!-- x=\"%f\" y=\"%f\" -->\n", scale_x, scale_y);
108 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", relative, filter);
109 svg("<!-- p=\"%d\" e=\"%d\" -->\n", pss, entropy);
110 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", output_path, init_path);
113 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
115 svg(" rect { stroke-width: 1; }\n");
116 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
117 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
118 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
119 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
120 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
121 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
122 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
123 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
124 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
125 svg("// line.sec1 { }\n");
126 svg(" line.sec5 { stroke-width: 2; }\n");
127 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
128 svg(" line.dot { stroke-dasharray: 2 4; }\n");
129 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
131 svg(" .run { font-size: 8; font-style: italic; }\n");
132 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
133 svg(" text.sec { font-size: 8; }\n");
134 svg(" text.t1 { font-size: 24; }\n");
135 svg(" text.t2 { font-size: 12; }\n");
136 svg(" text.idle { font-size: 18; }\n");
138 svg(" ]]>\n </style>\n</defs>\n\n");
143 static void svg_title(void)
145 char cmdline[256] = "";
146 char filename[PATH_MAX];
148 char rootbdev[16] = "Unknown";
149 char model[256] = "Unknown";
150 char date[256] = "Unknown";
151 char cpu[256] = "Unknown";
152 char build[256] = "Unknown";
159 /* grab /proc/cmdline */
160 fd = openat(procfd, "cmdline", O_RDONLY);
163 if (!fgets(cmdline, 255, f))
164 sprintf(cmdline, "Unknown");
168 /* extract root fs so we can find disk model name in sysfs */
169 c = strstr(cmdline, "root=/dev/");
171 strncpy(rootbdev, &c[10], 3);
174 sprintf(filename, "block/%s/device/model", rootbdev);
175 fd = openat(sysfd, filename, O_RDONLY);
178 if (!fgets(model, 255, f))
179 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
183 /* various utsname parameters */
185 fprintf(stderr, "Error getting uname info\n");
189 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
192 fd = openat(procfd, "cpuinfo", O_RDONLY);
195 while (fgets(buf, 255, f)) {
196 if (strstr(buf, "model name")) {
197 strncpy(cpu, &buf[13], 255);
204 /* Build - 1st line from /etc/system-release */
205 f = fopen("/etc/system-release", "r");
207 if (fgets(buf, 255, f))
208 strncpy(build, buf, 255);
212 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
215 uts.sysname, uts.release, uts.version, uts.machine);
216 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
218 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
220 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
222 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
225 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
228 svg("%.03fs", idletime);
232 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",
233 hz, len, overrun, pscount, pfiltered);
237 static void svg_graph_box(int height)
242 /* outside box, fill */
243 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
245 time_to_graph(sampletime[samples-1] - graph_start),
246 ps_to_graph(height));
248 for (d = graph_start; d <= sampletime[samples-1];
249 d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
250 /* lines for each second */
252 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
253 time_to_graph(d - graph_start),
254 time_to_graph(d - graph_start),
255 ps_to_graph(height));
256 else if (i % 10 == 0)
257 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
258 time_to_graph(d - graph_start),
259 time_to_graph(d - graph_start),
260 ps_to_graph(height));
262 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
263 time_to_graph(d - graph_start),
264 time_to_graph(d - graph_start),
265 ps_to_graph(height));
269 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
270 time_to_graph(d - graph_start),
279 static void svg_pss_graph(void)
281 struct ps_struct *ps;
284 svg("\n\n<!-- Pss memory size graph -->\n");
286 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
288 /* vsize 1000 == 1000mb */
290 /* draw some hlines for usable memory sizes */
291 for (i = 100000; i < 1000000; i += 100000) {
292 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
295 time_to_graph(sampletime[samples-1] - graph_start),
297 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
298 time_to_graph(sampletime[samples-1] - graph_start) + 5,
299 kb_to_graph(i), (1000000 - i) / 1000);
303 /* now plot the graph itself */
304 for (i = 1; i < samples ; i++) {
311 /* put all the small pss blocks into the bottom */
313 while (ps->next_ps) {
317 if (ps->sample[i].pss <= (100 * scale_y))
318 top += ps->sample[i].pss;
320 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
322 time_to_graph(sampletime[i - 1] - graph_start),
323 kb_to_graph(1000000.0 - top),
324 time_to_graph(sampletime[i] - sampletime[i - 1]),
325 kb_to_graph(top - bottom));
329 /* now plot the ones that are of significant size */
331 while (ps->next_ps) {
335 /* don't draw anything smaller than 2mb */
336 if (ps->sample[i].pss > (100 * scale_y)) {
337 top = bottom + ps->sample[i].pss;
338 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
339 colorwheel[ps->pid % 12],
340 time_to_graph(sampletime[i - 1] - graph_start),
341 kb_to_graph(1000000.0 - top),
342 time_to_graph(sampletime[i] - sampletime[i - 1]),
343 kb_to_graph(top - bottom));
349 /* overlay all the text labels */
350 for (i = 1; i < samples ; i++) {
357 /* put all the small pss blocks into the bottom */
359 while (ps->next_ps) {
363 if (ps->sample[i].pss <= (100 * scale_y))
364 top += ps->sample[i].pss;
369 /* now plot the ones that are of significant size */
371 while (ps->next_ps) {
375 /* don't draw anything smaller than 2mb */
376 if (ps->sample[i].pss > (100 * scale_y)) {
377 top = bottom + ps->sample[i].pss;
378 /* draw a label with the process / PID */
379 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
380 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
381 time_to_graph(sampletime[i] - graph_start),
382 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
390 /* debug output - full data dump */
391 svg("\n\n<!-- PSS map - csv format -->\n");
393 while (ps->next_ps) {
397 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
398 for (i = 0; i < samples ; i++) {
399 svg("%d," , ps->sample[i].pss);
406 static void svg_io_bi_bar(void)
413 svg("<!-- IO utilization graph - In -->\n");
415 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
418 * calculate rounding range
420 * We need to round IO data since IO block data is not updated on
421 * each poll. Applying a smoothing function loses some burst data,
422 * so keep the smoothing range short.
424 range = 0.25 / (1.0 / hz);
426 range = 2.0; /* no smoothing */
428 /* surrounding box */
431 /* find the max IO first */
432 for (i = 1; i < samples; i++) {
437 start = max(i - ((range / 2) - 1), 0);
438 stop = min(i + (range / 2), samples - 1);
440 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
446 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
453 for (i = 1; i < samples; i++) {
459 start = max(i - ((range / 2) - 1), 0);
460 stop = min(i + (range / 2), samples);
462 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
467 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
468 time_to_graph(sampletime[i - 1] - graph_start),
469 (scale_y * 5) - (pbi * (scale_y * 5)),
470 time_to_graph(sampletime[i] - sampletime[i - 1]),
471 pbi * (scale_y * 5));
473 /* labels around highest value */
475 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
476 time_to_graph(sampletime[i] - graph_start) + 5,
477 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
478 max / 1024.0 / (interval / 1000000000.0));
483 static void svg_io_bo_bar(void)
490 svg("<!-- IO utilization graph - out -->\n");
492 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
495 * calculate rounding range
497 * We need to round IO data since IO block data is not updated on
498 * each poll. Applying a smoothing function loses some burst data,
499 * so keep the smoothing range short.
501 range = 0.25 / (1.0 / hz);
503 range = 2.0; /* no smoothing */
505 /* surrounding box */
508 /* find the max IO first */
509 for (i = 1; i < samples; i++) {
514 start = max(i - ((range / 2) - 1), 0);
515 stop = min(i + (range / 2), samples - 1);
517 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
521 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
530 for (i = 1; i < samples; i++) {
536 start = max(i - ((range / 2) - 1), 0);
537 stop = min(i + (range / 2), samples);
539 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
544 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
545 time_to_graph(sampletime[i - 1] - graph_start),
546 (scale_y * 5) - (pbo * (scale_y * 5)),
547 time_to_graph(sampletime[i] - sampletime[i - 1]),
548 pbo * (scale_y * 5));
550 /* labels around highest bo value */
552 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
553 time_to_graph(sampletime[i] - graph_start) + 5,
554 ((scale_y * 5) - (pbo * (scale_y * 5))),
555 max / 1024.0 / (interval / 1000000000.0));
561 static void svg_cpu_bar(void)
565 svg("<!-- CPU utilization graph -->\n");
567 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
568 /* surrounding box */
571 /* bars for each sample, proportional to the CPU util. */
572 for (i = 1; i < samples; i++) {
579 for (c = 0; c < cpus; c++)
580 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
582 trt = trt / 1000000000.0;
584 trt = trt / (double)cpus;
587 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
593 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
594 time_to_graph(sampletime[i - 1] - graph_start),
595 (scale_y * 5) - (ptrt * (scale_y * 5)),
596 time_to_graph(sampletime[i] - sampletime[i - 1]),
597 ptrt * (scale_y * 5));
602 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 ((scale_y * 5) - (ptwt * (scale_y * 5))),
638 time_to_graph(sampletime[i] - sampletime[i - 1]),
639 ptwt * (scale_y * 5));
645 static void svg_entropy_bar(void)
649 svg("<!-- entropy pool graph -->\n");
651 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
652 /* surrounding box */
655 /* bars for each sample, scale 0-4096 */
656 for (i = 1; i < samples; i++) {
657 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
658 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
659 time_to_graph(sampletime[i - 1] - graph_start),
660 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
661 time_to_graph(sampletime[i] - sampletime[i - 1]),
662 (entropy_avail[i] / 4096.) * (scale_y * 5));
667 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;
698 static int ps_filter(struct ps_struct *ps)
703 /* can't draw data when there is only 1 sample (need start + stop) */
704 if (ps->first == ps->last)
707 /* don't filter kthreadd */
711 /* drop stuff that doesn't use any real CPU time */
712 if (ps->total <= 0.001)
719 static void svg_do_initcall(int count_only)
721 FILE _cleanup_pclose_ *f = NULL;
727 /* can't plot initcall when disabled or in relative mode */
728 if (!initcall || relative) {
734 svg("<!-- initcall -->\n");
736 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
737 /* surrounding box */
738 svg_graph_box(kcount);
744 * Initcall graphing - parses dmesg buffer and displays kernel threads
745 * This somewhat uses the same methods and scaling to show processes
746 * but looks a lot simpler. It's overlaid entirely onto the PS graph
750 f = popen("dmesg", "r");
759 if (fgets(l, sizeof(l) - 1, f) == NULL)
762 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
763 &t, func, &ret, &usecs);
765 /* also parse initcalls done by module loading */
766 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
767 &t, func, &ret, &usecs);
772 /* chop the +0xXX/0xXX stuff */
773 while(func[z] != '+')
778 /* filter out irrelevant stuff */
784 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
785 func, t, usecs, ret);
791 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
792 time_to_graph(t - (usecs / 1000000.0)),
794 time_to_graph(usecs / 1000000.0),
798 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
799 time_to_graph(t - (usecs / 1000000.0)) + 5,
800 ps_to_graph(kcount) + 15,
809 static void svg_ps_bars(void)
811 struct ps_struct *ps;
817 svg("<!-- Process graph -->\n");
819 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
821 /* surrounding box */
822 svg_graph_box(pcount);
824 /* pass 2 - ps boxes */
826 while ((ps = get_next_ps(ps))) {
833 /* leave some trace of what we actually filtered etc. */
834 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->name, ps->pid,
835 ps->ppid, ps->total);
837 /* it would be nice if we could use exec_start from /proc/pid/sched,
838 * but it's unreliable and gives bogus numbers */
839 starttime = sampletime[ps->first];
841 if (!ps_filter(ps)) {
842 /* remember where _to_ our children need to draw a line */
843 ps->pos_x = time_to_graph(starttime - graph_start);
844 ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
846 /* hook children to our parent coords instead */
847 ps->pos_x = ps->parent->pos_x;
848 ps->pos_y = ps->parent->pos_y;
850 /* if this is the last child, we might still need to draw a connecting line */
851 if ((!ps->next) && (ps->parent))
852 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
854 ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
860 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
861 time_to_graph(starttime - graph_start),
863 time_to_graph(sampletime[ps->last] - starttime),
866 /* paint cpu load over these */
867 for (t = ps->first + 1; t < ps->last; t++) {
871 /* calculate over interval */
872 rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
873 wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
875 prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
876 wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
878 /* this can happen if timekeeping isn't accurate enough */
884 if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
887 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
888 time_to_graph(sampletime[t - 1] - graph_start),
890 time_to_graph(sampletime[t] - sampletime[t - 1]),
893 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
894 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
895 time_to_graph(sampletime[t - 1] - graph_start),
896 ps_to_graph(j + (1.0 - prt)),
897 time_to_graph(sampletime[t] - sampletime[t - 1]),
901 /* determine where to display the process name */
902 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
903 /* too small to fit label inside the box */
908 /* text label of process name */
909 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i] <tspan class=\"run\">%.03fs</tspan></text>\n",
910 time_to_graph(sampletime[w] - graph_start) + 5.0,
911 ps_to_graph(j) + 14.0,
914 (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
915 /* paint lines to the parent process */
917 /* horizontal part */
918 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
919 time_to_graph(starttime - graph_start),
920 ps_to_graph(j) + 10.0,
922 ps_to_graph(j) + 10.0);
924 /* one vertical line connecting all the horizontal ones up */
926 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
928 ps_to_graph(j) + 10.0,
933 j++; /* count boxes */
938 /* last pass - determine when idle */
940 /* make sure we start counting from the point where we actually have
941 * data: assume that bootchart's first sample is when data started
944 while (ps->next_ps) {
950 for (i = ps->first; i < samples - (hz / 2); i++) {
955 /* subtract bootchart cpu utilization from total */
957 for (c = 0; c < cpus; c++)
958 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
959 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
962 * our definition of "idle":
964 * if for (hz / 2) we've used less CPU than (interval / 2) ...
965 * defaults to 4.0%, which experimentally, is where atom idles
967 if ((crt - brt) < (interval / 2.0)) {
968 idletime = sampletime[i] - graph_start;
969 svg("\n<!-- idle detected at %.03f seconds -->\n",
971 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
972 time_to_graph(idletime),
974 time_to_graph(idletime),
975 ps_to_graph(pcount) + scale_y);
976 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
977 time_to_graph(idletime) + 5.0,
978 ps_to_graph(pcount) + scale_y,
986 static void svg_top_ten_cpu(void)
988 struct ps_struct *top[10];
989 struct ps_struct emptyps;
990 struct ps_struct *ps;
993 memset(&emptyps, 0, sizeof(struct ps_struct));
994 for (n=0; n < 10; n++)
997 /* walk all ps's and setup ptrs */
999 while ((ps = get_next_ps(ps))) {
1000 for (n = 0; n < 10; n++) {
1001 if (ps->total <= top[n]->total)
1003 /* cascade insert */
1004 for (m = 9; m > n; m--)
1011 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1012 for (n = 0; n < 10; n++)
1013 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
1021 static void svg_top_ten_pss(void)
1023 struct ps_struct *top[10];
1024 struct ps_struct emptyps;
1025 struct ps_struct *ps;
1028 memset(&emptyps, 0, sizeof(struct ps_struct));
1029 for (n=0; n < 10; n++)
1032 /* walk all ps's and setup ptrs */
1034 while ((ps = get_next_ps(ps))) {
1035 for (n = 0; n < 10; n++) {
1036 if (ps->pss_max <= top[n]->pss_max)
1038 /* cascade insert */
1039 for (m = 9; m > n; m--)
1046 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1047 for (n = 0; n < 10; n++)
1048 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
1058 struct ps_struct *ps;
1060 memset(&str, 0, sizeof(str));
1064 /* count initcall thread count first */
1066 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1068 /* then count processes */
1069 while ((ps = get_next_ps(ps))) {
1075 psize = ps_to_graph(pcount) + (scale_y * 2);
1077 esize = (entropy ? scale_y * 7 : 0);
1079 /* after this, we can draw the header with proper sizing */
1082 svg("<g transform=\"translate(10,400)\">\n");
1086 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1090 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1094 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1099 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1104 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1108 svg("<g transform=\"translate(10, 0)\">\n");
1112 svg("<g transform=\"translate(10,200)\">\n");
1117 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1123 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1127 svg("<g transform=\"translate(410,200)\">\n");