chiark / gitweb /
bootchart: if "root=/dev" is not matched, don't try to read /sys
[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(const char *build)
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 *c;
153         FILE *f;
154         time_t t;
155         int fd;
156         struct utsname uts;
157
158         /* grab /proc/cmdline */
159         fd = openat(procfd, "cmdline", O_RDONLY);
160         f = fdopen(fd, "r");
161         if (f) {
162                 if (!fgets(cmdline, 255, f))
163                         sprintf(cmdline, "Unknown");
164                 fclose(f);
165         }
166
167         /* extract root fs so we can find disk model name in sysfs */
168         /* FIXME: this works only in the simple case */
169         c = strstr(cmdline, "root=/dev/");
170         if (c) {
171                 strncpy(rootbdev, &c[10], 3);
172                 rootbdev[3] = '\0';
173                 sprintf(filename, "block/%s/device/model", rootbdev);
174                 fd = openat(sysfd, filename, O_RDONLY);
175                 f = fdopen(fd, "r");
176                 if (f) {
177                         if (!fgets(model, 255, f))
178                                 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
179                         fclose(f);
180                 }
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         svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
205             uts.nodename, date);
206         svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
207             uts.sysname, uts.release, uts.version, uts.machine);
208         svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
209             cpu);
210         svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
211             model);
212         svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
213             cmdline);
214         svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
215             build);
216         svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
217         svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
218
219         if (idletime >= 0.0)
220                 svg("%.03fs", idletime);
221         else
222                 svg("Not detected");
223         svg("</text>\n");
224         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",
225             hz, len, overrun, pscount, pfiltered);
226 }
227
228
229 static void svg_graph_box(int height)
230 {
231         double d = 0.0;
232         int i = 0;
233
234         /* outside box, fill */
235         svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
236             time_to_graph(0.0),
237             time_to_graph(sampletime[samples-1] - graph_start),
238             ps_to_graph(height));
239
240         for (d = graph_start; d <= sampletime[samples-1];
241              d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) {
242                 /* lines for each second */
243                 if (i % 50 == 0)
244                         svg("  <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
245                             time_to_graph(d - graph_start),
246                             time_to_graph(d - graph_start),
247                             ps_to_graph(height));
248                 else if (i % 10 == 0)
249                         svg("  <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
250                             time_to_graph(d - graph_start),
251                             time_to_graph(d - graph_start),
252                             ps_to_graph(height));
253                 else
254                         svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
255                             time_to_graph(d - graph_start),
256                             time_to_graph(d - graph_start),
257                             ps_to_graph(height));
258
259                 /* time label */
260                 if (i % 10 == 0)
261                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
262                             time_to_graph(d - graph_start),
263                             -5.0,
264                             d - graph_start);
265
266                 i++;
267         }
268 }
269
270
271 static void svg_pss_graph(void)
272 {
273         struct ps_struct *ps;
274         int i;
275
276         svg("\n\n<!-- Pss memory size graph -->\n");
277
278         svg("\n  <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
279
280         /* vsize 1000 == 1000mb */
281         svg_graph_box(100);
282         /* draw some hlines for usable memory sizes */
283         for (i = 100000; i < 1000000; i += 100000) {
284                 svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
285                         time_to_graph(.0),
286                         kb_to_graph(i),
287                         time_to_graph(sampletime[samples-1] - graph_start),
288                         kb_to_graph(i));
289                 svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
290                     time_to_graph(sampletime[samples-1] - graph_start) + 5,
291                     kb_to_graph(i), (1000000 - i) / 1000);
292         }
293         svg("\n");
294
295         /* now plot the graph itself */
296         for (i = 1; i < samples ; i++) {
297                 int bottom;
298                 int top;
299
300                 bottom = 0;
301                 top = 0;
302
303                 /* put all the small pss blocks into the bottom */
304                 ps = ps_first;
305                 while (ps->next_ps) {
306                         ps = ps->next_ps;
307                         if (!ps)
308                                 continue;
309                         if (ps->sample[i].pss <= (100 * scale_y))
310                                 top += ps->sample[i].pss;
311                 };
312                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
313                     "rgb(64,64,64)",
314                     time_to_graph(sampletime[i - 1] - graph_start),
315                     kb_to_graph(1000000.0 - top),
316                     time_to_graph(sampletime[i] - sampletime[i - 1]),
317                     kb_to_graph(top - bottom));
318
319                 bottom = top;
320
321                 /* now plot the ones that are of significant size */
322                 ps = ps_first;
323                 while (ps->next_ps) {
324                         ps = ps->next_ps;
325                         if (!ps)
326                                 continue;
327                         /* don't draw anything smaller than 2mb */
328                         if (ps->sample[i].pss > (100 * scale_y)) {
329                                 top = bottom + ps->sample[i].pss;
330                                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
331                                     colorwheel[ps->pid % 12],
332                                     time_to_graph(sampletime[i - 1] - graph_start),
333                                     kb_to_graph(1000000.0 - top),
334                                     time_to_graph(sampletime[i] - sampletime[i - 1]),
335                                     kb_to_graph(top - bottom));
336                                 bottom = top;
337                         }
338                 }
339         }
340
341         /* overlay all the text labels */
342         for (i = 1; i < samples ; i++) {
343                 int bottom;
344                 int top;
345
346                 bottom = 0;
347                 top = 0;
348
349                 /* put all the small pss blocks into the bottom */
350                 ps = ps_first;
351                 while (ps->next_ps) {
352                         ps = ps->next_ps;
353                         if (!ps)
354                                 continue;
355                         if (ps->sample[i].pss <= (100 * scale_y))
356                                 top += ps->sample[i].pss;
357                 };
358
359                 bottom = top;
360
361                 /* now plot the ones that are of significant size */
362                 ps = ps_first;
363                 while (ps->next_ps) {
364                         ps = ps->next_ps;
365                         if (!ps)
366                                 continue;
367                         /* don't draw anything smaller than 2mb */
368                         if (ps->sample[i].pss > (100 * scale_y)) {
369                                 top = bottom + ps->sample[i].pss;
370                                 /* draw a label with the process / PID */
371                                 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y)))
372                                         svg("  <text x=\"%.03f\" y=\"%.03f\">%s [%i]</text>\n",
373                                             time_to_graph(sampletime[i] - graph_start),
374                                             kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
375                                             ps->name,
376                                             ps->pid);
377                                 bottom = top;
378                         }
379                 }
380         }
381
382         /* debug output - full data dump */
383         svg("\n\n<!-- PSS map - csv format -->\n");
384         ps = ps_first;
385         while (ps->next_ps) {
386                 ps = ps->next_ps;
387                 if (!ps)
388                         continue;
389                 svg("<!-- %s [%d] pss=", ps->name, ps->pid);
390                 for (i = 0; i < samples ; i++) {
391                         svg("%d," , ps->sample[i].pss);
392                 }
393                 svg(" -->\n");
394         }
395
396 }
397
398 static void svg_io_bi_bar(void)
399 {
400         double max = 0.0;
401         double range;
402         int max_here = 0;
403         int i;
404
405         svg("<!-- IO utilization graph - In -->\n");
406
407         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
408
409         /*
410          * calculate rounding range
411          *
412          * We need to round IO data since IO block data is not updated on
413          * each poll. Applying a smoothing function loses some burst data,
414          * so keep the smoothing range short.
415          */
416         range = 0.25 / (1.0 / hz);
417         if (range < 2.0)
418                 range = 2.0; /* no smoothing */
419
420         /* surrounding box */
421         svg_graph_box(5);
422
423         /* find the max IO first */
424         for (i = 1; i < samples; i++) {
425                 int start;
426                 int stop;
427                 double tot;
428
429                 start = max(i - ((range / 2) - 1), 0);
430                 stop = min(i + (range / 2), samples - 1);
431
432                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
433                       / (stop - start);
434                 if (tot > max) {
435                         max = tot;
436                         max_here = i;
437                 }
438                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
439                       / (stop - start);
440                 if (tot > max)
441                         max = tot;
442         }
443
444         /* plot bi */
445         for (i = 1; i < samples; i++) {
446                 int start;
447                 int stop;
448                 double tot;
449                 double pbi;
450
451                 start = max(i - ((range / 2) - 1), 0);
452                 stop = min(i + (range / 2), samples);
453
454                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
455                       / (stop - start);
456                 pbi = tot / max;
457
458                 if (pbi > 0.001)
459                         svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
460                             time_to_graph(sampletime[i - 1] - graph_start),
461                             (scale_y * 5) - (pbi * (scale_y * 5)),
462                             time_to_graph(sampletime[i] - sampletime[i - 1]),
463                             pbi * (scale_y * 5));
464
465                 /* labels around highest value */
466                 if (i == max_here) {
467                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
468                             time_to_graph(sampletime[i] - graph_start) + 5,
469                             ((scale_y * 5) - (pbi * (scale_y * 5))) + 15,
470                             max / 1024.0 / (interval / 1000000000.0));
471                 }
472         }
473 }
474
475 static void svg_io_bo_bar(void)
476 {
477         double max = 0.0;
478         double range;
479         int max_here = 0;
480         int i;
481
482         svg("<!-- IO utilization graph - out -->\n");
483
484         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
485
486         /*
487          * calculate rounding range
488          *
489          * We need to round IO data since IO block data is not updated on
490          * each poll. Applying a smoothing function loses some burst data,
491          * so keep the smoothing range short.
492          */
493         range = 0.25 / (1.0 / hz);
494         if (range < 2.0)
495                 range = 2.0; /* no smoothing */
496
497         /* surrounding box */
498         svg_graph_box(5);
499
500         /* find the max IO first */
501         for (i = 1; i < samples; i++) {
502                 int start;
503                 int stop;
504                 double tot;
505
506                 start = max(i - ((range / 2) - 1), 0);
507                 stop = min(i + (range / 2), samples - 1);
508
509                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
510                       / (stop - start);
511                 if (tot > max)
512                         max = tot;
513                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
514                       / (stop - start);
515                 if (tot > max) {
516                         max = tot;
517                         max_here = i;
518                 }
519         }
520
521         /* plot bo */
522         for (i = 1; i < samples; i++) {
523                 int start;
524                 int stop;
525                 double tot;
526                 double pbo;
527
528                 start = max(i - ((range / 2) - 1), 0);
529                 stop = min(i + (range / 2), samples);
530
531                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
532                       / (stop - start);
533                 pbo = tot / max;
534
535                 if (pbo > 0.001)
536                         svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
537                             time_to_graph(sampletime[i - 1] - graph_start),
538                             (scale_y * 5) - (pbo * (scale_y * 5)),
539                             time_to_graph(sampletime[i] - sampletime[i - 1]),
540                             pbo * (scale_y * 5));
541
542                 /* labels around highest bo value */
543                 if (i == max_here) {
544                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
545                             time_to_graph(sampletime[i] - graph_start) + 5,
546                             ((scale_y * 5) - (pbo * (scale_y * 5))),
547                             max / 1024.0 / (interval / 1000000000.0));
548                 }
549         }
550 }
551
552
553 static void svg_cpu_bar(void)
554 {
555         int i;
556
557         svg("<!-- CPU utilization graph -->\n");
558
559         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
560         /* surrounding box */
561         svg_graph_box(5);
562
563         /* bars for each sample, proportional to the CPU util. */
564         for (i = 1; i < samples; i++) {
565                 int c;
566                 double trt;
567                 double ptrt;
568
569                 ptrt = trt = 0.0;
570
571                 for (c = 0; c < cpus; c++)
572                         trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
573
574                 trt = trt / 1000000000.0;
575
576                 trt = trt / (double)cpus;
577
578                 if (trt > 0.0)
579                         ptrt = trt / (sampletime[i] - sampletime[i - 1]);
580
581                 if (ptrt > 1.0)
582                         ptrt = 1.0;
583
584                 if (ptrt > 0.001) {
585                         svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
586                             time_to_graph(sampletime[i - 1] - graph_start),
587                             (scale_y * 5) - (ptrt * (scale_y * 5)),
588                             time_to_graph(sampletime[i] - sampletime[i - 1]),
589                             ptrt * (scale_y * 5));
590                 }
591         }
592 }
593
594 static void svg_wait_bar(void)
595 {
596         int i;
597
598         svg("<!-- Wait time aggregation box -->\n");
599
600         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
601
602         /* surrounding box */
603         svg_graph_box(5);
604
605         /* bars for each sample, proportional to the CPU util. */
606         for (i = 1; i < samples; i++) {
607                 int c;
608                 double twt;
609                 double ptwt;
610
611                 ptwt = twt = 0.0;
612
613                 for (c = 0; c < cpus; c++)
614                         twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
615
616                 twt = twt / 1000000000.0;
617
618                 twt = twt / (double)cpus;
619
620                 if (twt > 0.0)
621                         ptwt = twt / (sampletime[i] - sampletime[i - 1]);
622
623                 if (ptwt > 1.0)
624                         ptwt = 1.0;
625
626                 if (ptwt > 0.001) {
627                         svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
628                             time_to_graph(sampletime[i - 1] - graph_start),
629                             ((scale_y * 5) - (ptwt * (scale_y * 5))),
630                             time_to_graph(sampletime[i] - sampletime[i - 1]),
631                             ptwt * (scale_y * 5));
632                 }
633         }
634 }
635
636
637 static void svg_entropy_bar(void)
638 {
639         int i;
640
641         svg("<!-- entropy pool graph -->\n");
642
643         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
644         /* surrounding box */
645         svg_graph_box(5);
646
647         /* bars for each sample, scale 0-4096 */
648         for (i = 1; i < samples; i++) {
649                 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
650                 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
651                     time_to_graph(sampletime[i - 1] - graph_start),
652                     ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))),
653                     time_to_graph(sampletime[i] - sampletime[i - 1]),
654                     (entropy_avail[i] / 4096.) * (scale_y * 5));
655         }
656 }
657
658
659 static struct ps_struct *get_next_ps(struct ps_struct *ps)
660 {
661         /*
662          * walk the list of processes and return the next one to be
663          * painted
664          */
665         if (ps == ps_first)
666                 return ps->next_ps;
667
668         /* go deep */
669         if (ps->children)
670                 return ps->children;
671
672         /* find siblings */
673         if (ps->next)
674                 return ps->next;
675
676         /* go back for parent siblings */
677         while (1) {
678                 if (ps->parent)
679                         if (ps->parent->next)
680                                 return ps->parent->next;
681                 ps = ps->parent;
682                 if (!ps)
683                         return ps;
684         }
685
686         return NULL;
687 }
688
689
690 static int ps_filter(struct ps_struct *ps)
691 {
692         if (!filter)
693                 return 0;
694
695         /* can't draw data when there is only 1 sample (need start + stop) */
696         if (ps->first == ps->last)
697                 return -1;
698
699         /* don't filter kthreadd */
700         if (ps->pid == 2)
701                 return 0;
702
703         /* drop stuff that doesn't use any real CPU time */
704         if (ps->total <= 0.001)
705                 return -1;
706
707         return 0;
708 }
709
710
711 static void svg_do_initcall(int count_only)
712 {
713         FILE _cleanup_pclose_ *f = NULL;
714         double t;
715         char func[256];
716         int ret;
717         int usecs;
718
719         /* can't plot initcall when disabled or in relative mode */
720         if (!initcall || relative) {
721                 kcount = 0;
722                 return;
723         }
724
725         if (!count_only) {
726                 svg("<!-- initcall -->\n");
727
728                 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
729                 /* surrounding box */
730                 svg_graph_box(kcount);
731         }
732
733         kcount = 0;
734
735         /*
736          * Initcall graphing - parses dmesg buffer and displays kernel threads
737          * This somewhat uses the same methods and scaling to show processes
738          * but looks a lot simpler. It's overlaid entirely onto the PS graph
739          * when appropriate.
740          */
741
742         f = popen("dmesg", "r");
743         if (!f)
744                 return;
745
746         while (!feof(f)) {
747                 int c;
748                 int z = 0;
749                 char l[256];
750
751                 if (fgets(l, sizeof(l) - 1, f) == NULL)
752                         continue;
753
754                 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
755                            &t, func, &ret, &usecs);
756                 if (c != 4) {
757                         /* also parse initcalls done by module loading */
758                         c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
759                                    &t, func, &ret, &usecs);
760                         if (c != 4)
761                                 continue;
762                 }
763
764                 /* chop the +0xXX/0xXX stuff */
765                 while(func[z] != '+')
766                         z++;
767                 func[z] = 0;
768
769                 if (count_only) {
770                         /* filter out irrelevant stuff */
771                         if (usecs >= 1000)
772                                 kcount++;
773                         continue;
774                 }
775
776                 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
777                     func, t, usecs, ret);
778
779                 if (usecs < 1000)
780                         continue;
781
782                 /* rect */
783                 svg("  <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
784                     time_to_graph(t - (usecs / 1000000.0)),
785                     ps_to_graph(kcount),
786                     time_to_graph(usecs / 1000000.0),
787                     ps_to_graph(1));
788
789                 /* label */
790                 svg("  <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
791                     time_to_graph(t - (usecs / 1000000.0)) + 5,
792                     ps_to_graph(kcount) + 15,
793                     func,
794                     usecs / 1000000.0);
795
796                 kcount++;
797         }
798 }
799
800
801 static void svg_ps_bars(void)
802 {
803         struct ps_struct *ps;
804         int i = 0;
805         int j = 0;
806         int w;
807         int pid;
808
809         svg("<!-- Process graph -->\n");
810
811         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
812
813         /* surrounding box */
814         svg_graph_box(pcount);
815
816         /* pass 2 - ps boxes */
817         ps = ps_first;
818         while ((ps = get_next_ps(ps))) {
819                 double starttime;
820                 int t;
821
822                 if (!ps)
823                         continue;
824
825                 /* leave some trace of what we actually filtered etc. */
826                 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", ps->name, ps->pid,
827                     ps->ppid, ps->total);
828
829                 /* it would be nice if we could use exec_start from /proc/pid/sched,
830                  * but it's unreliable and gives bogus numbers */
831                 starttime = sampletime[ps->first];
832
833                 if (!ps_filter(ps)) {
834                         /* remember where _to_ our children need to draw a line */
835                         ps->pos_x = time_to_graph(starttime - graph_start);
836                         ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
837                 } else {
838                         /* hook children to our parent coords instead */
839                         ps->pos_x = ps->parent->pos_x;
840                         ps->pos_y = ps->parent->pos_y;
841
842                         /* if this is the last child, we might still need to draw a connecting line */
843                         if ((!ps->next) && (ps->parent))
844                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
845                                     ps->parent->pos_x,
846                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
847                                     ps->parent->pos_x,
848                                     ps->parent->pos_y);
849                         continue;
850                 }
851
852                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
853                     time_to_graph(starttime - graph_start),
854                     ps_to_graph(j),
855                     time_to_graph(sampletime[ps->last] - starttime),
856                     ps_to_graph(1));
857
858                 /* paint cpu load over these */
859                 for (t = ps->first + 1; t < ps->last; t++) {
860                         double rt, prt;
861                         double wt, wrt;
862
863                         /* calculate over interval */
864                         rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
865                         wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
866
867                         prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
868                         wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
869
870                         /* this can happen if timekeeping isn't accurate enough */
871                         if (prt > 1.0)
872                                 prt = 1.0;
873                         if (wrt > 1.0)
874                                 wrt = 1.0;
875
876                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
877                                 continue;
878
879                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
880                             time_to_graph(sampletime[t - 1] - graph_start),
881                             ps_to_graph(j),
882                             time_to_graph(sampletime[t] - sampletime[t - 1]),
883                             ps_to_graph(wrt));
884
885                         /* draw cpu over wait - TODO figure out how/why run + wait > interval */
886                         svg("    <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
887                             time_to_graph(sampletime[t - 1] - graph_start),
888                             ps_to_graph(j + (1.0 - prt)),
889                             time_to_graph(sampletime[t] - sampletime[t - 1]),
890                             ps_to_graph(prt));
891                 }
892
893                 /* determine where to display the process name */
894                 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
895                         /* too small to fit label inside the box */
896                         w = ps->last;
897                 else
898                         w = ps->first;
899
900                 /* text label of process name */
901                 svg("  <text x=\"%.03f\" y=\"%.03f\">%s [%i] <tspan class=\"run\">%.03fs</tspan></text>\n",
902                     time_to_graph(sampletime[w] - graph_start) + 5.0,
903                     ps_to_graph(j) + 14.0,
904                     ps->name,
905                     ps->pid,
906                     (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
907                 /* paint lines to the parent process */
908                 if (ps->parent) {
909                         /* horizontal part */
910                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
911                             time_to_graph(starttime - graph_start),
912                             ps_to_graph(j) + 10.0,
913                             ps->parent->pos_x,
914                             ps_to_graph(j) + 10.0);
915
916                         /* one vertical line connecting all the horizontal ones up */
917                         if (!ps->next)
918                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
919                                     ps->parent->pos_x,
920                                     ps_to_graph(j) + 10.0,
921                                     ps->parent->pos_x,
922                                     ps->parent->pos_y);
923                 }
924
925                 j++; /* count boxes */
926
927                 svg("\n");
928         }
929
930         /* last pass - determine when idle */
931         pid = getpid();
932         /* make sure we start counting from the point where we actually have
933          * data: assume that bootchart's first sample is when data started
934          */
935         ps = ps_first;
936         while (ps->next_ps) {
937                 ps = ps->next_ps;
938                 if (ps->pid == pid)
939                         break;
940         }
941
942         for (i = ps->first; i < samples - (hz / 2); i++) {
943                 double crt;
944                 double brt;
945                 int c;
946
947                 /* subtract bootchart cpu utilization from total */
948                 crt = 0.0;
949                 for (c = 0; c < cpus; c++)
950                         crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime;
951                 brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime;
952
953                 /*
954                  * our definition of "idle":
955                  *
956                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
957                  * defaults to 4.0%, which experimentally, is where atom idles
958                  */
959                 if ((crt - brt) < (interval / 2.0)) {
960                         idletime = sampletime[i] - graph_start;
961                         svg("\n<!-- idle detected at %.03f seconds -->\n",
962                             idletime);
963                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
964                             time_to_graph(idletime),
965                             -scale_y,
966                             time_to_graph(idletime),
967                             ps_to_graph(pcount) + scale_y);
968                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
969                             time_to_graph(idletime) + 5.0,
970                             ps_to_graph(pcount) + scale_y,
971                             idletime);
972                         break;
973                 }
974         }
975 }
976
977
978 static void svg_top_ten_cpu(void)
979 {
980         struct ps_struct *top[10];
981         struct ps_struct emptyps;
982         struct ps_struct *ps;
983         int n, m;
984
985         memset(&emptyps, 0, sizeof(struct ps_struct));
986         for (n=0; n < 10; n++)
987                 top[n] = &emptyps;
988
989         /* walk all ps's and setup ptrs */
990         ps = ps_first;
991         while ((ps = get_next_ps(ps))) {
992                 for (n = 0; n < 10; n++) {
993                         if (ps->total <= top[n]->total)
994                                 continue;
995                         /* cascade insert */
996                         for (m = 9; m > n; m--)
997                                 top[m] = top[m-1];
998                         top[n] = ps;
999                         break;
1000                 }
1001         }
1002
1003         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1004         for (n = 0; n < 10; n++)
1005                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - %s[%d]</text>\n",
1006                     20 + (n * 13),
1007                     top[n]->total,
1008                     top[n]->name,
1009                     top[n]->pid);
1010 }
1011
1012
1013 static void svg_top_ten_pss(void)
1014 {
1015         struct ps_struct *top[10];
1016         struct ps_struct emptyps;
1017         struct ps_struct *ps;
1018         int n, m;
1019
1020         memset(&emptyps, 0, sizeof(struct ps_struct));
1021         for (n=0; n < 10; n++)
1022                 top[n] = &emptyps;
1023
1024         /* walk all ps's and setup ptrs */
1025         ps = ps_first;
1026         while ((ps = get_next_ps(ps))) {
1027                 for (n = 0; n < 10; n++) {
1028                         if (ps->pss_max <= top[n]->pss_max)
1029                                 continue;
1030                         /* cascade insert */
1031                         for (m = 9; m > n; m--)
1032                                 top[m] = top[m-1];
1033                         top[n] = ps;
1034                         break;
1035                 }
1036         }
1037
1038         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1039         for (n = 0; n < 10; n++)
1040                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - %s[%d]</text>\n",
1041                     20 + (n * 13),
1042                     top[n]->pss_max,
1043                     top[n]->name,
1044                     top[n]->pid);
1045 }
1046
1047
1048 void svg_do(const char *build)
1049 {
1050         struct ps_struct *ps;
1051
1052         memset(&str, 0, sizeof(str));
1053
1054         ps = ps_first;
1055
1056         /* count initcall thread count first */
1057         svg_do_initcall(1);
1058         ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0);
1059
1060         /* then count processes */
1061         while ((ps = get_next_ps(ps))) {
1062                 if (!ps_filter(ps))
1063                         pcount++;
1064                 else
1065                         pfiltered++;
1066         }
1067         psize = ps_to_graph(pcount) + (scale_y * 2);
1068
1069         esize = (entropy ? scale_y * 7 : 0);
1070
1071         /* after this, we can draw the header with proper sizing */
1072         svg_header();
1073
1074         svg("<g transform=\"translate(10,400)\">\n");
1075         svg_io_bi_bar();
1076         svg("</g>\n\n");
1077
1078         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 7.0));
1079         svg_io_bo_bar();
1080         svg("</g>\n\n");
1081
1082         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 14.0));
1083         svg_cpu_bar();
1084         svg("</g>\n\n");
1085
1086         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 21.0));
1087         svg_wait_bar();
1088         svg("</g>\n\n");
1089
1090         if (kcount) {
1091                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0));
1092                 svg_do_initcall(0);
1093                 svg("</g>\n\n");
1094         }
1095
1096         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize);
1097         svg_ps_bars();
1098         svg("</g>\n\n");
1099
1100         svg("<g transform=\"translate(10,  0)\">\n");
1101         svg_title(build);
1102         svg("</g>\n\n");
1103
1104         svg("<g transform=\"translate(10,200)\">\n");
1105         svg_top_ten_cpu();
1106         svg("</g>\n\n");
1107
1108         if (entropy) {
1109                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize);
1110                 svg_entropy_bar();
1111                 svg("</g>\n\n");
1112         }
1113
1114         if (pss) {
1115                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize);
1116                 svg_pss_graph();
1117                 svg("</g>\n\n");
1118
1119                 svg("<g transform=\"translate(410,200)\">\n");
1120                 svg_top_ten_pss();
1121                 svg("</g>\n\n");
1122         }
1123
1124         /* svg footer */
1125         svg("\n</svg>\n");
1126 }