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