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