chiark / gitweb /
bootchart: more fixes for bootchart in the initramfs
[elogind.git] / src / bootchart / svg.c
1 /***
2   bootchart.c - This file is part of systemd-bootchart
3
4   Copyright (C) 2009-2013 Intel Coproration
5
6   Authors:
7     Auke Kok <auke-jan.h.kok@intel.com>
8
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.
13
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.
18
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/>.
21  ***/
22
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <limits.h>
29 #include <unistd.h>
30 #include <sys/utsname.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33
34 #include "bootchart.h"
35 #include "util.h"
36 #include "macro.h"
37
38
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))
43
44 #define max(x, y) (((x) > (y)) ? (x) : (y))
45 #define min(x, y) (((x) < (y)) ? (x) : (y))
46
47 static char str[8092];
48
49 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
50
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
64 };
65
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;
73
74
75 static void svg_header(void)
76 {
77         float w;
78         float h;
79
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);
83
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;
88
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");
92
93         //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
94         svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
95             w, h);
96         svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
97
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");
104
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);
111
112         /* style sheet */
113         svg("<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n");
114
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");
130
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");
137
138         svg("    ]]>\n   </style>\n</defs>\n\n");
139
140 }
141
142
143 static void svg_title(void)
144 {
145         char cmdline[256] = "";
146         char filename[PATH_MAX];
147         char buf[256];
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";
153         char *c;
154         FILE *f;
155         time_t t;
156         int fd;
157         struct utsname uts;
158
159         /* grab /proc/cmdline */
160         fd = openat(procfd, "cmdline", O_RDONLY);
161         f = fdopen(fd, "r");
162         if (f) {
163                 if (!fgets(cmdline, 255, f))
164                         sprintf(cmdline, "Unknown");
165                 fclose(f);
166         }
167
168         /* extract root fs so we can find disk model name in sysfs */
169         c = strstr(cmdline, "root=/dev/");
170         if (c) {
171                 strncpy(rootbdev, &c[10], 3);
172                 rootbdev[3] = '\0';
173         }
174         sprintf(filename, "block/%s/device/model", rootbdev);
175         fd = openat(sysfd, filename, O_RDONLY);
176         f = fdopen(fd, "r");
177         if (f) {
178                 if (!fgets(model, 255, f))
179                         fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
180                 fclose(f);
181         }
182
183         /* various utsname parameters */
184         if (uname(&uts))
185                 fprintf(stderr, "Error getting uname info\n");
186
187         /* date */
188         t = time(NULL);
189         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
190
191         /* CPU type */
192         fd = openat(procfd, "cpuinfo", O_RDONLY);
193         f = fdopen(fd, "r");
194         if (f) {
195                 while (fgets(buf, 255, f)) {
196                         if (strstr(buf, "model name")) {
197                                 strncpy(cpu, &buf[13], 255);
198                                 break;
199                         }
200                 }
201                 fclose(f);
202         }
203
204         /* Build - 1st line from /etc/system-release */
205         f = fopen("/etc/system-release", "r");
206         if (f) {
207                 if (fgets(buf, 255, f))
208                         strncpy(build, buf, 255);
209                 fclose(f);
210         }
211
212         svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
213             uts.nodename, date);
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",
217             cpu);
218         svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
219             model);
220         svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
221             cmdline);
222         svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
223             build);
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: ");
226
227         if (idletime >= 0.0)
228                 svg("%.03fs", idletime);
229         else
230                 svg("Not detected");
231         svg("</text>\n");
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);
234 }
235
236
237 static void svg_graph_box(int height)
238 {
239         double d = 0.0;
240         int i = 0;
241
242         /* outside box, fill */
243         svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
244             time_to_graph(0.0),
245             time_to_graph(sampletime[samples-1] - graph_start),
246             ps_to_graph(height));
247
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 */
251                 if (i % 50 == 0)
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));
261                 else
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));
266
267                 /* time label */
268                 if (i % 10 == 0)
269                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
270                             time_to_graph(d - graph_start),
271                             -5.0,
272                             d - graph_start);
273
274                 i++;
275         }
276 }
277
278
279 static void svg_pss_graph(void)
280 {
281         struct ps_struct *ps;
282         int i;
283
284         svg("\n\n<!-- Pss memory size graph -->\n");
285
286         svg("\n  <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
287
288         /* vsize 1000 == 1000mb */
289         svg_graph_box(100);
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",
293                         time_to_graph(.0),
294                         kb_to_graph(i),
295                         time_to_graph(sampletime[samples-1] - graph_start),
296                         kb_to_graph(i));
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);
300         }
301         svg("\n");
302
303         /* now plot the graph itself */
304         for (i = 1; i < samples ; i++) {
305                 int bottom;
306                 int top;
307
308                 bottom = 0;
309                 top = 0;
310
311                 /* put all the small pss blocks into the bottom */
312                 ps = ps_first;
313                 while (ps->next_ps) {
314                         ps = ps->next_ps;
315                         if (!ps)
316                                 continue;
317                         if (ps->sample[i].pss <= (100 * scale_y))
318                                 top += ps->sample[i].pss;
319                 };
320                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
321                     "rgb(64,64,64)",
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));
326
327                 bottom = top;
328
329                 /* now plot the ones that are of significant size */
330                 ps = ps_first;
331                 while (ps->next_ps) {
332                         ps = ps->next_ps;
333                         if (!ps)
334                                 continue;
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));
344                                 bottom = top;
345                         }
346                 }
347         }
348
349         /* overlay all the text labels */
350         for (i = 1; i < samples ; i++) {
351                 int bottom;
352                 int top;
353
354                 bottom = 0;
355                 top = 0;
356
357                 /* put all the small pss blocks into the bottom */
358                 ps = ps_first;
359                 while (ps->next_ps) {
360                         ps = ps->next_ps;
361                         if (!ps)
362                                 continue;
363                         if (ps->sample[i].pss <= (100 * scale_y))
364                                 top += ps->sample[i].pss;
365                 };
366
367                 bottom = top;
368
369                 /* now plot the ones that are of significant size */
370                 ps = ps_first;
371                 while (ps->next_ps) {
372                         ps = ps->next_ps;
373                         if (!ps)
374                                 continue;
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)),
383                                             ps->name,
384                                             ps->pid);
385                                 bottom = top;
386                         }
387                 }
388         }
389
390         /* debug output - full data dump */
391         svg("\n\n<!-- PSS map - csv format -->\n");
392         ps = ps_first;
393         while (ps->next_ps) {
394                 ps = ps->next_ps;
395                 if (!ps)
396                         continue;
397                 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
398                 for (i = 0; i < samples ; i++) {
399                         svg("%d," , ps->sample[i].pss);
400                 }
401                 svg(" -->\n");
402         }
403
404 }
405
406 static void svg_io_bi_bar(void)
407 {
408         double max = 0.0;
409         double range;
410         int max_here = 0;
411         int i;
412
413         svg("<!-- IO utilization graph - In -->\n");
414
415         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
416
417         /*
418          * calculate rounding range
419          *
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.
423          */
424         range = 0.25 / (1.0 / hz);
425         if (range < 2.0)
426                 range = 2.0; /* no smoothing */
427
428         /* surrounding box */
429         svg_graph_box(5);
430
431         /* find the max IO first */
432         for (i = 1; i < samples; i++) {
433                 int start;
434                 int stop;
435                 double tot;
436
437                 start = max(i - ((range / 2) - 1), 0);
438                 stop = min(i + (range / 2), samples - 1);
439
440                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
441                       / (stop - start);
442                 if (tot > max) {
443                         max = tot;
444                         max_here = i;
445                 }
446                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
447                       / (stop - start);
448                 if (tot > max)
449                         max = tot;
450         }
451
452         /* plot bi */
453         for (i = 1; i < samples; i++) {
454                 int start;
455                 int stop;
456                 double tot;
457                 double pbi;
458
459                 start = max(i - ((range / 2) - 1), 0);
460                 stop = min(i + (range / 2), samples);
461
462                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
463                       / (stop - start);
464                 pbi = tot / max;
465
466                 if (pbi > 0.001)
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));
472
473                 /* labels around highest value */
474                 if (i == max_here) {
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));
479                 }
480         }
481 }
482
483 static void svg_io_bo_bar(void)
484 {
485         double max = 0.0;
486         double range;
487         int max_here = 0;
488         int i;
489
490         svg("<!-- IO utilization graph - out -->\n");
491
492         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
493
494         /*
495          * calculate rounding range
496          *
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.
500          */
501         range = 0.25 / (1.0 / hz);
502         if (range < 2.0)
503                 range = 2.0; /* no smoothing */
504
505         /* surrounding box */
506         svg_graph_box(5);
507
508         /* find the max IO first */
509         for (i = 1; i < samples; i++) {
510                 int start;
511                 int stop;
512                 double tot;
513
514                 start = max(i - ((range / 2) - 1), 0);
515                 stop = min(i + (range / 2), samples - 1);
516
517                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
518                       / (stop - start);
519                 if (tot > max)
520                         max = tot;
521                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
522                       / (stop - start);
523                 if (tot > max) {
524                         max = tot;
525                         max_here = i;
526                 }
527         }
528
529         /* plot bo */
530         for (i = 1; i < samples; i++) {
531                 int start;
532                 int stop;
533                 double tot;
534                 double pbo;
535
536                 start = max(i - ((range / 2) - 1), 0);
537                 stop = min(i + (range / 2), samples);
538
539                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
540                       / (stop - start);
541                 pbo = tot / max;
542
543                 if (pbo > 0.001)
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));
549
550                 /* labels around highest bo value */
551                 if (i == max_here) {
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));
556                 }
557         }
558 }
559
560
561 static void svg_cpu_bar(void)
562 {
563         int i;
564
565         svg("<!-- CPU utilization graph -->\n");
566
567         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
568         /* surrounding box */
569         svg_graph_box(5);
570
571         /* bars for each sample, proportional to the CPU util. */
572         for (i = 1; i < samples; i++) {
573                 int c;
574                 double trt;
575                 double ptrt;
576
577                 ptrt = trt = 0.0;
578
579                 for (c = 0; c < cpus; c++)
580                         trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
581
582                 trt = trt / 1000000000.0;
583
584                 trt = trt / (double)cpus;
585
586                 if (trt > 0.0)
587                         ptrt = trt / (sampletime[i] - sampletime[i - 1]);
588
589                 if (ptrt > 1.0)
590                         ptrt = 1.0;
591
592                 if (ptrt > 0.001) {
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));
598                 }
599         }
600 }
601
602 static void svg_wait_bar(void)
603 {
604         int i;
605
606         svg("<!-- Wait time aggregation box -->\n");
607
608         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
609
610         /* surrounding box */
611         svg_graph_box(5);
612
613         /* bars for each sample, proportional to the CPU util. */
614         for (i = 1; i < samples; i++) {
615                 int c;
616                 double twt;
617                 double ptwt;
618
619                 ptwt = twt = 0.0;
620
621                 for (c = 0; c < cpus; c++)
622                         twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
623
624                 twt = twt / 1000000000.0;
625
626                 twt = twt / (double)cpus;
627
628                 if (twt > 0.0)
629                         ptwt = twt / (sampletime[i] - sampletime[i - 1]);
630
631                 if (ptwt > 1.0)
632                         ptwt = 1.0;
633
634                 if (ptwt > 0.001) {
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));
640                 }
641         }
642 }
643
644
645 static void svg_entropy_bar(void)
646 {
647         int i;
648
649         svg("<!-- entropy pool graph -->\n");
650
651         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
652         /* surrounding box */
653         svg_graph_box(5);
654
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));
663         }
664 }
665
666
667 static struct ps_struct *get_next_ps(struct ps_struct *ps)
668 {
669         /*
670          * walk the list of processes and return the next one to be
671          * painted
672          */
673         if (ps == ps_first)
674                 return ps->next_ps;
675
676         /* go deep */
677         if (ps->children)
678                 return ps->children;
679
680         /* find siblings */
681         if (ps->next)
682                 return ps->next;
683
684         /* go back for parent siblings */
685         while (1) {
686                 if (ps->parent)
687                         if (ps->parent->next)
688                                 return ps->parent->next;
689                 ps = ps->parent;
690                 if (!ps)
691                         return ps;
692         }
693
694         return NULL;
695 }
696
697
698 static int ps_filter(struct ps_struct *ps)
699 {
700         if (!filter)
701                 return 0;
702
703         /* can't draw data when there is only 1 sample (need start + stop) */
704         if (ps->first == ps->last)
705                 return -1;
706
707         /* don't filter kthreadd */
708         if (ps->pid == 2)
709                 return 0;
710
711         /* drop stuff that doesn't use any real CPU time */
712         if (ps->total <= 0.001)
713                 return -1;
714
715         return 0;
716 }
717
718
719 static void svg_do_initcall(int count_only)
720 {
721         FILE _cleanup_pclose_ *f = NULL;
722         double t;
723         char func[256];
724         int ret;
725         int usecs;
726
727         /* can't plot initcall when disabled or in relative mode */
728         if (!initcall || relative) {
729                 kcount = 0;
730                 return;
731         }
732
733         if (!count_only) {
734                 svg("<!-- initcall -->\n");
735
736                 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
737                 /* surrounding box */
738                 svg_graph_box(kcount);
739         }
740
741         kcount = 0;
742
743         /*
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
747          * when appropriate.
748          */
749
750         f = popen("dmesg", "r");
751         if (!f)
752                 return;
753
754         while (!feof(f)) {
755                 int c;
756                 int z = 0;
757                 char l[256];
758
759                 if (fgets(l, sizeof(l) - 1, f) == NULL)
760                         continue;
761
762                 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
763                            &t, func, &ret, &usecs);
764                 if (c != 4) {
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);
768                         if (c != 4)
769                                 continue;
770                 }
771
772                 /* chop the +0xXX/0xXX stuff */
773                 while(func[z] != '+')
774                         z++;
775                 func[z] = 0;
776
777                 if (count_only) {
778                         /* filter out irrelevant stuff */
779                         if (usecs >= 1000)
780                                 kcount++;
781                         continue;
782                 }
783
784                 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
785                     func, t, usecs, ret);
786
787                 if (usecs < 1000)
788                         continue;
789
790                 /* rect */
791                 svg("  <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
792                     time_to_graph(t - (usecs / 1000000.0)),
793                     ps_to_graph(kcount),
794                     time_to_graph(usecs / 1000000.0),
795                     ps_to_graph(1));
796
797                 /* label */
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,
801                     func,
802                     usecs / 1000000.0);
803
804                 kcount++;
805         }
806 }
807
808
809 static void svg_ps_bars(void)
810 {
811         struct ps_struct *ps;
812         int i = 0;
813         int j = 0;
814         int w;
815         int pid;
816
817         svg("<!-- Process graph -->\n");
818
819         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
820
821         /* surrounding box */
822         svg_graph_box(pcount);
823
824         /* pass 2 - ps boxes */
825         ps = ps_first;
826         while ((ps = get_next_ps(ps))) {
827                 double starttime;
828                 int t;
829
830                 if (!ps)
831                         continue;
832
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);
836
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];
840
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 */
845                 } else {
846                         /* hook children to our parent coords instead */
847                         ps->pos_x = ps->parent->pos_x;
848                         ps->pos_y = ps->parent->pos_y;
849
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",
853                                     ps->parent->pos_x,
854                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
855                                     ps->parent->pos_x,
856                                     ps->parent->pos_y);
857                         continue;
858                 }
859
860                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
861                     time_to_graph(starttime - graph_start),
862                     ps_to_graph(j),
863                     time_to_graph(sampletime[ps->last] - starttime),
864                     ps_to_graph(1));
865
866                 /* paint cpu load over these */
867                 for (t = ps->first + 1; t < ps->last; t++) {
868                         double rt, prt;
869                         double wt, wrt;
870
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;
874
875                         prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
876                         wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
877
878                         /* this can happen if timekeeping isn't accurate enough */
879                         if (prt > 1.0)
880                                 prt = 1.0;
881                         if (wrt > 1.0)
882                                 wrt = 1.0;
883
884                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
885                                 continue;
886
887                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
888                             time_to_graph(sampletime[t - 1] - graph_start),
889                             ps_to_graph(j),
890                             time_to_graph(sampletime[t] - sampletime[t - 1]),
891                             ps_to_graph(wrt));
892
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]),
898                             ps_to_graph(prt));
899                 }
900
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 */
904                         w = ps->last;
905                 else
906                         w = ps->first;
907
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,
912                     ps->name,
913                     ps->pid,
914                     (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
915                 /* paint lines to the parent process */
916                 if (ps->parent) {
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,
921                             ps->parent->pos_x,
922                             ps_to_graph(j) + 10.0);
923
924                         /* one vertical line connecting all the horizontal ones up */
925                         if (!ps->next)
926                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
927                                     ps->parent->pos_x,
928                                     ps_to_graph(j) + 10.0,
929                                     ps->parent->pos_x,
930                                     ps->parent->pos_y);
931                 }
932
933                 j++; /* count boxes */
934
935                 svg("\n");
936         }
937
938         /* last pass - determine when idle */
939         pid = getpid();
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
942          */
943         ps = ps_first;
944         while (ps->next_ps) {
945                 ps = ps->next_ps;
946                 if (ps->pid == pid)
947                         break;
948         }
949
950         for (i = ps->first; i < samples - (hz / 2); i++) {
951                 double crt;
952                 double brt;
953                 int c;
954
955                 /* subtract bootchart cpu utilization from total */
956                 crt = 0.0;
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;
960
961                 /*
962                  * our definition of "idle":
963                  *
964                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
965                  * defaults to 4.0%, which experimentally, is where atom idles
966                  */
967                 if ((crt - brt) < (interval / 2.0)) {
968                         idletime = sampletime[i] - graph_start;
969                         svg("\n<!-- idle detected at %.03f seconds -->\n",
970                             idletime);
971                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
972                             time_to_graph(idletime),
973                             -scale_y,
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,
979                             idletime);
980                         break;
981                 }
982         }
983 }
984
985
986 static void svg_top_ten_cpu(void)
987 {
988         struct ps_struct *top[10];
989         struct ps_struct emptyps;
990         struct ps_struct *ps;
991         int n, m;
992
993         memset(&emptyps, 0, sizeof(struct ps_struct));
994         for (n=0; n < 10; n++)
995                 top[n] = &emptyps;
996
997         /* walk all ps's and setup ptrs */
998         ps = ps_first;
999         while ((ps = get_next_ps(ps))) {
1000                 for (n = 0; n < 10; n++) {
1001                         if (ps->total <= top[n]->total)
1002                                 continue;
1003                         /* cascade insert */
1004                         for (m = 9; m > n; m--)
1005                                 top[m] = top[m-1];
1006                         top[n] = ps;
1007                         break;
1008                 }
1009         }
1010
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",
1014                     20 + (n * 13),
1015                     top[n]->total,
1016                     top[n]->name,
1017                     top[n]->pid);
1018 }
1019
1020
1021 static void svg_top_ten_pss(void)
1022 {
1023         struct ps_struct *top[10];
1024         struct ps_struct emptyps;
1025         struct ps_struct *ps;
1026         int n, m;
1027
1028         memset(&emptyps, 0, sizeof(struct ps_struct));
1029         for (n=0; n < 10; n++)
1030                 top[n] = &emptyps;
1031
1032         /* walk all ps's and setup ptrs */
1033         ps = ps_first;
1034         while ((ps = get_next_ps(ps))) {
1035                 for (n = 0; n < 10; n++) {
1036                         if (ps->pss_max <= top[n]->pss_max)
1037                                 continue;
1038                         /* cascade insert */
1039                         for (m = 9; m > n; m--)
1040                                 top[m] = top[m-1];
1041                         top[n] = ps;
1042                         break;
1043                 }
1044         }
1045
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",
1049                     20 + (n * 13),
1050                     top[n]->pss_max,
1051                     top[n]->name,
1052                     top[n]->pid);
1053 }
1054
1055
1056 void svg_do(void)
1057 {
1058         struct ps_struct *ps;
1059
1060         memset(&str, 0, sizeof(str));
1061
1062         ps = ps_first;
1063
1064         /* count initcall thread count first */
1065         svg_do_initcall(1);
1066         ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1067
1068         /* then count processes */
1069         while ((ps = get_next_ps(ps))) {
1070                 if (!ps_filter(ps))
1071                         pcount++;
1072                 else
1073                         pfiltered++;
1074         }
1075         psize = ps_to_graph(pcount) + (scale_y * 2);
1076
1077         esize = (entropy ? scale_y * 7 : 0);
1078
1079         /* after this, we can draw the header with proper sizing */
1080         svg_header();
1081
1082         svg("<g transform=\"translate(10,400)\">\n");
1083         svg_io_bi_bar();
1084         svg("</g>\n\n");
1085
1086         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1087         svg_io_bo_bar();
1088         svg("</g>\n\n");
1089
1090         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1091         svg_cpu_bar();
1092         svg("</g>\n\n");
1093
1094         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1095         svg_wait_bar();
1096         svg("</g>\n\n");
1097
1098         if (kcount) {
1099                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1100                 svg_do_initcall(0);
1101                 svg("</g>\n\n");
1102         }
1103
1104         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1105         svg_ps_bars();
1106         svg("</g>\n\n");
1107
1108         svg("<g transform=\"translate(10,  0)\">\n");
1109         svg_title();
1110         svg("</g>\n\n");
1111
1112         svg("<g transform=\"translate(10,200)\">\n");
1113         svg_top_ten_cpu();
1114         svg("</g>\n\n");
1115
1116         if (entropy) {
1117                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1118                 svg_entropy_bar();
1119                 svg("</g>\n\n");
1120         }
1121
1122         if (pss) {
1123                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1124                 svg_pss_graph();
1125                 svg("</g>\n\n");
1126
1127                 svg("<g transform=\"translate(410,200)\">\n");
1128                 svg_top_ten_pss();
1129                 svg("</g>\n\n");
1130         }
1131
1132         /* svg footer */
1133         svg("\n</svg>\n");
1134 }