chiark / gitweb /
Use initalization instead of explicit zeroing in more places
[elogind.git] / src / bootchart / svg.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2009-2013 Intel Coproration
7
8   Authors:
9     Auke Kok <auke-jan.h.kok@intel.com>
10
11   systemd is free software; you can redistribute it and/or modify it
12   under the terms of the GNU Lesser General Public License as published by
13   the Free Software Foundation; either version 2.1 of the License, or
14   (at your option) any later version.
15
16   systemd is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public License
22   along with systemd; If not, see <http://www.gnu.org/licenses/>.
23  ***/
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <sys/utsname.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #include "util.h"
37 #include "macro.h"
38 #include "store.h"
39 #include "svg.h"
40 #include "bootchart.h"
41
42 #define time_to_graph(t) ((t) * arg_scale_x)
43 #define ps_to_graph(n) ((n) * arg_scale_y)
44 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
45 #define to_color(n) (192.0 - ((n) * 192.0))
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 * const 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 static void svg_header(void) {
75         float w;
76         float h;
77
78         /* min width is about 1600px due to the label */
79         w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
80         w = ((w < 1600.0) ? 1600.0 : w);
81
82         /* height is variable based on pss, psize, ksize */
83         h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
84             + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
85             + psize + ksize + esize;
86
87         svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
88         svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
89         svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
90
91         //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
92         svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
93             w, h);
94         svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
95
96         /* write some basic info as a comment, including some help */
97         svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
98         svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that       -->\n");
99         svg("<!-- render these files properly but more slowly are ImageMagick, gimp,  -->\n");
100         svg("<!-- inkscape, etc. To display the files on your system, just point      -->\n");
101         svg("<!-- your browser to file:///run/log/ and click. This bootchart was      -->\n\n");
102
103         svg("<!-- generated by bootchart version %s, running with options:  -->\n", VERSION);
104         svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
105         svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
106         svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
107         svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
108         svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
109
110         /* style sheet */
111         svg("<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n");
112
113         svg("      rect       { stroke-width: 1; }\n");
114         svg("      rect.cpu   { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
115         svg("      rect.wait  { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
116         svg("      rect.bi    { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
117         svg("      rect.bo    { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
118         svg("      rect.ps    { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
119         svg("      rect.krnl  { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
120         svg("      rect.box   { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
121         svg("      rect.clrw  { stroke-width: 0; fill-opacity: 0.7;}\n");
122         svg("      line       { stroke: rgb(64,64,64); stroke-width: 1; }\n");
123         svg("//    line.sec1  { }\n");
124         svg("      line.sec5  { stroke-width: 2; }\n");
125         svg("      line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
126         svg("      line.dot   { stroke-dasharray: 2 4; }\n");
127         svg("      line.idle  { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
128
129         svg("      .run       { font-size: 8; font-style: italic; }\n");
130         svg("      text       { font-family: Verdana, Helvetica; font-size: 10; }\n");
131         svg("      text.sec   { font-size: 8; }\n");
132         svg("      text.t1    { font-size: 24; }\n");
133         svg("      text.t2    { font-size: 12; }\n");
134         svg("      text.idle  { font-size: 18; }\n");
135
136         svg("    ]]>\n   </style>\n</defs>\n\n");
137 }
138
139 static void svg_title(const char *build) {
140         char cmdline[256] = "";
141         char filename[PATH_MAX];
142         char buf[256];
143         char rootbdev[16] = "Unknown";
144         char model[256] = "Unknown";
145         char date[256] = "Unknown";
146         char cpu[256] = "Unknown";
147         char *c;
148         FILE *f;
149         time_t t;
150         int fd;
151         struct utsname uts;
152
153         /* grab /proc/cmdline */
154         fd = openat(procfd, "cmdline", O_RDONLY);
155         f = fdopen(fd, "r");
156         if (f) {
157                 if (!fgets(cmdline, 255, f))
158                         sprintf(cmdline, "Unknown");
159                 fclose(f);
160         }
161
162         /* extract root fs so we can find disk model name in sysfs */
163         /* FIXME: this works only in the simple case */
164         c = strstr(cmdline, "root=/dev/");
165         if (c) {
166                 strncpy(rootbdev, &c[10], 3);
167                 rootbdev[3] = '\0';
168                 sprintf(filename, "block/%s/device/model", rootbdev);
169                 fd = openat(sysfd, filename, O_RDONLY);
170                 f = fdopen(fd, "r");
171                 if (f) {
172                         if (!fgets(model, 255, f))
173                                 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
174                         fclose(f);
175                 }
176         }
177
178         /* various utsname parameters */
179         if (uname(&uts))
180                 fprintf(stderr, "Error getting uname info\n");
181
182         /* date */
183         t = time(NULL);
184         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
185
186         /* CPU type */
187         fd = openat(procfd, "cpuinfo", O_RDONLY);
188         f = fdopen(fd, "r");
189         if (f) {
190                 while (fgets(buf, 255, f)) {
191                         if (strstr(buf, "model name")) {
192                                 strncpy(cpu, &buf[13], 255);
193                                 break;
194                         }
195                 }
196                 fclose(f);
197         }
198
199         svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
200             uts.nodename, date);
201         svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
202             uts.sysname, uts.release, uts.version, uts.machine);
203         svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
204             cpu);
205         svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
206             model);
207         svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
208             cmdline);
209         svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
210             build);
211         svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
212         svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
213
214         if (idletime >= 0.0)
215                 svg("%.03fs", idletime);
216         else
217                 svg("Not detected");
218         svg("</text>\n");
219         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",
220             arg_hz, arg_samples_len, overrun, pscount, pfiltered);
221 }
222
223 static void svg_graph_box(int height) {
224         double d = 0.0;
225         int i = 0;
226
227         /* outside box, fill */
228         svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
229             time_to_graph(0.0),
230             time_to_graph(sampletime[samples-1] - graph_start),
231             ps_to_graph(height));
232
233         for (d = graph_start; d <= sampletime[samples-1];
234              d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
235                 /* lines for each second */
236                 if (i % 50 == 0)
237                         svg("  <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
238                             time_to_graph(d - graph_start),
239                             time_to_graph(d - graph_start),
240                             ps_to_graph(height));
241                 else if (i % 10 == 0)
242                         svg("  <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
243                             time_to_graph(d - graph_start),
244                             time_to_graph(d - graph_start),
245                             ps_to_graph(height));
246                 else
247                         svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
248                             time_to_graph(d - graph_start),
249                             time_to_graph(d - graph_start),
250                             ps_to_graph(height));
251
252                 /* time label */
253                 if (i % 10 == 0)
254                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
255                             time_to_graph(d - graph_start),
256                             -5.0,
257                             d - graph_start);
258
259                 i++;
260         }
261 }
262
263 /* xml comments must not contain "--" */
264 static char* xml_comment_encode(const char* name) {
265         char *enc_name, *p;
266
267         enc_name = strdup(name);
268         if (!enc_name)
269                 return NULL;
270
271         for (p = enc_name; *p; p++)
272                 if (p[0] == '-' && p[1] == '-')
273                         p[1] = '_';
274
275         return enc_name;
276 }
277
278 static void svg_pss_graph(void) {
279         struct ps_struct *ps;
280         int i;
281
282         svg("\n\n<!-- Pss memory size graph -->\n");
283
284         svg("\n  <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
285
286         /* vsize 1000 == 1000mb */
287         svg_graph_box(100);
288         /* draw some hlines for usable memory sizes */
289         for (i = 100000; i < 1000000; i += 100000) {
290                 svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
291                         time_to_graph(.0),
292                         kb_to_graph(i),
293                         time_to_graph(sampletime[samples-1] - graph_start),
294                         kb_to_graph(i));
295                 svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
296                     time_to_graph(sampletime[samples-1] - graph_start) + 5,
297                     kb_to_graph(i), (1000000 - i) / 1000);
298         }
299         svg("\n");
300
301         /* now plot the graph itself */
302         for (i = 1; i < samples ; i++) {
303                 int bottom;
304                 int top;
305
306                 bottom = 0;
307                 top = 0;
308
309                 /* put all the small pss blocks into the bottom */
310                 ps = ps_first;
311                 while (ps->next_ps) {
312                         ps = ps->next_ps;
313                         if (!ps)
314                                 continue;
315                         if (ps->sample[i].pss <= (100 * arg_scale_y))
316                                 top += ps->sample[i].pss;
317                 };
318                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
319                     "rgb(64,64,64)",
320                     time_to_graph(sampletime[i - 1] - graph_start),
321                     kb_to_graph(1000000.0 - top),
322                     time_to_graph(sampletime[i] - sampletime[i - 1]),
323                     kb_to_graph(top - bottom));
324
325                 bottom = top;
326
327                 /* now plot the ones that are of significant size */
328                 ps = ps_first;
329                 while (ps->next_ps) {
330                         ps = ps->next_ps;
331                         if (!ps)
332                                 continue;
333                         /* don't draw anything smaller than 2mb */
334                         if (ps->sample[i].pss > (100 * arg_scale_y)) {
335                                 top = bottom + ps->sample[i].pss;
336                                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
337                                     colorwheel[ps->pid % 12],
338                                     time_to_graph(sampletime[i - 1] - graph_start),
339                                     kb_to_graph(1000000.0 - top),
340                                     time_to_graph(sampletime[i] - sampletime[i - 1]),
341                                     kb_to_graph(top - bottom));
342                                 bottom = top;
343                         }
344                 }
345         }
346
347         /* overlay all the text labels */
348         for (i = 1; i < samples ; i++) {
349                 int bottom;
350                 int top;
351
352                 bottom = 0;
353                 top = 0;
354
355                 /* put all the small pss blocks into the bottom */
356                 ps = ps_first;
357                 while (ps->next_ps) {
358                         ps = ps->next_ps;
359                         if (!ps)
360                                 continue;
361                         if (ps->sample[i].pss <= (100 * arg_scale_y))
362                                 top += ps->sample[i].pss;
363                 };
364
365                 bottom = top;
366
367                 /* now plot the ones that are of significant size */
368                 ps = ps_first;
369                 while (ps->next_ps) {
370                         ps = ps->next_ps;
371                         if (!ps)
372                                 continue;
373                         /* don't draw anything smaller than 2mb */
374                         if (ps->sample[i].pss > (100 * arg_scale_y)) {
375                                 top = bottom + ps->sample[i].pss;
376                                 /* draw a label with the process / PID */
377                                 if ((i == 1) || (ps->sample[i - 1].pss <= (100 * arg_scale_y)))
378                                         svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
379                                             time_to_graph(sampletime[i] - graph_start),
380                                             kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
381                                             ps->name,
382                                             ps->pid);
383                                 bottom = top;
384                         }
385                 }
386         }
387
388         /* debug output - full data dump */
389         svg("\n\n<!-- PSS map - csv format -->\n");
390         ps = ps_first;
391         while (ps->next_ps) {
392                 char _cleanup_free_ *enc_name = NULL;
393                 ps = ps->next_ps;
394                 if (!ps)
395                         continue;
396
397                 enc_name = xml_comment_encode(ps->name);
398                 if(!enc_name)
399                         continue;
400
401                 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
402
403                 for (i = 0; i < samples ; i++) {
404                         svg("%d," , ps->sample[i].pss);
405                 }
406                 svg(" -->\n");
407         }
408
409 }
410
411 static void svg_io_bi_bar(void) {
412         double max = 0.0;
413         double range;
414         int max_here = 0;
415         int i;
416
417         svg("<!-- IO utilization graph - In -->\n");
418
419         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
420
421         /*
422          * calculate rounding range
423          *
424          * We need to round IO data since IO block data is not updated on
425          * each poll. Applying a smoothing function loses some burst data,
426          * so keep the smoothing range short.
427          */
428         range = 0.25 / (1.0 / arg_hz);
429         if (range < 2.0)
430                 range = 2.0; /* no smoothing */
431
432         /* surrounding box */
433         svg_graph_box(5);
434
435         /* find the max IO first */
436         for (i = 1; i < samples; i++) {
437                 int start;
438                 int stop;
439                 double tot;
440
441                 start = MAX(i - ((range / 2) - 1), 0);
442                 stop = MIN(i + (range / 2), samples - 1);
443
444                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
445                       / (stop - start);
446                 if (tot > max) {
447                         max = tot;
448                         max_here = i;
449                 }
450                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
451                       / (stop - start);
452                 if (tot > max)
453                         max = tot;
454         }
455
456         /* plot bi */
457         for (i = 1; i < samples; i++) {
458                 int start;
459                 int stop;
460                 double tot;
461                 double pbi;
462
463                 start = MAX(i - ((range / 2) - 1), 0);
464                 stop = MIN(i + (range / 2), samples);
465
466                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
467                       / (stop - start);
468                 pbi = tot / max;
469
470                 if (pbi > 0.001)
471                         svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
472                             time_to_graph(sampletime[i - 1] - graph_start),
473                             (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
474                             time_to_graph(sampletime[i] - sampletime[i - 1]),
475                             pbi * (arg_scale_y * 5));
476
477                 /* labels around highest value */
478                 if (i == max_here) {
479                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
480                             time_to_graph(sampletime[i] - graph_start) + 5,
481                             ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
482                             max / 1024.0 / (interval / 1000000000.0));
483                 }
484         }
485 }
486
487 static void svg_io_bo_bar(void) {
488         double max = 0.0;
489         double range;
490         int max_here = 0;
491         int i;
492
493         svg("<!-- IO utilization graph - out -->\n");
494
495         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
496
497         /*
498          * calculate rounding range
499          *
500          * We need to round IO data since IO block data is not updated on
501          * each poll. Applying a smoothing function loses some burst data,
502          * so keep the smoothing range short.
503          */
504         range = 0.25 / (1.0 / arg_hz);
505         if (range < 2.0)
506                 range = 2.0; /* no smoothing */
507
508         /* surrounding box */
509         svg_graph_box(5);
510
511         /* find the max IO first */
512         for (i = 1; i < samples; i++) {
513                 int start;
514                 int stop;
515                 double tot;
516
517                 start = MAX(i - ((range / 2) - 1), 0);
518                 stop = MIN(i + (range / 2), samples - 1);
519
520                 tot = (double)(blockstat[stop].bi - blockstat[start].bi)
521                       / (stop - start);
522                 if (tot > max)
523                         max = tot;
524                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
525                       / (stop - start);
526                 if (tot > max) {
527                         max = tot;
528                         max_here = i;
529                 }
530         }
531
532         /* plot bo */
533         for (i = 1; i < samples; i++) {
534                 int start;
535                 int stop;
536                 double tot;
537                 double pbo;
538
539                 start = MAX(i - ((range / 2) - 1), 0);
540                 stop = MIN(i + (range / 2), samples);
541
542                 tot = (double)(blockstat[stop].bo - blockstat[start].bo)
543                       / (stop - start);
544                 pbo = tot / max;
545
546                 if (pbo > 0.001)
547                         svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
548                             time_to_graph(sampletime[i - 1] - graph_start),
549                             (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
550                             time_to_graph(sampletime[i] - sampletime[i - 1]),
551                             pbo * (arg_scale_y * 5));
552
553                 /* labels around highest bo value */
554                 if (i == max_here) {
555                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
556                             time_to_graph(sampletime[i] - graph_start) + 5,
557                             ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
558                             max / 1024.0 / (interval / 1000000000.0));
559                 }
560         }
561 }
562
563 static void svg_cpu_bar(void) {
564         int i;
565
566         svg("<!-- CPU utilization graph -->\n");
567
568         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
569         /* surrounding box */
570         svg_graph_box(5);
571
572         /* bars for each sample, proportional to the CPU util. */
573         for (i = 1; i < samples; i++) {
574                 int c;
575                 double trt;
576                 double ptrt;
577
578                 ptrt = trt = 0.0;
579
580                 for (c = 0; c < cpus; c++)
581                         trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
582
583                 trt = trt / 1000000000.0;
584
585                 trt = trt / (double)cpus;
586
587                 if (trt > 0.0)
588                         ptrt = trt / (sampletime[i] - sampletime[i - 1]);
589
590                 if (ptrt > 1.0)
591                         ptrt = 1.0;
592
593                 if (ptrt > 0.001) {
594                         svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
595                             time_to_graph(sampletime[i - 1] - graph_start),
596                             (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
597                             time_to_graph(sampletime[i] - sampletime[i - 1]),
598                             ptrt * (arg_scale_y * 5));
599                 }
600         }
601 }
602
603 static void svg_wait_bar(void) {
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                             ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
638                             time_to_graph(sampletime[i] - sampletime[i - 1]),
639                             ptwt * (arg_scale_y * 5));
640                 }
641         }
642 }
643
644
645 static void svg_entropy_bar(void) {
646         int i;
647
648         svg("<!-- entropy pool graph -->\n");
649
650         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
651         /* surrounding box */
652         svg_graph_box(5);
653
654         /* bars for each sample, scale 0-4096 */
655         for (i = 1; i < samples; i++) {
656                 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
657                 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
658                     time_to_graph(sampletime[i - 1] - graph_start),
659                     ((arg_scale_y * 5) - ((entropy_avail[i] / 4096.) * (arg_scale_y * 5))),
660                     time_to_graph(sampletime[i] - sampletime[i - 1]),
661                     (entropy_avail[i] / 4096.) * (arg_scale_y * 5));
662         }
663 }
664
665 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
666         /*
667          * walk the list of processes and return the next one to be
668          * painted
669          */
670         if (ps == ps_first)
671                 return ps->next_ps;
672
673         /* go deep */
674         if (ps->children)
675                 return ps->children;
676
677         /* find siblings */
678         if (ps->next)
679                 return ps->next;
680
681         /* go back for parent siblings */
682         while (1) {
683                 if (ps->parent)
684                         if (ps->parent->next)
685                                 return ps->parent->next;
686                 ps = ps->parent;
687                 if (!ps)
688                         return ps;
689         }
690
691         return NULL;
692 }
693
694 static int ps_filter(struct ps_struct *ps) {
695         if (!arg_filter)
696                 return 0;
697
698         /* can't draw data when there is only 1 sample (need start + stop) */
699         if (ps->first == ps->last)
700                 return -1;
701
702         /* don't filter kthreadd */
703         if (ps->pid == 2)
704                 return 0;
705
706         /* drop stuff that doesn't use any real CPU time */
707         if (ps->total <= 0.001)
708                 return -1;
709
710         return 0;
711 }
712
713 static void svg_do_initcall(int count_only) {
714         FILE _cleanup_pclose_ *f = NULL;
715         double t;
716         char func[256];
717         int ret;
718         int usecs;
719
720         /* can't plot initcall when disabled or in relative mode */
721         if (!initcall || arg_relative) {
722                 kcount = 0;
723                 return;
724         }
725
726         if (!count_only) {
727                 svg("<!-- initcall -->\n");
728
729                 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
730                 /* surrounding box */
731                 svg_graph_box(kcount);
732         }
733
734         kcount = 0;
735
736         /*
737          * Initcall graphing - parses dmesg buffer and displays kernel threads
738          * This somewhat uses the same methods and scaling to show processes
739          * but looks a lot simpler. It's overlaid entirely onto the PS graph
740          * when appropriate.
741          */
742
743         f = popen("dmesg", "r");
744         if (!f)
745                 return;
746
747         while (!feof(f)) {
748                 int c;
749                 int z = 0;
750                 char l[256];
751
752                 if (fgets(l, sizeof(l) - 1, f) == NULL)
753                         continue;
754
755                 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
756                            &t, func, &ret, &usecs);
757                 if (c != 4) {
758                         /* also parse initcalls done by module loading */
759                         c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
760                                    &t, func, &ret, &usecs);
761                         if (c != 4)
762                                 continue;
763                 }
764
765                 /* chop the +0xXX/0xXX stuff */
766                 while(func[z] != '+')
767                         z++;
768                 func[z] = 0;
769
770                 if (count_only) {
771                         /* filter out irrelevant stuff */
772                         if (usecs >= 1000)
773                                 kcount++;
774                         continue;
775                 }
776
777                 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
778                     func, t, usecs, ret);
779
780                 if (usecs < 1000)
781                         continue;
782
783                 /* rect */
784                 svg("  <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
785                     time_to_graph(t - (usecs / 1000000.0)),
786                     ps_to_graph(kcount),
787                     time_to_graph(usecs / 1000000.0),
788                     ps_to_graph(1));
789
790                 /* label */
791                 svg("  <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
792                     time_to_graph(t - (usecs / 1000000.0)) + 5,
793                     ps_to_graph(kcount) + 15,
794                     func,
795                     usecs / 1000000.0);
796
797                 kcount++;
798         }
799 }
800
801 static void svg_ps_bars(void) {
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                 char _cleanup_free_ *enc_name = NULL;
819
820                 double starttime;
821                 int t;
822
823                 enc_name = xml_comment_encode(ps->name);
824                 if(!enc_name)
825                         continue;
826
827                 /* leave some trace of what we actually filtered etc. */
828                 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
829                     ps->ppid, ps->total);
830
831                 /* it would be nice if we could use exec_start from /proc/pid/sched,
832                  * but it's unreliable and gives bogus numbers */
833                 starttime = sampletime[ps->first];
834
835                 if (!ps_filter(ps)) {
836                         /* remember where _to_ our children need to draw a line */
837                         ps->pos_x = time_to_graph(starttime - graph_start);
838                         ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
839                 } else {
840                         /* hook children to our parent coords instead */
841                         ps->pos_x = ps->parent->pos_x;
842                         ps->pos_y = ps->parent->pos_y;
843
844                         /* if this is the last child, we might still need to draw a connecting line */
845                         if ((!ps->next) && (ps->parent))
846                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
847                                     ps->parent->pos_x,
848                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
849                                     ps->parent->pos_x,
850                                     ps->parent->pos_y);
851                         continue;
852                 }
853
854                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
855                     time_to_graph(starttime - graph_start),
856                     ps_to_graph(j),
857                     time_to_graph(sampletime[ps->last] - starttime),
858                     ps_to_graph(1));
859
860                 /* paint cpu load over these */
861                 for (t = ps->first + 1; t < ps->last; t++) {
862                         double rt, prt;
863                         double wt, wrt;
864
865                         /* calculate over interval */
866                         rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
867                         wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
868
869                         prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
870                         wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
871
872                         /* this can happen if timekeeping isn't accurate enough */
873                         if (prt > 1.0)
874                                 prt = 1.0;
875                         if (wrt > 1.0)
876                                 wrt = 1.0;
877
878                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
879                                 continue;
880
881                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
882                             time_to_graph(sampletime[t - 1] - graph_start),
883                             ps_to_graph(j),
884                             time_to_graph(sampletime[t] - sampletime[t - 1]),
885                             ps_to_graph(wrt));
886
887                         /* draw cpu over wait - TODO figure out how/why run + wait > interval */
888                         svg("    <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
889                             time_to_graph(sampletime[t - 1] - graph_start),
890                             ps_to_graph(j + (1.0 - prt)),
891                             time_to_graph(sampletime[t] - sampletime[t - 1]),
892                             ps_to_graph(prt));
893                 }
894
895                 /* determine where to display the process name */
896                 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
897                         /* too small to fit label inside the box */
898                         w = ps->last;
899                 else
900                         w = ps->first;
901
902                 /* text label of process name */
903                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
904                     time_to_graph(sampletime[w] - graph_start) + 5.0,
905                     ps_to_graph(j) + 14.0,
906                     ps->name,
907                     ps->pid,
908                     (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
909                 /* paint lines to the parent process */
910                 if (ps->parent) {
911                         /* horizontal part */
912                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
913                             time_to_graph(starttime - graph_start),
914                             ps_to_graph(j) + 10.0,
915                             ps->parent->pos_x,
916                             ps_to_graph(j) + 10.0);
917
918                         /* one vertical line connecting all the horizontal ones up */
919                         if (!ps->next)
920                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
921                                     ps->parent->pos_x,
922                                     ps_to_graph(j) + 10.0,
923                                     ps->parent->pos_x,
924                                     ps->parent->pos_y);
925                 }
926
927                 j++; /* count boxes */
928
929                 svg("\n");
930         }
931
932         /* last pass - determine when idle */
933         pid = getpid();
934         /* make sure we start counting from the point where we actually have
935          * data: assume that bootchart's first sample is when data started
936          */
937         ps = ps_first;
938         while (ps->next_ps) {
939                 ps = ps->next_ps;
940                 if (ps->pid == pid)
941                         break;
942         }
943
944         for (i = ps->first; i < samples - (arg_hz / 2); i++) {
945                 double crt;
946                 double brt;
947                 int c;
948
949                 /* subtract bootchart cpu utilization from total */
950                 crt = 0.0;
951                 for (c = 0; c < cpus; c++)
952                         crt += cpustat[c].sample[i + ((int)arg_hz / 2)].runtime - cpustat[c].sample[i].runtime;
953                 brt = ps->sample[i + ((int)arg_hz / 2)].runtime - ps->sample[i].runtime;
954
955                 /*
956                  * our definition of "idle":
957                  *
958                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
959                  * defaults to 4.0%, which experimentally, is where atom idles
960                  */
961                 if ((crt - brt) < (interval / 2.0)) {
962                         idletime = sampletime[i] - graph_start;
963                         svg("\n<!-- idle detected at %.03f seconds -->\n",
964                             idletime);
965                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
966                             time_to_graph(idletime),
967                             -arg_scale_y,
968                             time_to_graph(idletime),
969                             ps_to_graph(pcount) + arg_scale_y);
970                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
971                             time_to_graph(idletime) + 5.0,
972                             ps_to_graph(pcount) + arg_scale_y,
973                             idletime);
974                         break;
975                 }
976         }
977 }
978
979 static void svg_top_ten_cpu(void) {
980         struct ps_struct *top[10];
981         struct ps_struct emptyps = {};
982         struct ps_struct *ps;
983         int n, m;
984
985         for (n = 0; n < (int) ELEMENTSOF(top); 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 - <![CDATA[%s]]> [%d]</text>\n",
1005                     20 + (n * 13),
1006                     top[n]->total,
1007                     top[n]->name,
1008                     top[n]->pid);
1009 }
1010
1011 static void svg_top_ten_pss(void) {
1012         struct ps_struct *top[10];
1013         struct ps_struct emptyps = {};
1014         struct ps_struct *ps;
1015         int n, m;
1016
1017         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1018                 top[n] = &emptyps;
1019
1020         /* walk all ps's and setup ptrs */
1021         ps = ps_first;
1022         while ((ps = get_next_ps(ps))) {
1023                 for (n = 0; n < 10; n++) {
1024                         if (ps->pss_max <= top[n]->pss_max)
1025                                 continue;
1026                         /* cascade insert */
1027                         for (m = 9; m > n; m--)
1028                                 top[m] = top[m-1];
1029                         top[n] = ps;
1030                         break;
1031                 }
1032         }
1033
1034         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1035         for (n = 0; n < 10; n++)
1036                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1037                     20 + (n * 13),
1038                     top[n]->pss_max,
1039                     top[n]->name,
1040                     top[n]->pid);
1041 }
1042
1043 void svg_do(const char *build) {
1044         struct ps_struct *ps;
1045
1046         memset(&str, 0, sizeof(str));
1047
1048         ps = ps_first;
1049
1050         /* count initcall thread count first */
1051         svg_do_initcall(1);
1052         ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1053
1054         /* then count processes */
1055         while ((ps = get_next_ps(ps))) {
1056                 if (!ps_filter(ps))
1057                         pcount++;
1058                 else
1059                         pfiltered++;
1060         }
1061         psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1062
1063         esize = (arg_entropy ? arg_scale_y * 7 : 0);
1064
1065         /* after this, we can draw the header with proper sizing */
1066         svg_header();
1067
1068         svg("<g transform=\"translate(10,400)\">\n");
1069         svg_io_bi_bar();
1070         svg("</g>\n\n");
1071
1072         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1073         svg_io_bo_bar();
1074         svg("</g>\n\n");
1075
1076         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1077         svg_cpu_bar();
1078         svg("</g>\n\n");
1079
1080         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1081         svg_wait_bar();
1082         svg("</g>\n\n");
1083
1084         if (kcount) {
1085                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1086                 svg_do_initcall(0);
1087                 svg("</g>\n\n");
1088         }
1089
1090         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1091         svg_ps_bars();
1092         svg("</g>\n\n");
1093
1094         svg("<g transform=\"translate(10,  0)\">\n");
1095         svg_title(build);
1096         svg("</g>\n\n");
1097
1098         svg("<g transform=\"translate(10,200)\">\n");
1099         svg_top_ten_cpu();
1100         svg("</g>\n\n");
1101
1102         if (arg_entropy) {
1103                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1104                 svg_entropy_bar();
1105                 svg("</g>\n\n");
1106         }
1107
1108         if (arg_pss) {
1109                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1110                 svg_pss_graph();
1111                 svg("</g>\n\n");
1112
1113                 svg("<g transform=\"translate(410,200)\">\n");
1114                 svg_top_ten_pss();
1115                 svg("</g>\n\n");
1116         }
1117
1118         /* svg footer */
1119         svg("\n</svg>\n");
1120 }