chiark / gitweb /
c21f6e86b425899f77fb762d37554901646bd17a
[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 float psize = 0;
72 static float ksize = 0;
73 static float 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         float w;
80         float 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.cpu   { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
127         svg("      rect.wait  { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
128         svg("      rect.bi    { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
129         svg("      rect.bo    { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
130         svg("      rect.ps    { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
131         svg("      rect.krnl  { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
132         svg("      rect.box   { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
133         svg("      rect.clrw  { stroke-width: 0; fill-opacity: 0.7;}\n");
134         svg("      line       { stroke: rgb(64,64,64); stroke-width: 1; }\n");
135         svg("//    line.sec1  { }\n");
136         svg("      line.sec5  { stroke-width: 2; }\n");
137         svg("      line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
138         svg("      line.dot   { stroke-dasharray: 2 4; }\n");
139         svg("      line.idle  { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
140
141         svg("      .run       { font-size: 8; font-style: italic; }\n");
142         svg("      text       { font-family: Verdana, Helvetica; font-size: 10; }\n");
143         svg("      text.sec   { font-size: 8; }\n");
144         svg("      text.t1    { font-size: 24; }\n");
145         svg("      text.t2    { font-size: 12; }\n");
146         svg("      text.idle  { font-size: 18; }\n");
147
148         svg("    ]]>\n   </style>\n</defs>\n\n");
149 }
150
151 static void svg_title(const char *build) {
152         char cmdline[256] = "";
153         char filename[PATH_MAX];
154         char buf[256];
155         char rootbdev[16] = "Unknown";
156         char model[256] = "Unknown";
157         char date[256] = "Unknown";
158         char cpu[256] = "Unknown";
159         char *c;
160         FILE *f;
161         time_t t;
162         int fd;
163         struct utsname uts;
164
165         /* grab /proc/cmdline */
166         fd = openat(procfd, "cmdline", O_RDONLY);
167         f = fdopen(fd, "r");
168         if (f) {
169                 if (!fgets(cmdline, 255, f))
170                         sprintf(cmdline, "Unknown");
171                 fclose(f);
172         }
173
174         /* extract root fs so we can find disk model name in sysfs */
175         /* FIXME: this works only in the simple case */
176         c = strstr(cmdline, "root=/dev/");
177         if (c) {
178                 strncpy(rootbdev, &c[10], 3);
179                 rootbdev[3] = '\0';
180                 sprintf(filename, "block/%s/device/model", rootbdev);
181                 fd = openat(sysfd, filename, O_RDONLY);
182                 f = fdopen(fd, "r");
183                 if (f) {
184                         if (!fgets(model, 255, f))
185                                 fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
186                         fclose(f);
187                 }
188         }
189
190         /* various utsname parameters */
191         if (uname(&uts))
192                 fprintf(stderr, "Error getting uname info\n");
193
194         /* date */
195         t = time(NULL);
196         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
197
198         /* CPU type */
199         fd = openat(procfd, "cpuinfo", O_RDONLY);
200         f = fdopen(fd, "r");
201         if (f) {
202                 while (fgets(buf, 255, f)) {
203                         if (strstr(buf, "model name")) {
204                                 strncpy(cpu, &buf[13], 255);
205                                 break;
206                         }
207                 }
208                 fclose(f);
209         }
210
211         svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
212             uts.nodename, date);
213         svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
214             uts.sysname, uts.release, uts.version, uts.machine);
215         svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
216             cpu);
217         svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
218             model);
219         svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
220             cmdline);
221         svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
222             build);
223         svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
224         svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
225
226         if (idletime >= 0.0)
227                 svg("%.03fs", idletime);
228         else
229                 svg("Not detected");
230         svg("</text>\n");
231         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",
232             arg_hz, arg_samples_len, overrun, pscount, pfiltered);
233 }
234
235 static void svg_graph_box(int height) {
236         double d = 0.0;
237         int i = 0;
238         double finalsample = 0.0;
239         struct list_sample_data *sampledata_last;
240
241         sampledata_last = head;
242         LIST_FOREACH_BEFORE(link, sampledata, head) {
243                 sampledata_last = sampledata;
244         }
245
246         finalsample = sampledata_last->sampletime;
247
248         /* outside box, fill */
249         svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
250             time_to_graph(0.0),
251             time_to_graph(finalsample - graph_start),
252             ps_to_graph(height));
253
254         for (d = graph_start; d <= finalsample;
255              d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
256                 /* lines for each second */
257                 if (i % 50 == 0)
258                         svg("  <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
259                             time_to_graph(d - graph_start),
260                             time_to_graph(d - graph_start),
261                             ps_to_graph(height));
262                 else if (i % 10 == 0)
263                         svg("  <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
264                             time_to_graph(d - graph_start),
265                             time_to_graph(d - graph_start),
266                             ps_to_graph(height));
267                 else
268                         svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
269                             time_to_graph(d - graph_start),
270                             time_to_graph(d - graph_start),
271                             ps_to_graph(height));
272
273                 /* time label */
274                 if (i % 10 == 0)
275                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
276                             time_to_graph(d - graph_start),
277                             -5.0,
278                             d - graph_start);
279
280                 i++;
281         }
282 }
283
284 /* xml comments must not contain "--" */
285 static char* xml_comment_encode(const char* name) {
286         char *enc_name, *p;
287
288         enc_name = strdup(name);
289         if (!enc_name)
290                 return NULL;
291
292         for (p = enc_name; *p; p++)
293                 if (p[0] == '-' && p[1] == '-')
294                         p[1] = '_';
295
296         return enc_name;
297 }
298
299 static void svg_pss_graph(void) {
300         struct ps_struct *ps;
301         int i;
302         struct list_sample_data *sampledata_last;
303
304         sampledata_last = head;
305         LIST_FOREACH_BEFORE(link, sampledata, head) {
306                 sampledata_last = sampledata;
307         }
308
309
310         svg("\n\n<!-- Pss memory size graph -->\n");
311
312         svg("\n  <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
313
314         /* vsize 1000 == 1000mb */
315         svg_graph_box(100);
316         /* draw some hlines for usable memory sizes */
317         for (i = 100000; i < 1000000; i += 100000) {
318                 svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
319                         time_to_graph(.0),
320                         kb_to_graph(i),
321                         time_to_graph(sampledata_last->sampletime - graph_start),
322                         kb_to_graph(i));
323                 svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
324                     time_to_graph(sampledata_last->sampletime - graph_start) + 5,
325                     kb_to_graph(i), (1000000 - i) / 1000);
326         }
327         svg("\n");
328
329         /* now plot the graph itself */
330         i = 1;
331         prev_sampledata = head;
332         LIST_FOREACH_BEFORE(link, sampledata, head) {
333                 int bottom;
334                 int top;
335                 struct ps_sched_struct *cross_place;
336
337                 bottom = 0;
338                 top = 0;
339
340                 /* put all the small pss blocks into the bottom */
341                 ps = ps_first;
342                 while (ps->next_ps) {
343                         ps = ps->next_ps;
344                         if (!ps)
345                                 continue;
346                         ps->sample = ps->first;
347                         while (ps->sample->next) {
348                                 ps->sample = ps->sample->next;
349                                 if (ps->sample->sampledata == sampledata)
350                                         break;
351                         }
352                         if (ps->sample->sampledata == sampledata) {
353                                 if (ps->sample->pss <= (100 * arg_scale_y))
354                                         top += ps->sample->pss;
355                                 break;
356                         }
357                 }
358                 while (ps->sample->cross) {
359                         cross_place = ps->sample->cross;
360                         ps = ps->sample->cross->ps_new;
361                         ps->sample = cross_place;
362                         if (ps->sample->pss <= (100 * arg_scale_y))
363                                 top += ps->sample->pss;
364                 }
365
366                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
367                     "rgb(64,64,64)",
368                     time_to_graph(prev_sampledata->sampletime - graph_start),
369                     kb_to_graph(1000000.0 - top),
370                     time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
371                     kb_to_graph(top - bottom));
372                 bottom = top;
373
374                 /* now plot the ones that are of significant size */
375                 ps = ps_first;
376                 while (ps->next_ps) {
377                         ps = ps->next_ps;
378                         if (!ps)
379                                 continue;
380                         ps->sample = ps->first;
381                         while (ps->sample->next) {
382                                 ps->sample = ps->sample->next;
383                                 if (ps->sample->sampledata == sampledata)
384                                         break;
385                         }
386                         /* don't draw anything smaller than 2mb */
387                         if (ps->sample->sampledata == sampledata) {
388                                 if (ps->sample->pss > (100 * arg_scale_y)) {
389                                 top = bottom + ps->sample->pss;
390                                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
391                                   colorwheel[ps->pid % 12],
392                                   time_to_graph(prev_sampledata->sampletime - graph_start),
393                                   kb_to_graph(1000000.0 - top),
394                                   time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
395                                   kb_to_graph(top - bottom));
396                                 bottom = top;
397                                 }
398                                 break;
399                         }
400                 }
401                 while ((cross_place = ps->sample->cross)) {
402                         ps = ps->sample->cross->ps_new;
403                         ps->sample = cross_place;
404                         if (ps->sample->pss > (100 * arg_scale_y)) {
405                                 top = bottom + ps->sample->pss;
406                                 svg("    <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
407                                   colorwheel[ps->pid % 12],
408                                   time_to_graph(prev_sampledata->sampletime - graph_start),
409                                   kb_to_graph(1000000.0 - top),
410                                   time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
411                                   kb_to_graph(top - bottom));
412                                 bottom = top;
413                         }
414                 }
415                 prev_sampledata = sampledata;
416                 i++;
417         }
418
419         /* overlay all the text labels */
420         i = 1;
421         LIST_FOREACH_BEFORE(link, sampledata, head) {
422                 int bottom;
423                 int top = 0;
424                 struct ps_sched_struct *prev_sample;
425                 struct ps_sched_struct *cross_place;
426
427                 /* put all the small pss blocks into the bottom */
428                 ps = ps_first->next_ps;
429                 while (ps->next_ps) {
430                         ps = ps->next_ps;
431                         if (!ps)
432                                 continue;
433                         ps->sample = ps->first;
434                         while (ps->sample->next) {
435                                 ps->sample = ps->sample->next;
436                                 if (ps->sample->sampledata == sampledata)
437                                         break;
438                         }
439                         if (ps->sample->sampledata == sampledata) {
440                                 if (ps->sample->pss <= (100 * arg_scale_y))
441                                         top += ps->sample->pss;
442                                 break;
443                         }
444                 }
445                 while ((cross_place = ps->sample->cross)) {
446                         ps = ps->sample->cross->ps_new;
447                         ps->sample = cross_place;
448                         if (ps->sample->pss <= (100 * arg_scale_y))
449                                 top += ps->sample->pss;
450                 }
451                 bottom = top;
452
453                 /* now plot the ones that are of significant size */
454                 ps = ps_first;
455                 while (ps->next_ps) {
456                         prev_sample = ps->sample;
457                         ps = ps->next_ps;
458                         if (!ps)
459                                 continue;
460                         ps->sample = ps->first;
461                         while (ps->sample->next) {
462                                 prev_sample = ps->sample;
463                                 ps->sample = ps->sample->next;
464                                 if (ps->sample->sampledata == sampledata)
465                                         break;
466                         }
467                         /* don't draw anything smaller than 2mb */
468                         if (ps->sample->sampledata == sampledata) {
469                                 if (ps->sample->pss > (100 * arg_scale_y)) {
470                                         top = bottom + ps->sample->pss;
471                                         /* draw a label with the process / PID */
472                                         if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
473                                                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
474                                                     time_to_graph(sampledata->sampletime - graph_start),
475                                                     kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
476                                                     ps->name,
477                                                     ps->pid);
478                                         bottom = top;
479                                 }
480                                 break;
481                         }
482                 }
483                 while ((cross_place = ps->sample->cross)) {
484                         ps = ps->sample->cross->ps_new;
485                         ps->sample = cross_place;
486                         prev_sample = ps->sample->prev;
487                         if (ps->sample->pss > (100 * arg_scale_y)) {
488                                 top = bottom + ps->sample->pss;
489                                 /* draw a label with the process / PID */
490                                 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
491                                         svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
492                                             time_to_graph(sampledata->sampletime - graph_start),
493                                             kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
494                                             ps->name,
495                                             ps->pid);
496                                 bottom = top;
497                         }
498                 }
499                 i++;
500         }
501
502         /* debug output - full data dump */
503         svg("\n\n<!-- PSS map - csv format -->\n");
504         ps = ps_first;
505         while (ps->next_ps) {
506                 _cleanup_free_ char *enc_name = NULL;
507                 ps = ps->next_ps;
508                 if (!ps)
509                         continue;
510
511                 enc_name = xml_comment_encode(ps->name);
512                 if (!enc_name)
513                         continue;
514
515                 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
516
517                 ps->sample = ps->first;
518                 while (ps->sample->next) {
519                         ps->sample = ps->sample->next;
520                         svg("%d," , ps->sample->pss);
521                 }
522                 svg(" -->\n");
523         }
524
525 }
526
527 static void svg_io_bi_bar(void) {
528         double max = 0.0;
529         double range;
530         int max_here = 0;
531         int i;
532         int k;
533         struct list_sample_data *start_sampledata;
534         struct list_sample_data *stop_sampledata;
535
536         svg("<!-- IO utilization graph - In -->\n");
537
538         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
539
540         /*
541          * calculate rounding range
542          *
543          * We need to round IO data since IO block data is not updated on
544          * each poll. Applying a smoothing function loses some burst data,
545          * so keep the smoothing range short.
546          */
547         range = 0.25 / (1.0 / arg_hz);
548         if (range < 2.0)
549                 range = 2.0; /* no smoothing */
550
551         /* surrounding box */
552         svg_graph_box(5);
553
554         /* find the max IO first */
555         i = 1;
556         LIST_FOREACH_BEFORE(link, sampledata, head) {
557                 int start;
558                 int stop;
559                 int diff;
560                 double tot;
561
562                 start = MAX(i - ((range / 2) - 1), 0);
563                 stop = MIN(i + (range / 2), samples - 1);
564                 diff = (stop - start);
565
566                 start_sampledata = sampledata;
567                 stop_sampledata = sampledata;
568
569                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
570                         start_sampledata = start_sampledata->link_next;
571                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
572                         stop_sampledata = stop_sampledata->link_prev;
573
574                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
575                         / diff;
576
577                 if (tot > max) {
578                         max = tot;
579                         max_here = i;
580                 }
581
582                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
583                         / diff;
584
585                 if (tot > max)
586                         max = tot;
587
588                 i++;
589         }
590
591         /* plot bi */
592         i = 1;
593         prev_sampledata = head;
594         LIST_FOREACH_BEFORE(link, sampledata, head) {
595                 int start;
596                 int stop;
597                 int diff;
598                 double tot;
599                 double pbi = 0;
600
601                 start = MAX(i - ((range / 2) - 1), 0);
602                 stop = MIN(i + (range / 2), samples);
603                 diff = (stop - start);
604
605                 start_sampledata = sampledata;
606                 stop_sampledata = sampledata;
607
608                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
609                         start_sampledata = start_sampledata->link_next;
610                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
611                         stop_sampledata = stop_sampledata->link_prev;
612
613                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
614                         / diff;
615
616                 if (max > 0)
617                         pbi = tot / max;
618
619                 if (pbi > 0.001)
620                         svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
621                             time_to_graph(prev_sampledata->sampletime - graph_start),
622                             (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
623                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
624                             pbi * (arg_scale_y * 5));
625
626                 /* labels around highest value */
627                 if (i == max_here) {
628                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
629                             time_to_graph(sampledata->sampletime - graph_start) + 5,
630                             ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
631                             max / 1024.0 / (interval / 1000000000.0));
632                 }
633                 i++;
634                 prev_sampledata = sampledata;
635         }
636 }
637
638 static void svg_io_bo_bar(void) {
639         double max = 0.0;
640         double range;
641         int max_here = 0;
642         int i;
643         int k;
644         struct list_sample_data *start_sampledata;
645         struct list_sample_data *stop_sampledata;
646
647         svg("<!-- IO utilization graph - out -->\n");
648
649         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
650
651         /*
652          * calculate rounding range
653          *
654          * We need to round IO data since IO block data is not updated on
655          * each poll. Applying a smoothing function loses some burst data,
656          * so keep the smoothing range short.
657          */
658         range = 0.25 / (1.0 / arg_hz);
659         if (range < 2.0)
660                 range = 2.0; /* no smoothing */
661
662         /* surrounding box */
663         svg_graph_box(5);
664
665         /* find the max IO first */
666         i = 0;
667         LIST_FOREACH_BEFORE(link, sampledata, head) {
668                 int start;
669                 int stop;
670                 int diff;
671                 double tot;
672
673                 start = MAX(i - ((range / 2) - 1), 0);
674                 stop = MIN(i + (range / 2), samples - 1);
675                 diff = (stop - start);
676
677                 start_sampledata = sampledata;
678                 stop_sampledata = sampledata;
679
680                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
681                         start_sampledata = start_sampledata->link_next;
682                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
683                         stop_sampledata = stop_sampledata->link_prev;
684
685                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
686                         / diff;
687                 if (tot > max)
688                         max = tot;
689                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
690                         / diff;
691                 if (tot > max) {
692                         max = tot;
693                         max_here = i;
694                 }
695                 i++;
696         }
697
698         /* plot bo */
699         prev_sampledata = head;
700         i=1;
701         LIST_FOREACH_BEFORE(link, sampledata, head) {
702                 int start;
703                 int stop;
704                 int diff;
705                 double tot;
706                 double pbo;
707
708                 tot = 0;
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></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                 /* paint lines to the parent process */
1104                 if (ps->parent) {
1105                         /* horizontal part */
1106                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1107                             time_to_graph(starttime - graph_start),
1108                             ps_to_graph(j) + 10.0,
1109                             ps->parent->pos_x,
1110                             ps_to_graph(j) + 10.0);
1111
1112                         /* one vertical line connecting all the horizontal ones up */
1113                         if (!ps->next)
1114                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1115                                     ps->parent->pos_x,
1116                                     ps_to_graph(j) + 10.0,
1117                                     ps->parent->pos_x,
1118                                     ps->parent->pos_y);
1119                 }
1120
1121                 j++; /* count boxes */
1122
1123                 svg("\n");
1124         }
1125
1126         /* last pass - determine when idle */
1127         pid = getpid();
1128         /* make sure we start counting from the point where we actually have
1129          * data: assume that bootchart's first sample is when data started
1130          */
1131
1132         ps = ps_first;
1133         while (ps->next_ps) {
1134                 ps = ps->next_ps;
1135                 if (ps->pid == pid)
1136                         break;
1137         }
1138
1139         /* need to know last node first */
1140         ps->sample = ps->first;
1141         i = ps->sample->next->sampledata->counter;
1142
1143         while (ps->sample->next && i<(samples-(arg_hz/2))) {
1144                 double crt;
1145                 double brt;
1146                 int c;
1147                 int ii;
1148                 struct ps_sched_struct *sample_hz;
1149
1150                 ps->sample = ps->sample->next;
1151                 sample_hz = ps->sample;
1152                 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1153                         sample_hz = sample_hz->next;
1154
1155                 /* subtract bootchart cpu utilization from total */
1156                 crt = 0.0;
1157                 for (c = 0; c < cpus; c++)
1158                         crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1159                 brt = sample_hz->runtime - ps->sample->runtime;
1160                 /*
1161                  * our definition of "idle":
1162                  *
1163                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
1164                  * defaults to 4.0%, which experimentally, is where atom idles
1165                  */
1166                 if ((crt - brt) < (interval / 2.0)) {
1167                         idletime = ps->sample->sampledata->sampletime - graph_start;
1168                         svg("\n<!-- idle detected at %.03f seconds -->\n",
1169                             idletime);
1170                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1171                             time_to_graph(idletime),
1172                             -arg_scale_y,
1173                             time_to_graph(idletime),
1174                             ps_to_graph(pcount) + arg_scale_y);
1175                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1176                             time_to_graph(idletime) + 5.0,
1177                             ps_to_graph(pcount) + arg_scale_y,
1178                             idletime);
1179                         break;
1180                 }
1181                 i++;
1182         }
1183 }
1184
1185 static void svg_top_ten_cpu(void) {
1186         struct ps_struct *top[10];
1187         struct ps_struct emptyps = {};
1188         struct ps_struct *ps;
1189         int n, m;
1190
1191         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1192                 top[n] = &emptyps;
1193
1194         /* walk all ps's and setup ptrs */
1195         ps = ps_first;
1196         while ((ps = get_next_ps(ps))) {
1197                 for (n = 0; n < 10; n++) {
1198                         if (ps->total <= top[n]->total)
1199                                 continue;
1200                         /* cascade insert */
1201                         for (m = 9; m > n; m--)
1202                                 top[m] = top[m-1];
1203                         top[n] = ps;
1204                         break;
1205                 }
1206         }
1207
1208         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1209         for (n = 0; n < 10; n++)
1210                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1211                     20 + (n * 13),
1212                     top[n]->total,
1213                     top[n]->name,
1214                     top[n]->pid);
1215 }
1216
1217 static void svg_top_ten_pss(void) {
1218         struct ps_struct *top[10];
1219         struct ps_struct emptyps = {};
1220         struct ps_struct *ps;
1221         int n, m;
1222
1223         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1224                 top[n] = &emptyps;
1225
1226         /* walk all ps's and setup ptrs */
1227         ps = ps_first;
1228         while ((ps = get_next_ps(ps))) {
1229                 for (n = 0; n < 10; n++) {
1230                         if (ps->pss_max <= top[n]->pss_max)
1231                                 continue;
1232                         /* cascade insert */
1233                         for (m = 9; m > n; m--)
1234                                 top[m] = top[m-1];
1235                         top[n] = ps;
1236                         break;
1237                 }
1238         }
1239
1240         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1241         for (n = 0; n < 10; n++)
1242                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1243                     20 + (n * 13),
1244                     top[n]->pss_max,
1245                     top[n]->name,
1246                     top[n]->pid);
1247 }
1248
1249 void svg_do(const char *build) {
1250         struct ps_struct *ps;
1251
1252         memset(&str, 0, sizeof(str));
1253
1254         ps = ps_first;
1255
1256         /* count initcall thread count first */
1257         svg_do_initcall(1);
1258         ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1259
1260         /* then count processes */
1261         while ((ps = get_next_ps(ps))) {
1262                 if (!ps_filter(ps))
1263                         pcount++;
1264                 else
1265                         pfiltered++;
1266         }
1267         psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1268
1269         esize = (arg_entropy ? arg_scale_y * 7 : 0);
1270
1271         /* after this, we can draw the header with proper sizing */
1272         svg_header();
1273
1274         svg("<g transform=\"translate(10,400)\">\n");
1275         svg_io_bi_bar();
1276         svg("</g>\n\n");
1277
1278         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1279         svg_io_bo_bar();
1280         svg("</g>\n\n");
1281
1282         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1283         svg_cpu_bar();
1284         svg("</g>\n\n");
1285
1286         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1287         svg_wait_bar();
1288         svg("</g>\n\n");
1289
1290         if (kcount) {
1291                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1292                 svg_do_initcall(0);
1293                 svg("</g>\n\n");
1294         }
1295
1296         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1297         svg_ps_bars();
1298         svg("</g>\n\n");
1299
1300         svg("<g transform=\"translate(10,  0)\">\n");
1301         svg_title(build);
1302         svg("</g>\n\n");
1303
1304         svg("<g transform=\"translate(10,200)\">\n");
1305         svg_top_ten_cpu();
1306         svg("</g>\n\n");
1307
1308         if (arg_entropy) {
1309                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1310                 svg_entropy_bar();
1311                 svg("</g>\n\n");
1312         }
1313
1314         if (arg_pss) {
1315                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1316                 svg_pss_graph();
1317                 svg("</g>\n\n");
1318
1319                 svg("<g transform=\"translate(410,200)\">\n");
1320                 svg_top_ten_pss();
1321                 svg("</g>\n\n");
1322         }
1323
1324         /* svg footer */
1325         svg("\n</svg>\n");
1326 }