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