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