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