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>
32 #include "bootchart.h"
37 #define time_to_graph(t) ((t) * scale_x)
38 #define ps_to_graph(n) ((n) * scale_y)
39 #define kb_to_graph(m) ((m) * scale_y * 0.0001)
40 #define to_color(n) (192.0 - ((n) * 192.0))
42 #define max(x, y) (((x) > (y)) ? (x) : (y))
43 #define min(x, y) (((x) < (y)) ? (x) : (y))
45 static char str[8092];
47 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
49 static const char *colorwheel[12] = {
50 "rgb(255,32,32)", // red
51 "rgb(32,192,192)", // cyan
52 "rgb(255,128,32)", // orange
53 "rgb(128,32,192)", // blue-violet
54 "rgb(255,255,32)", // yellow
55 "rgb(192,32,128)", // red-violet
56 "rgb(32,255,32)", // green
57 "rgb(255,64,32)", // red-orange
58 "rgb(32,32,255)", // blue
59 "rgb(255,192,32)", // yellow-orange
60 "rgb(192,32,192)", // violet
61 "rgb(32,192,32)" // yellow-green
64 static double idletime = -1.0;
65 static int pfiltered = 0;
66 static int pcount = 0;
67 static int kcount = 0;
68 static float psize = 0;
69 static float ksize = 0;
70 static float esize = 0;
73 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 + (scale_y * 30.0) /* base graphs and title */
84 + (pss ? (100.0 * scale_y) + (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:///var/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", hz, len);
105 svg("<!-- x=\"%f\" y=\"%f\" -->\n", scale_x, scale_y);
106 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", relative, filter);
107 svg("<!-- p=\"%d\" e=\"%d\" -->\n", pss, entropy);
108 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", output_path, 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");
141 static void svg_title(void)
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";
150 char build[256] = "Unknown";
156 /* grab /proc/cmdline */
157 f = fopen("/proc/cmdline", "r");
159 if (!fgets(cmdline, 255, f))
160 sprintf(cmdline, "Unknown");
164 /* extract root fs so we can find disk model name in sysfs */
165 c = strstr(cmdline, "root=/dev/");
167 strncpy(rootbdev, &c[10], 3);
170 sprintf(filename, "/sys/block/%s/device/model", rootbdev);
171 f = fopen(filename, "r");
173 if (!fgets(model, 255, f))
174 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 f = fopen("/proc/cpuinfo", "r");
189 while (fgets(buf, 255, f)) {
190 if (strstr(buf, "model name")) {
191 strncpy(cpu, &buf[13], 255);
198 /* Build - 1st line from /etc/system-release */
199 f = fopen("/etc/system-release", "r");
201 if (fgets(buf, 255, f))
202 strncpy(build, buf, 255);
206 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
208 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
209 uts.sysname, uts.release, uts.version, uts.machine);
210 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
212 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
216 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
218 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
219 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
222 svg("%.03fs", idletime);
226 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",
227 hz, len, overrun, pscount, pfiltered);
231 static void svg_graph_box(int height)
236 /* outside box, fill */
237 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
239 time_to_graph(sampletime[samples-1] - graph_start),
240 ps_to_graph(height));
242 for (d = graph_start; d <= sampletime[samples-1];
243 d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
244 /* lines for each second */
246 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
247 time_to_graph(d - graph_start),
248 time_to_graph(d - graph_start),
249 ps_to_graph(height));
250 else if (i % 10 == 0)
251 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
252 time_to_graph(d - graph_start),
253 time_to_graph(d - graph_start),
254 ps_to_graph(height));
256 svg(" <line class=\"sec01\" 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));
263 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
264 time_to_graph(d - graph_start),
273 static void svg_pss_graph(void)
275 struct ps_struct *ps;
278 svg("\n\n<!-- Pss memory size graph -->\n");
280 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
282 /* vsize 1000 == 1000mb */
284 /* draw some hlines for usable memory sizes */
285 for (i = 100000; i < 1000000; i += 100000) {
286 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
289 time_to_graph(sampletime[samples-1] - graph_start),
291 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
292 time_to_graph(sampletime[samples-1] - graph_start) + 5,
293 kb_to_graph(i), (1000000 - i) / 1000);
297 /* now plot the graph itself */
298 for (i = 1; i < samples ; i++) {
305 /* put all the small pss blocks into the bottom */
307 while (ps->next_ps) {
311 if (ps->sample[i].pss <= (100 * scale_y))
312 top += ps->sample[i].pss;
314 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
316 time_to_graph(sampletime[i - 1] - graph_start),
317 kb_to_graph(1000000.0 - top),
318 time_to_graph(sampletime[i] - sampletime[i - 1]),
319 kb_to_graph(top - bottom));
323 /* now plot the ones that are of significant size */
325 while (ps->next_ps) {
329 /* don't draw anything smaller than 2mb */
330 if (ps->sample[i].pss > (100 * scale_y)) {
331 top = bottom + ps->sample[i].pss;
332 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
333 colorwheel[ps->pid % 12],
334 time_to_graph(sampletime[i - 1] - graph_start),
335 kb_to_graph(1000000.0 - top),
336 time_to_graph(sampletime[i] - sampletime[i - 1]),
337 kb_to_graph(top - bottom));
343 /* overlay all the text labels */
344 for (i = 1; i < samples ; i++) {
351 /* put all the small pss blocks into the bottom */
353 while (ps->next_ps) {
357 if (ps->sample[i].pss <= (100 * scale_y))
358 top += ps->sample[i].pss;
363 /* now plot the ones that are of significant size */
365 while (ps->next_ps) {
369 /* don't draw anything smaller than 2mb */
370 if (ps->sample[i].pss > (100 * scale_y)) {
371 top = bottom + ps->sample[i].pss;
372 /* draw a label with the process / PID */
373 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
374 svg(" <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
375 time_to_graph(sampletime[i] - graph_start),
376 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
384 /* debug output - full data dump */
385 svg("\n\n<!-- PSS map - csv format -->\n");
387 while (ps->next_ps) {
391 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
392 for (i = 0; i < samples ; i++) {
393 svg("%d," , ps->sample[i].pss);
400 static void svg_io_bi_bar(void)
407 svg("<!-- IO utilization graph - In -->\n");
409 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
412 * calculate rounding range
414 * We need to round IO data since IO block data is not updated on
415 * each poll. Applying a smoothing function loses some burst data,
416 * so keep the smoothing range short.
418 range = 0.25 / (1.0 / hz);
420 range = 2.0; /* no smoothing */
422 /* surrounding box */
425 /* find the max IO first */
426 for (i = 1; i < samples; i++) {
431 start = max(i - ((range / 2) - 1), 0);
432 stop = min(i + (range / 2), samples - 1);
434 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
440 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
447 for (i = 1; i < samples; i++) {
453 start = max(i - ((range / 2) - 1), 0);
454 stop = min(i + (range / 2), samples);
456 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
461 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
462 time_to_graph(sampletime[i - 1] - graph_start),
463 (scale_y * 5) - (pbi * (scale_y * 5)),
464 time_to_graph(sampletime[i] - sampletime[i - 1]),
465 pbi * (scale_y * 5));
467 /* labels around highest value */
469 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
470 time_to_graph(sampletime[i] - graph_start) + 5,
471 ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
472 max / 1024.0 / (interval / 1000000000.0));
477 static void svg_io_bo_bar(void)
484 svg("<!-- IO utilization graph - out -->\n");
486 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
489 * calculate rounding range
491 * We need to round IO data since IO block data is not updated on
492 * each poll. Applying a smoothing function loses some burst data,
493 * so keep the smoothing range short.
495 range = 0.25 / (1.0 / hz);
497 range = 2.0; /* no smoothing */
499 /* surrounding box */
502 /* find the max IO first */
503 for (i = 1; i < samples; i++) {
508 start = max(i - ((range / 2) - 1), 0);
509 stop = min(i + (range / 2), samples - 1);
511 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
515 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
524 for (i = 1; i < samples; i++) {
530 start = max(i - ((range / 2) - 1), 0);
531 stop = min(i + (range / 2), samples);
533 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
538 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
539 time_to_graph(sampletime[i - 1] - graph_start),
540 (scale_y * 5) - (pbo * (scale_y * 5)),
541 time_to_graph(sampletime[i] - sampletime[i - 1]),
542 pbo * (scale_y * 5));
544 /* labels around highest bo value */
546 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
547 time_to_graph(sampletime[i] - graph_start) + 5,
548 ((scale_y * 5) - (pbo * (scale_y * 5))),
549 max / 1024.0 / (interval / 1000000000.0));
555 static void svg_cpu_bar(void)
559 svg("<!-- CPU utilization graph -->\n");
561 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
562 /* surrounding box */
565 /* bars for each sample, proportional to the CPU util. */
566 for (i = 1; i < samples; i++) {
573 for (c = 0; c < cpus; c++)
574 trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
576 trt = trt / 1000000000.0;
578 trt = trt / (double)cpus;
581 ptrt = trt / (sampletime[i] - sampletime[i - 1]);
587 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
588 time_to_graph(sampletime[i - 1] - graph_start),
589 (scale_y * 5) - (ptrt * (scale_y * 5)),
590 time_to_graph(sampletime[i] - sampletime[i - 1]),
591 ptrt * (scale_y * 5));
596 static void svg_wait_bar(void)
600 svg("<!-- Wait time aggregation box -->\n");
602 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
604 /* surrounding box */
607 /* bars for each sample, proportional to the CPU util. */
608 for (i = 1; i < samples; i++) {
615 for (c = 0; c < cpus; c++)
616 twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
618 twt = twt / 1000000000.0;
620 twt = twt / (double)cpus;
623 ptwt = twt / (sampletime[i] - sampletime[i - 1]);
629 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
630 time_to_graph(sampletime[i - 1] - graph_start),
631 ((scale_y * 5) - (ptwt * (scale_y * 5))),
632 time_to_graph(sampletime[i] - sampletime[i - 1]),
633 ptwt * (scale_y * 5));
639 static void svg_entropy_bar(void)
643 svg("<!-- entropy pool graph -->\n");
645 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
646 /* surrounding box */
649 /* bars for each sample, scale 0-4096 */
650 for (i = 1; i < samples; i++) {
651 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
652 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
653 time_to_graph(sampletime[i - 1] - graph_start),
654 ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
655 time_to_graph(sampletime[i] - sampletime[i - 1]),
656 (entropy_avail[i] / 4096.) * (scale_y * 5));
661 static struct ps_struct *get_next_ps(struct ps_struct *ps)
664 * walk the list of processes and return the next one to be
678 /* go back for parent siblings */
681 if (ps->parent->next)
682 return ps->parent->next;
692 static int ps_filter(struct ps_struct *ps)
697 /* can't draw data when there is only 1 sample (need start + stop) */
698 if (ps->first == ps->last)
701 /* don't filter kthreadd */
705 /* drop stuff that doesn't use any real CPU time */
706 if (ps->total <= 0.001)
713 static void svg_do_initcall(int count_only)
715 FILE _cleanup_pclose_ *f = NULL;
721 /* can't plot initcall when disabled or in relative mode */
722 if (!initcall || relative) {
728 svg("<!-- initcall -->\n");
730 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
731 /* surrounding box */
732 svg_graph_box(kcount);
738 * Initcall graphing - parses dmesg buffer and displays kernel threads
739 * This somewhat uses the same methods and scaling to show processes
740 * but looks a lot simpler. It's overlaid entirely onto the PS graph
744 f = popen("dmesg", "r");
753 if (fgets(l, sizeof(l) - 1, f) == NULL)
756 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
757 &t, func, &ret, &usecs);
759 /* also parse initcalls done by module loading */
760 c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
761 &t, func, &ret, &usecs);
766 /* chop the +0xXX/0xXX stuff */
767 while(func[z] != '+')
772 /* filter out irrelevant stuff */
778 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
779 func, t, usecs, ret);
785 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
786 time_to_graph(t - (usecs / 1000000.0)),
788 time_to_graph(usecs / 1000000.0),
792 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
793 time_to_graph(t - (usecs / 1000000.0)) + 5,
794 ps_to_graph(kcount) + 15,
803 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))) {
827 /* leave some trace of what we actually filtered etc. */
828 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->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\">%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 - (hz / 2); i++) {
949 /* subtract bootchart cpu utilization from total */
951 for (c = 0; c < cpus; c++)
952 crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
953 brt = ps->sample[i + ((int)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) + 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) + scale_y,
980 static void svg_top_ten_cpu(void)
982 struct ps_struct *top[10];
983 struct ps_struct emptyps;
984 struct ps_struct *ps;
987 memset(&emptyps, 0, sizeof(struct ps_struct));
988 for (n=0; n < 10; n++)
991 /* walk all ps's and setup ptrs */
993 while ((ps = get_next_ps(ps))) {
994 for (n = 0; n < 10; n++) {
995 if (ps->total <= top[n]->total)
998 for (m = 9; m > n; m--)
1005 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1006 for (n = 0; n < 10; n++)
1007 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
1015 static void svg_top_ten_pss(void)
1017 struct ps_struct *top[10];
1018 struct ps_struct emptyps;
1019 struct ps_struct *ps;
1022 memset(&emptyps, 0, sizeof(struct ps_struct));
1023 for (n=0; n < 10; n++)
1026 /* walk all ps's and setup ptrs */
1028 while ((ps = get_next_ps(ps))) {
1029 for (n = 0; n < 10; n++) {
1030 if (ps->pss_max <= top[n]->pss_max)
1032 /* cascade insert */
1033 for (m = 9; m > n; m--)
1040 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1041 for (n = 0; n < 10; n++)
1042 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
1052 struct ps_struct *ps;
1054 memset(&str, 0, sizeof(str));
1058 /* count initcall thread count first */
1060 ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1062 /* then count processes */
1063 while ((ps = get_next_ps(ps))) {
1069 psize = ps_to_graph(pcount) + (scale_y * 2);
1071 esize = (entropy ? scale_y * 7 : 0);
1073 /* after this, we can draw the header with proper sizing */
1076 svg("<g transform=\"translate(10,400)\">\n");
1080 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1084 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1088 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1093 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1098 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1102 svg("<g transform=\"translate(10, 0)\">\n");
1106 svg("<g transform=\"translate(10,200)\">\n");
1111 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1117 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1121 svg("<g transform=\"translate(410,200)\">\n");