chiark / gitweb /
use memzero(foo, length); for all memset(foo, 0, length); calls
[elogind.git] / src / bootchart / svg.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2009-2013 Intel Corporation
7
8   Authors:
9     Auke Kok <auke-jan.h.kok@intel.com>
10
11   systemd is free software; you can redistribute it and/or modify it
12   under the terms of the GNU Lesser General Public License as published by
13   the Free Software Foundation; either version 2.1 of the License, or
14   (at your option) any later version.
15
16   systemd is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public License
22   along with systemd; If not, see <http://www.gnu.org/licenses/>.
23  ***/
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <sys/utsname.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #include "util.h"
37 #include "macro.h"
38 #include "store.h"
39 #include "svg.h"
40 #include "bootchart.h"
41 #include "list.h"
42
43 #define time_to_graph(t) ((t) * arg_scale_x)
44 #define ps_to_graph(n) ((n) * arg_scale_y)
45 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
46 #define to_color(n) (192.0 - ((n) * 192.0))
47
48 static char str[8092];
49
50 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
51
52 static const char * const colorwheel[12] = {
53         "rgb(255,32,32)",  // red
54         "rgb(32,192,192)", // cyan
55         "rgb(255,128,32)", // orange
56         "rgb(128,32,192)", // blue-violet
57         "rgb(255,255,32)", // yellow
58         "rgb(192,32,128)", // red-violet
59         "rgb(32,255,32)",  // green
60         "rgb(255,64,32)",  // red-orange
61         "rgb(32,32,255)",  // blue
62         "rgb(255,192,32)", // yellow-orange
63         "rgb(192,32,192)", // violet
64         "rgb(32,192,32)"   // yellow-green
65 };
66
67 static double idletime = -1.0;
68 static int pfiltered = 0;
69 static int pcount = 0;
70 static int kcount = 0;
71 static double psize = 0;
72 static double ksize = 0;
73 static double esize = 0;
74 static struct list_sample_data *sampledata;
75 static struct list_sample_data *prev_sampledata;
76 extern struct list_sample_data *head;
77
78 static void svg_header(void) {
79         double w;
80         double h;
81         struct list_sample_data *sampledata_last;
82
83         sampledata = head;
84         LIST_FIND_TAIL(link, sampledata, head);
85         sampledata_last = head;
86         LIST_FOREACH_BEFORE(link, sampledata, head) {
87                 sampledata_last = sampledata;
88         }
89
90         /* min width is about 1600px due to the label */
91         w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
92         w = ((w < 1600.0) ? 1600.0 : w);
93
94         /* height is variable based on pss, psize, ksize */
95         h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
96             + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
97             + psize + ksize + esize;
98
99         svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
100         svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
101         svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
102
103         //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
104         svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
105             w, h);
106         svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
107
108         /* write some basic info as a comment, including some help */
109         svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
110         svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that       -->\n");
111         svg("<!-- render these files properly but more slowly are ImageMagick, gimp,  -->\n");
112         svg("<!-- inkscape, etc. To display the files on your system, just point      -->\n");
113         svg("<!-- your browser to file:///run/log/ and click. This bootchart was      -->\n\n");
114
115         svg("<!-- generated by bootchart version %s, running with options:  -->\n", VERSION);
116         svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
117         svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
118         svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
119         svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
120         svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
121
122         /* style sheet */
123         svg("<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n");
124
125         svg("      rect       { stroke-width: 1; }\n");
126         svg("      rect.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                 pbo = 0;
709
710                 start = MAX(i - ((range / 2) - 1), 0);
711                 stop = MIN(i + (range / 2), samples);
712                 diff = (stop - start);
713
714                 start_sampledata = sampledata;
715                 stop_sampledata = sampledata;
716
717                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
718                         start_sampledata = start_sampledata->link_next;
719                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
720                         stop_sampledata = stop_sampledata->link_prev;
721
722                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
723                         / diff;
724
725                 if (max > 0)
726                         pbo = tot / max;
727
728                 if (pbo > 0.001)
729                         svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
730                             time_to_graph(prev_sampledata->sampletime - graph_start),
731                             (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
732                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
733                             pbo * (arg_scale_y * 5));
734
735                 /* labels around highest bo value */
736                 if (i == max_here) {
737                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
738                             time_to_graph(sampledata->sampletime - graph_start) + 5,
739                             ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
740                             max / 1024.0 / (interval / 1000000000.0));
741                 }
742                 i++;
743                 prev_sampledata = sampledata;
744         }
745 }
746
747 static void svg_cpu_bar(void) {
748
749         svg("<!-- CPU utilization graph -->\n");
750
751         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
752         /* surrounding box */
753         svg_graph_box(5);
754
755         /* bars for each sample, proportional to the CPU util. */
756         prev_sampledata = head;
757         LIST_FOREACH_BEFORE(link, sampledata, head) {
758                 int c;
759                 double trt;
760                 double ptrt;
761
762                 ptrt = trt = 0.0;
763
764                 for (c = 0; c < cpus; c++)
765                         trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
766
767                 trt = trt / 1000000000.0;
768
769                 trt = trt / (double)cpus;
770
771                 if (trt > 0.0)
772                         ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
773
774                 if (ptrt > 1.0)
775                         ptrt = 1.0;
776
777                 if (ptrt > 0.001) {
778                         svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
779                             time_to_graph(prev_sampledata->sampletime - graph_start),
780                             (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
781                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
782                             ptrt * (arg_scale_y * 5));
783                 }
784                 prev_sampledata = sampledata;
785         }
786 }
787
788 static void svg_wait_bar(void) {
789
790         svg("<!-- Wait time aggregation box -->\n");
791
792         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
793
794         /* surrounding box */
795         svg_graph_box(5);
796
797         /* bars for each sample, proportional to the CPU util. */
798         prev_sampledata = head;
799         LIST_FOREACH_BEFORE(link, sampledata, head) {
800                 int c;
801                 double twt;
802                 double ptwt;
803
804                 ptwt = twt = 0.0;
805
806                 for (c = 0; c < cpus; c++)
807                         twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
808
809                 twt = twt / 1000000000.0;
810
811                 twt = twt / (double)cpus;
812
813                 if (twt > 0.0)
814                         ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
815
816                 if (ptwt > 1.0)
817                         ptwt = 1.0;
818
819                 if (ptwt > 0.001) {
820                         svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
821                             time_to_graph(prev_sampledata->sampletime - graph_start),
822                             ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
823                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
824                             ptwt * (arg_scale_y * 5));
825                 }
826                 prev_sampledata = sampledata;
827         }
828 }
829
830
831 static void svg_entropy_bar(void) {
832
833         svg("<!-- entropy pool graph -->\n");
834
835         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
836         /* surrounding box */
837         svg_graph_box(5);
838
839         /* bars for each sample, scale 0-4096 */
840         prev_sampledata = head;
841         LIST_FOREACH_BEFORE(link, sampledata, head) {
842                 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
843                 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
844                     time_to_graph(prev_sampledata->sampletime - graph_start),
845                     ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
846                     time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
847                     (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
848                 prev_sampledata = sampledata;
849         }
850 }
851
852 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
853         /*
854          * walk the list of processes and return the next one to be
855          * painted
856          */
857         if (ps == ps_first)
858                 return ps->next_ps;
859
860         /* go deep */
861         if (ps->children)
862                 return ps->children;
863
864         /* find siblings */
865         if (ps->next)
866                 return ps->next;
867
868         /* go back for parent siblings */
869         while (1) {
870                 if (ps->parent)
871                         if (ps->parent->next)
872                                 return ps->parent->next;
873                 ps = ps->parent;
874                 if (!ps)
875                         return ps;
876         }
877
878         return NULL;
879 }
880
881 static int ps_filter(struct ps_struct *ps) {
882         if (!arg_filter)
883                 return 0;
884
885         /* can't draw data when there is only 1 sample (need start + stop) */
886         if (ps->first == ps->last)
887                 return -1;
888
889         /* don't filter kthreadd */
890         if (ps->pid == 2)
891                 return 0;
892
893         /* drop stuff that doesn't use any real CPU time */
894         if (ps->total <= 0.001)
895                 return -1;
896
897         return 0;
898 }
899
900 static void svg_do_initcall(int count_only) {
901         _cleanup_pclose_ FILE *f = NULL;
902         double t;
903         char func[256];
904         int ret;
905         int usecs;
906
907         /* can't plot initcall when disabled or in relative mode */
908         if (!initcall || arg_relative) {
909                 kcount = 0;
910                 return;
911         }
912
913         if (!count_only) {
914                 svg("<!-- initcall -->\n");
915
916                 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
917                 /* surrounding box */
918                 svg_graph_box(kcount);
919         }
920
921         kcount = 0;
922
923         /*
924          * Initcall graphing - parses dmesg buffer and displays kernel threads
925          * This somewhat uses the same methods and scaling to show processes
926          * but looks a lot simpler. It's overlaid entirely onto the PS graph
927          * when appropriate.
928          */
929
930         f = popen("dmesg", "r");
931         if (!f)
932                 return;
933
934         while (!feof(f)) {
935                 int c;
936                 int z = 0;
937                 char l[256];
938
939                 if (fgets(l, sizeof(l) - 1, f) == NULL)
940                         continue;
941
942                 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
943                            &t, func, &ret, &usecs);
944                 if (c != 4) {
945                         /* also parse initcalls done by module loading */
946                         c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
947                                    &t, func, &ret, &usecs);
948                         if (c != 4)
949                                 continue;
950                 }
951
952                 /* chop the +0xXX/0xXX stuff */
953                 while(func[z] != '+')
954                         z++;
955                 func[z] = 0;
956
957                 if (count_only) {
958                         /* filter out irrelevant stuff */
959                         if (usecs >= 1000)
960                                 kcount++;
961                         continue;
962                 }
963
964                 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
965                     func, t, usecs, ret);
966
967                 if (usecs < 1000)
968                         continue;
969
970                 /* rect */
971                 svg("  <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
972                     time_to_graph(t - (usecs / 1000000.0)),
973                     ps_to_graph(kcount),
974                     time_to_graph(usecs / 1000000.0),
975                     ps_to_graph(1));
976
977                 /* label */
978                 svg("  <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
979                     time_to_graph(t - (usecs / 1000000.0)) + 5,
980                     ps_to_graph(kcount) + 15,
981                     func,
982                     usecs / 1000000.0);
983
984                 kcount++;
985         }
986 }
987
988 static void svg_ps_bars(void) {
989         struct ps_struct *ps;
990         int i = 0;
991         int j = 0;
992         int pid;
993         double w = 0.0;
994
995         svg("<!-- Process graph -->\n");
996
997         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
998
999         /* surrounding box */
1000         svg_graph_box(pcount);
1001
1002         /* pass 2 - ps boxes */
1003         ps = ps_first;
1004         while ((ps = get_next_ps(ps))) {
1005                 _cleanup_free_ char *enc_name = NULL;
1006                 double endtime;
1007                 double starttime;
1008                 int t;
1009
1010                 enc_name = xml_comment_encode(ps->name);
1011                 if (!enc_name)
1012                         continue;
1013
1014                 /* leave some trace of what we actually filtered etc. */
1015                 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1016                     ps->ppid, ps->total);
1017
1018                 starttime = ps->first->sampledata->sampletime;
1019
1020                 if (!ps_filter(ps)) {
1021                         /* remember where _to_ our children need to draw a line */
1022                         ps->pos_x = time_to_graph(starttime - graph_start);
1023                         ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1024                 } else if (ps->parent){
1025                         /* hook children to our parent coords instead */
1026                         ps->pos_x = ps->parent->pos_x;
1027                         ps->pos_y = ps->parent->pos_y;
1028
1029                         /* if this is the last child, we might still need to draw a connecting line */
1030                         if ((!ps->next) && (ps->parent))
1031                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1032                                     ps->parent->pos_x,
1033                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1034                                     ps->parent->pos_x,
1035                                     ps->parent->pos_y);
1036                         continue;
1037                 }
1038
1039                 endtime = ps->last->sampledata->sampletime;
1040                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1041                     time_to_graph(starttime - graph_start),
1042                     ps_to_graph(j),
1043                     time_to_graph(ps->last->sampledata->sampletime - starttime),
1044                     ps_to_graph(1));
1045
1046                 /* paint cpu load over these */
1047                 ps->sample = ps->first;
1048                 t = 1;
1049                 while (ps->sample->next) {
1050                         double rt, prt;
1051                         double wt, wrt;
1052                         struct ps_sched_struct *prev;
1053
1054                         prev = ps->sample;
1055                         ps->sample = ps->sample->next;
1056
1057                         /* calculate over interval */
1058                         rt = ps->sample->runtime - prev->runtime;
1059                         wt = ps->sample->waittime - prev->waittime;
1060
1061                         prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1062                         wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1063
1064                         /* this can happen if timekeeping isn't accurate enough */
1065                         if (prt > 1.0)
1066                                 prt = 1.0;
1067                         if (wrt > 1.0)
1068                                 wrt = 1.0;
1069
1070                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1071                                 continue;
1072
1073                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1074                             time_to_graph(prev->sampledata->sampletime - graph_start),
1075                             ps_to_graph(j),
1076                             time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1077                             ps_to_graph(wrt));
1078
1079                         /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1080                         svg("    <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1081                             time_to_graph(prev->sampledata->sampletime - graph_start),
1082                             ps_to_graph(j + (1.0 - prt)),
1083                             time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1084                             ps_to_graph(prt));
1085                         t++;
1086                 }
1087
1088                 /* determine where to display the process name */
1089                 if ((endtime - starttime) < 1.5)
1090                         /* too small to fit label inside the box */
1091                         w = endtime;
1092                 else
1093                         w = starttime;
1094
1095                 /* text label of process name */
1096                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
1097                     time_to_graph(w - graph_start) + 5.0,
1098                     ps_to_graph(j) + 14.0,
1099                     ps->name,
1100                     ps->pid,
1101                     (ps->last->runtime - ps->first->runtime) / 1000000000.0);
1102                 /* paint lines to the parent process */
1103                 if (ps->parent) {
1104                         /* horizontal part */
1105                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1106                             time_to_graph(starttime - graph_start),
1107                             ps_to_graph(j) + 10.0,
1108                             ps->parent->pos_x,
1109                             ps_to_graph(j) + 10.0);
1110
1111                         /* one vertical line connecting all the horizontal ones up */
1112                         if (!ps->next)
1113                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1114                                     ps->parent->pos_x,
1115                                     ps_to_graph(j) + 10.0,
1116                                     ps->parent->pos_x,
1117                                     ps->parent->pos_y);
1118                 }
1119
1120                 j++; /* count boxes */
1121
1122                 svg("\n");
1123         }
1124
1125         /* last pass - determine when idle */
1126         pid = getpid();
1127         /* make sure we start counting from the point where we actually have
1128          * data: assume that bootchart's first sample is when data started
1129          */
1130
1131         ps = ps_first;
1132         while (ps->next_ps) {
1133                 ps = ps->next_ps;
1134                 if (ps->pid == pid)
1135                         break;
1136         }
1137
1138         /* need to know last node first */
1139         ps->sample = ps->first;
1140         i = ps->sample->next->sampledata->counter;
1141
1142         while (ps->sample->next && i<(samples-(arg_hz/2))) {
1143                 double crt;
1144                 double brt;
1145                 int c;
1146                 int ii;
1147                 struct ps_sched_struct *sample_hz;
1148
1149                 ps->sample = ps->sample->next;
1150                 sample_hz = ps->sample;
1151                 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1152                         sample_hz = sample_hz->next;
1153
1154                 /* subtract bootchart cpu utilization from total */
1155                 crt = 0.0;
1156                 for (c = 0; c < cpus; c++)
1157                         crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1158                 brt = sample_hz->runtime - ps->sample->runtime;
1159                 /*
1160                  * our definition of "idle":
1161                  *
1162                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
1163                  * defaults to 4.0%, which experimentally, is where atom idles
1164                  */
1165                 if ((crt - brt) < (interval / 2.0)) {
1166                         idletime = ps->sample->sampledata->sampletime - graph_start;
1167                         svg("\n<!-- idle detected at %.03f seconds -->\n",
1168                             idletime);
1169                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1170                             time_to_graph(idletime),
1171                             -arg_scale_y,
1172                             time_to_graph(idletime),
1173                             ps_to_graph(pcount) + arg_scale_y);
1174                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1175                             time_to_graph(idletime) + 5.0,
1176                             ps_to_graph(pcount) + arg_scale_y,
1177                             idletime);
1178                         break;
1179                 }
1180                 i++;
1181         }
1182 }
1183
1184 static void svg_top_ten_cpu(void) {
1185         struct ps_struct *top[10];
1186         struct ps_struct emptyps = {};
1187         struct ps_struct *ps;
1188         int n, m;
1189
1190         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1191                 top[n] = &emptyps;
1192
1193         /* walk all ps's and setup ptrs */
1194         ps = ps_first;
1195         while ((ps = get_next_ps(ps))) {
1196                 for (n = 0; n < 10; n++) {
1197                         if (ps->total <= top[n]->total)
1198                                 continue;
1199                         /* cascade insert */
1200                         for (m = 9; m > n; m--)
1201                                 top[m] = top[m-1];
1202                         top[n] = ps;
1203                         break;
1204                 }
1205         }
1206
1207         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1208         for (n = 0; n < 10; n++)
1209                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1210                     20 + (n * 13),
1211                     top[n]->total,
1212                     top[n]->name,
1213                     top[n]->pid);
1214 }
1215
1216 static void svg_top_ten_pss(void) {
1217         struct ps_struct *top[10];
1218         struct ps_struct emptyps = {};
1219         struct ps_struct *ps;
1220         int n, m;
1221
1222         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1223                 top[n] = &emptyps;
1224
1225         /* walk all ps's and setup ptrs */
1226         ps = ps_first;
1227         while ((ps = get_next_ps(ps))) {
1228                 for (n = 0; n < 10; n++) {
1229                         if (ps->pss_max <= top[n]->pss_max)
1230                                 continue;
1231                         /* cascade insert */
1232                         for (m = 9; m > n; m--)
1233                                 top[m] = top[m-1];
1234                         top[n] = ps;
1235                         break;
1236                 }
1237         }
1238
1239         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1240         for (n = 0; n < 10; n++)
1241                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1242                     20 + (n * 13),
1243                     top[n]->pss_max,
1244                     top[n]->name,
1245                     top[n]->pid);
1246 }
1247
1248 void svg_do(const char *build) {
1249         struct ps_struct *ps;
1250
1251         memzero(&str, sizeof(str));
1252
1253         ps = ps_first;
1254
1255         /* count initcall thread count first */
1256         svg_do_initcall(1);
1257         ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1258
1259         /* then count processes */
1260         while ((ps = get_next_ps(ps))) {
1261                 if (!ps_filter(ps))
1262                         pcount++;
1263                 else
1264                         pfiltered++;
1265         }
1266         psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1267
1268         esize = (arg_entropy ? arg_scale_y * 7 : 0);
1269
1270         /* after this, we can draw the header with proper sizing */
1271         svg_header();
1272
1273         svg("<g transform=\"translate(10,400)\">\n");
1274         svg_io_bi_bar();
1275         svg("</g>\n\n");
1276
1277         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1278         svg_io_bo_bar();
1279         svg("</g>\n\n");
1280
1281         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1282         svg_cpu_bar();
1283         svg("</g>\n\n");
1284
1285         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1286         svg_wait_bar();
1287         svg("</g>\n\n");
1288
1289         if (kcount) {
1290                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1291                 svg_do_initcall(0);
1292                 svg("</g>\n\n");
1293         }
1294
1295         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1296         svg_ps_bars();
1297         svg("</g>\n\n");
1298
1299         svg("<g transform=\"translate(10,  0)\">\n");
1300         svg_title(build);
1301         svg("</g>\n\n");
1302
1303         svg("<g transform=\"translate(10,200)\">\n");
1304         svg_top_ten_cpu();
1305         svg("</g>\n\n");
1306
1307         if (arg_entropy) {
1308                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1309                 svg_entropy_bar();
1310                 svg("</g>\n\n");
1311         }
1312
1313         if (arg_pss) {
1314                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1315                 svg_pss_graph();
1316                 svg("</g>\n\n");
1317
1318                 svg("<g transform=\"translate(410,200)\">\n");
1319                 svg_top_ten_pss();
1320                 svg("</g>\n\n");
1321         }
1322
1323         /* svg footer */
1324         svg("\n</svg>\n");
1325 }