chiark / gitweb /
Remove some dead code
[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 = NULL;
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 = NULL;
822
823                 double starttime;
824                 int t;
825
826                 enc_name = xml_comment_encode(ps->name);
827                 if(!enc_name)
828                         continue;
829
830                 /* leave some trace of what we actually filtered etc. */
831                 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
832                     ps->ppid, ps->total);
833
834                 /* it would be nice if we could use exec_start from /proc/pid/sched,
835                  * but it's unreliable and gives bogus numbers */
836                 starttime = sampletime[ps->first];
837
838                 if (!ps_filter(ps)) {
839                         /* remember where _to_ our children need to draw a line */
840                         ps->pos_x = time_to_graph(starttime - graph_start);
841                         ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
842                 } else {
843                         /* hook children to our parent coords instead */
844                         ps->pos_x = ps->parent->pos_x;
845                         ps->pos_y = ps->parent->pos_y;
846
847                         /* if this is the last child, we might still need to draw a connecting line */
848                         if ((!ps->next) && (ps->parent))
849                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
850                                     ps->parent->pos_x,
851                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
852                                     ps->parent->pos_x,
853                                     ps->parent->pos_y);
854                         continue;
855                 }
856
857                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
858                     time_to_graph(starttime - graph_start),
859                     ps_to_graph(j),
860                     time_to_graph(sampletime[ps->last] - starttime),
861                     ps_to_graph(1));
862
863                 /* paint cpu load over these */
864                 for (t = ps->first + 1; t < ps->last; t++) {
865                         double rt, prt;
866                         double wt, wrt;
867
868                         /* calculate over interval */
869                         rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
870                         wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
871
872                         prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
873                         wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
874
875                         /* this can happen if timekeeping isn't accurate enough */
876                         if (prt > 1.0)
877                                 prt = 1.0;
878                         if (wrt > 1.0)
879                                 wrt = 1.0;
880
881                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
882                                 continue;
883
884                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
885                             time_to_graph(sampletime[t - 1] - graph_start),
886                             ps_to_graph(j),
887                             time_to_graph(sampletime[t] - sampletime[t - 1]),
888                             ps_to_graph(wrt));
889
890                         /* draw cpu over wait - TODO figure out how/why run + wait > interval */
891                         svg("    <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
892                             time_to_graph(sampletime[t - 1] - graph_start),
893                             ps_to_graph(j + (1.0 - prt)),
894                             time_to_graph(sampletime[t] - sampletime[t - 1]),
895                             ps_to_graph(prt));
896                 }
897
898                 /* determine where to display the process name */
899                 if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
900                         /* too small to fit label inside the box */
901                         w = ps->last;
902                 else
903                         w = ps->first;
904
905                 /* text label of process name */
906                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
907                     time_to_graph(sampletime[w] - graph_start) + 5.0,
908                     ps_to_graph(j) + 14.0,
909                     ps->name,
910                     ps->pid,
911                     (ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
912                 /* paint lines to the parent process */
913                 if (ps->parent) {
914                         /* horizontal part */
915                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
916                             time_to_graph(starttime - graph_start),
917                             ps_to_graph(j) + 10.0,
918                             ps->parent->pos_x,
919                             ps_to_graph(j) + 10.0);
920
921                         /* one vertical line connecting all the horizontal ones up */
922                         if (!ps->next)
923                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
924                                     ps->parent->pos_x,
925                                     ps_to_graph(j) + 10.0,
926                                     ps->parent->pos_x,
927                                     ps->parent->pos_y);
928                 }
929
930                 j++; /* count boxes */
931
932                 svg("\n");
933         }
934
935         /* last pass - determine when idle */
936         pid = getpid();
937         /* make sure we start counting from the point where we actually have
938          * data: assume that bootchart's first sample is when data started
939          */
940         ps = ps_first;
941         while (ps->next_ps) {
942                 ps = ps->next_ps;
943                 if (ps->pid == pid)
944                         break;
945         }
946
947         for (i = ps->first; i < samples - (arg_hz / 2); i++) {
948                 double crt;
949                 double brt;
950                 int c;
951
952                 /* subtract bootchart cpu utilization from total */
953                 crt = 0.0;
954                 for (c = 0; c < cpus; c++)
955                         crt += cpustat[c].sample[i + ((int)arg_hz / 2)].runtime - cpustat[c].sample[i].runtime;
956                 brt = ps->sample[i + ((int)arg_hz / 2)].runtime - ps->sample[i].runtime;
957
958                 /*
959                  * our definition of "idle":
960                  *
961                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
962                  * defaults to 4.0%, which experimentally, is where atom idles
963                  */
964                 if ((crt - brt) < (interval / 2.0)) {
965                         idletime = sampletime[i] - graph_start;
966                         svg("\n<!-- idle detected at %.03f seconds -->\n",
967                             idletime);
968                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
969                             time_to_graph(idletime),
970                             -arg_scale_y,
971                             time_to_graph(idletime),
972                             ps_to_graph(pcount) + arg_scale_y);
973                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
974                             time_to_graph(idletime) + 5.0,
975                             ps_to_graph(pcount) + arg_scale_y,
976                             idletime);
977                         break;
978                 }
979         }
980 }
981
982 static void svg_top_ten_cpu(void) {
983         struct ps_struct *top[10];
984         struct ps_struct emptyps;
985         struct ps_struct *ps;
986         int n, m;
987
988         memset(&emptyps, 0, sizeof(struct ps_struct));
989         for (n=0; n < 10; n++)
990                 top[n] = &emptyps;
991
992         /* walk all ps's and setup ptrs */
993         ps = ps_first;
994         while ((ps = get_next_ps(ps))) {
995                 for (n = 0; n < 10; n++) {
996                         if (ps->total <= top[n]->total)
997                                 continue;
998                         /* cascade insert */
999                         for (m = 9; m > n; m--)
1000                                 top[m] = top[m-1];
1001                         top[n] = ps;
1002                         break;
1003                 }
1004         }
1005
1006         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1007         for (n = 0; n < 10; n++)
1008                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1009                     20 + (n * 13),
1010                     top[n]->total,
1011                     top[n]->name,
1012                     top[n]->pid);
1013 }
1014
1015 static void svg_top_ten_pss(void) {
1016         struct ps_struct *top[10];
1017         struct ps_struct emptyps;
1018         struct ps_struct *ps;
1019         int n, m;
1020
1021         memset(&emptyps, 0, sizeof(struct ps_struct));
1022         for (n=0; n < 10; n++)
1023                 top[n] = &emptyps;
1024
1025         /* walk all ps's and setup ptrs */
1026         ps = ps_first;
1027         while ((ps = get_next_ps(ps))) {
1028                 for (n = 0; n < 10; n++) {
1029                         if (ps->pss_max <= top[n]->pss_max)
1030                                 continue;
1031                         /* cascade insert */
1032                         for (m = 9; m > n; m--)
1033                                 top[m] = top[m-1];
1034                         top[n] = ps;
1035                         break;
1036                 }
1037         }
1038
1039         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1040         for (n = 0; n < 10; n++)
1041                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1042                     20 + (n * 13),
1043                     top[n]->pss_max,
1044                     top[n]->name,
1045                     top[n]->pid);
1046 }
1047
1048 void svg_do(const char *build) {
1049         struct ps_struct *ps;
1050
1051         memset(&str, 0, sizeof(str));
1052
1053         ps = ps_first;
1054
1055         /* count initcall thread count first */
1056         svg_do_initcall(1);
1057         ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1058
1059         /* then count processes */
1060         while ((ps = get_next_ps(ps))) {
1061                 if (!ps_filter(ps))
1062                         pcount++;
1063                 else
1064                         pfiltered++;
1065         }
1066         psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1067
1068         esize = (arg_entropy ? arg_scale_y * 7 : 0);
1069
1070         /* after this, we can draw the header with proper sizing */
1071         svg_header();
1072
1073         svg("<g transform=\"translate(10,400)\">\n");
1074         svg_io_bi_bar();
1075         svg("</g>\n\n");
1076
1077         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1078         svg_io_bo_bar();
1079         svg("</g>\n\n");
1080
1081         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1082         svg_cpu_bar();
1083         svg("</g>\n\n");
1084
1085         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1086         svg_wait_bar();
1087         svg("</g>\n\n");
1088
1089         if (kcount) {
1090                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1091                 svg_do_initcall(0);
1092                 svg("</g>\n\n");
1093         }
1094
1095         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1096         svg_ps_bars();
1097         svg("</g>\n\n");
1098
1099         svg("<g transform=\"translate(10,  0)\">\n");
1100         svg_title(build);
1101         svg("</g>\n\n");
1102
1103         svg("<g transform=\"translate(10,200)\">\n");
1104         svg_top_ten_cpu();
1105         svg("</g>\n\n");
1106
1107         if (arg_entropy) {
1108                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1109                 svg_entropy_bar();
1110                 svg("</g>\n\n");
1111         }
1112
1113         if (arg_pss) {
1114                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1115                 svg_pss_graph();
1116                 svg("</g>\n\n");
1117
1118                 svg("<g transform=\"translate(410,200)\">\n");
1119                 svg_top_ten_pss();
1120                 svg("</g>\n\n");
1121         }
1122
1123         /* svg footer */
1124         svg("\n</svg>\n");
1125 }