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