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