chiark / gitweb /
Dynamically allocate bootchart logs
[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 Coproration
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(struct list_sample_data, 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;
424                 struct ps_sched_struct *prev_sample;
425                 struct ps_sched_struct *cross_place;
426
427                 bottom = 0;
428                 top = 0;
429
430                 /* put all the small pss blocks into the bottom */
431                 ps = ps_first->next_ps;
432                 while (ps->next_ps) {
433                         ps = ps->next_ps;
434                         if (!ps)
435                                 continue;
436                         ps->sample = ps->first;
437                         while (ps->sample->next) {
438                                 ps->sample = ps->sample->next;
439                                 if (ps->sample->sampledata == sampledata)
440                                         break;
441                         }
442                         if (ps->sample->sampledata == sampledata) {
443                                 if (ps->sample->pss <= (100 * arg_scale_y))
444                                         top += ps->sample->pss;
445                                 break;
446                         }
447                 }
448                 while ((cross_place = ps->sample->cross)) {
449                         ps = ps->sample->cross->ps_new;
450                         ps->sample = cross_place;
451                         if (ps->sample->pss <= (100 * arg_scale_y))
452                                 top += ps->sample->pss;
453                 }
454                 bottom = top;
455
456                 /* now plot the ones that are of significant size */
457                 ps = ps_first;
458                 while (ps->next_ps) {
459                         prev_sample = ps->sample;
460                         ps = ps->next_ps;
461                         if (!ps)
462                                 continue;
463                         ps->sample = ps->first;
464                         while (ps->sample->next) {
465                                 prev_sample = ps->sample;
466                                 ps->sample = ps->sample->next;
467                                 if (ps->sample->sampledata == sampledata)
468                                         break;
469                         }
470                         /* don't draw anything smaller than 2mb */
471                         if (ps->sample->sampledata == sampledata) {
472                                 if (ps->sample->pss > (100 * arg_scale_y)) {
473                                         top = bottom + ps->sample->pss;
474                                         /* draw a label with the process / PID */
475                                         if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
476                                                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
477                                                     time_to_graph(sampledata->sampletime - graph_start),
478                                                     kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
479                                                     ps->name,
480                                                     ps->pid);
481                                         bottom = top;
482                                 }
483                                 break;
484                         }
485                 }
486                 while ((cross_place = ps->sample->cross)) {
487                         ps = ps->sample->cross->ps_new;
488                         ps->sample = cross_place;
489                         prev_sample = ps->sample->prev;
490                         if (ps->sample->pss > (100 * arg_scale_y)) {
491                                 top = bottom + ps->sample->pss;
492                                 /* draw a label with the process / PID */
493                                 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
494                                         svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
495                                             time_to_graph(sampledata->sampletime - graph_start),
496                                             kb_to_graph(1000000.0 - bottom - ((top -  bottom) / 2)),
497                                             ps->name,
498                                             ps->pid);
499                                 bottom = top;
500                         }
501                 }
502                 i++;
503         }
504
505         /* debug output - full data dump */
506         svg("\n\n<!-- PSS map - csv format -->\n");
507         ps = ps_first;
508         while (ps->next_ps) {
509                 _cleanup_free_ char *enc_name = NULL;
510                 ps = ps->next_ps;
511                 if (!ps)
512                         continue;
513
514                 enc_name = xml_comment_encode(ps->name);
515                 if(!enc_name)
516                         continue;
517
518                 svg("<!-- %s [%d] pss=", enc_name, ps->pid);
519
520                 ps->sample = ps->first;
521                 while (ps->sample->next) {
522                         ps->sample = ps->sample->next;
523                         svg("%d," , ps->sample->pss);
524                 }
525                 svg(" -->\n");
526         }
527
528 }
529
530 static void svg_io_bi_bar(void) {
531         double max = 0.0;
532         double range;
533         int max_here = 0;
534         int i;
535         int k;
536         struct list_sample_data *start_sampledata = sampledata;
537         struct list_sample_data *stop_sampledata = sampledata;
538
539         svg("<!-- IO utilization graph - In -->\n");
540
541         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
542
543         /*
544          * calculate rounding range
545          *
546          * We need to round IO data since IO block data is not updated on
547          * each poll. Applying a smoothing function loses some burst data,
548          * so keep the smoothing range short.
549          */
550         range = 0.25 / (1.0 / arg_hz);
551         if (range < 2.0)
552                 range = 2.0; /* no smoothing */
553
554         /* surrounding box */
555         svg_graph_box(5);
556
557         /* find the max IO first */
558         i = 1;
559         LIST_FOREACH_BEFORE(link, sampledata, head) {
560                 int start;
561                 int stop;
562                 int diff;
563                 double tot;
564
565                 start = MAX(i - ((range / 2) - 1), 0);
566                 stop = MIN(i + (range / 2), samples - 1);
567                 diff = (stop - start);
568
569                 start_sampledata = sampledata;
570                 stop_sampledata = sampledata;
571
572                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
573                         start_sampledata = start_sampledata->link_next;
574                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
575                         stop_sampledata = stop_sampledata->link_prev;
576
577                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
578                         / diff;
579
580                 if (tot > max) {
581                         max = tot;
582                         max_here = i;
583                 }
584
585                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
586                         / diff;
587
588                 if (tot > max)
589                         max = tot;
590
591                 i++;
592         }
593
594         /* plot bi */
595         i = 1;
596         prev_sampledata = head;
597         LIST_FOREACH_BEFORE(link, sampledata, head) {
598                 int start;
599                 int stop;
600                 int diff;
601                 double tot;
602                 double pbi;
603
604                 tot = 0;
605                 pbi = 0;
606
607                 start = MAX(i - ((range / 2) - 1), 0);
608                 stop = MIN(i + (range / 2), samples);
609                 diff = (stop - start);
610
611                 start_sampledata = sampledata;
612                 stop_sampledata = sampledata;
613
614                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
615                         start_sampledata = start_sampledata->link_next;
616                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
617                         stop_sampledata = stop_sampledata->link_prev;
618
619                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
620                         / diff;
621
622                 if (max > 0)
623                         pbi = tot / max;
624
625                 if (pbi > 0.001)
626                         svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
627                             time_to_graph(prev_sampledata->sampletime - graph_start),
628                             (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
629                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
630                             pbi * (arg_scale_y * 5));
631
632                 /* labels around highest value */
633                 if (i == max_here) {
634                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
635                             time_to_graph(sampledata->sampletime - graph_start) + 5,
636                             ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
637                             max / 1024.0 / (interval / 1000000000.0));
638                 }
639                 i++;
640                 prev_sampledata = sampledata;
641         }
642 }
643
644 static void svg_io_bo_bar(void) {
645         double max = 0.0;
646         double range;
647         int max_here = 0;
648         int i;
649         int k;
650         struct list_sample_data *start_sampledata = sampledata;
651         struct list_sample_data *stop_sampledata = sampledata;
652
653         svg("<!-- IO utilization graph - out -->\n");
654
655         svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
656
657         /*
658          * calculate rounding range
659          *
660          * We need to round IO data since IO block data is not updated on
661          * each poll. Applying a smoothing function loses some burst data,
662          * so keep the smoothing range short.
663          */
664         range = 0.25 / (1.0 / arg_hz);
665         if (range < 2.0)
666                 range = 2.0; /* no smoothing */
667
668         /* surrounding box */
669         svg_graph_box(5);
670
671         /* find the max IO first */
672         i = 0;
673         LIST_FOREACH_BEFORE(link, sampledata, head) {
674                 int start;
675                 int stop;
676                 int diff;
677                 double tot;
678
679                 start = MAX(i - ((range / 2) - 1), 0);
680                 stop = MIN(i + (range / 2), samples - 1);
681                 diff = (stop - start);
682
683                 start_sampledata = sampledata;
684                 stop_sampledata = sampledata;
685
686                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
687                         start_sampledata = start_sampledata->link_next;
688                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
689                         stop_sampledata = stop_sampledata->link_prev;
690
691                 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi)
692                         / diff;
693                 if (tot > max)
694                         max = tot;
695                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
696                         / diff;
697                 if (tot > max) {
698                         max = tot;
699                         max_here = i;
700                 }
701                 i++;
702         }
703
704         /* plot bo */
705         prev_sampledata = head;
706         i=1;
707         LIST_FOREACH_BEFORE(link, sampledata, head) {
708                 int start;
709                 int stop;
710                 int diff;
711                 double tot;
712                 double pbo;
713
714                 tot = 0;
715                 pbo = 0;
716
717                 start = MAX(i - ((range / 2) - 1), 0);
718                 stop = MIN(i + (range / 2), samples);
719                 diff = (stop - start);
720
721                 start_sampledata = sampledata;
722                 stop_sampledata = sampledata;
723
724                 for (k=0;(k<((range/2)-1))&&(start_sampledata->link_next);k++)
725                         start_sampledata = start_sampledata->link_next;
726                 for (k=0;(k<(range/2))&&(stop_sampledata->link_prev);k++)
727                         stop_sampledata = stop_sampledata->link_prev;
728
729                 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
730                         / diff;
731
732                 if (max > 0)
733                         pbo = tot / max;
734
735                 if (pbo > 0.001)
736                         svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
737                             time_to_graph(prev_sampledata->sampletime - graph_start),
738                             (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
739                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
740                             pbo * (arg_scale_y * 5));
741
742                 /* labels around highest bo value */
743                 if (i == max_here) {
744                         svg("  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
745                             time_to_graph(sampledata->sampletime - graph_start) + 5,
746                             ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
747                             max / 1024.0 / (interval / 1000000000.0));
748                 }
749                 i++;
750                 prev_sampledata = sampledata;
751         }
752 }
753
754 static void svg_cpu_bar(void) {
755
756         svg("<!-- CPU utilization graph -->\n");
757
758         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
759         /* surrounding box */
760         svg_graph_box(5);
761
762         /* bars for each sample, proportional to the CPU util. */
763         prev_sampledata = head;
764         LIST_FOREACH_BEFORE(link, sampledata, head) {
765                 int c;
766                 double trt;
767                 double ptrt;
768
769                 ptrt = trt = 0.0;
770
771                 for (c = 0; c < cpus; c++)
772                         trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
773
774                 trt = trt / 1000000000.0;
775
776                 trt = trt / (double)cpus;
777
778                 if (trt > 0.0)
779                         ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
780
781                 if (ptrt > 1.0)
782                         ptrt = 1.0;
783
784                 if (ptrt > 0.001) {
785                         svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
786                             time_to_graph(prev_sampledata->sampletime - graph_start),
787                             (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
788                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
789                             ptrt * (arg_scale_y * 5));
790                 }
791                 prev_sampledata = sampledata;
792         }
793 }
794
795 static void svg_wait_bar(void) {
796
797         svg("<!-- Wait time aggregation box -->\n");
798
799         svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
800
801         /* surrounding box */
802         svg_graph_box(5);
803
804         /* bars for each sample, proportional to the CPU util. */
805         prev_sampledata = head;
806         LIST_FOREACH_BEFORE(link, sampledata, head) {
807                 int c;
808                 double twt;
809                 double ptwt;
810
811                 ptwt = twt = 0.0;
812
813                 for (c = 0; c < cpus; c++)
814                         twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
815
816                 twt = twt / 1000000000.0;
817
818                 twt = twt / (double)cpus;
819
820                 if (twt > 0.0)
821                         ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
822
823                 if (ptwt > 1.0)
824                         ptwt = 1.0;
825
826                 if (ptwt > 0.001) {
827                         svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
828                             time_to_graph(prev_sampledata->sampletime - graph_start),
829                             ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
830                             time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
831                             ptwt * (arg_scale_y * 5));
832                 }
833                 prev_sampledata = sampledata;
834         }
835 }
836
837
838 static void svg_entropy_bar(void) {
839
840         svg("<!-- entropy pool graph -->\n");
841
842         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
843         /* surrounding box */
844         svg_graph_box(5);
845
846         /* bars for each sample, scale 0-4096 */
847         prev_sampledata = head;
848         LIST_FOREACH_BEFORE(link, sampledata, head) {
849                 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
850                 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
851                     time_to_graph(prev_sampledata->sampletime - graph_start),
852                     ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))),
853                     time_to_graph(sampledata->sampletime - prev_sampledata->sampletime),
854                     (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5));
855                 prev_sampledata = sampledata;
856         }
857 }
858
859 static struct ps_struct *get_next_ps(struct ps_struct *ps) {
860         /*
861          * walk the list of processes and return the next one to be
862          * painted
863          */
864         if (ps == ps_first)
865                 return ps->next_ps;
866
867         /* go deep */
868         if (ps->children)
869                 return ps->children;
870
871         /* find siblings */
872         if (ps->next)
873                 return ps->next;
874
875         /* go back for parent siblings */
876         while (1) {
877                 if (ps->parent)
878                         if (ps->parent->next)
879                                 return ps->parent->next;
880                 ps = ps->parent;
881                 if (!ps)
882                         return ps;
883         }
884
885         return NULL;
886 }
887
888 static int ps_filter(struct ps_struct *ps) {
889         if (!arg_filter)
890                 return 0;
891
892         /* can't draw data when there is only 1 sample (need start + stop) */
893         if (ps->first == ps->last)
894                 return -1;
895
896         /* don't filter kthreadd */
897         if (ps->pid == 2)
898                 return 0;
899
900         /* drop stuff that doesn't use any real CPU time */
901         if (ps->total <= 0.001)
902                 return -1;
903
904         return 0;
905 }
906
907 static void svg_do_initcall(int count_only) {
908         _cleanup_pclose_ FILE *f = NULL;
909         double t;
910         char func[256];
911         int ret;
912         int usecs;
913
914         /* can't plot initcall when disabled or in relative mode */
915         if (!initcall || arg_relative) {
916                 kcount = 0;
917                 return;
918         }
919
920         if (!count_only) {
921                 svg("<!-- initcall -->\n");
922
923                 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
924                 /* surrounding box */
925                 svg_graph_box(kcount);
926         }
927
928         kcount = 0;
929
930         /*
931          * Initcall graphing - parses dmesg buffer and displays kernel threads
932          * This somewhat uses the same methods and scaling to show processes
933          * but looks a lot simpler. It's overlaid entirely onto the PS graph
934          * when appropriate.
935          */
936
937         f = popen("dmesg", "r");
938         if (!f)
939                 return;
940
941         while (!feof(f)) {
942                 int c;
943                 int z = 0;
944                 char l[256];
945
946                 if (fgets(l, sizeof(l) - 1, f) == NULL)
947                         continue;
948
949                 c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
950                            &t, func, &ret, &usecs);
951                 if (c != 4) {
952                         /* also parse initcalls done by module loading */
953                         c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
954                                    &t, func, &ret, &usecs);
955                         if (c != 4)
956                                 continue;
957                 }
958
959                 /* chop the +0xXX/0xXX stuff */
960                 while(func[z] != '+')
961                         z++;
962                 func[z] = 0;
963
964                 if (count_only) {
965                         /* filter out irrelevant stuff */
966                         if (usecs >= 1000)
967                                 kcount++;
968                         continue;
969                 }
970
971                 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
972                     func, t, usecs, ret);
973
974                 if (usecs < 1000)
975                         continue;
976
977                 /* rect */
978                 svg("  <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
979                     time_to_graph(t - (usecs / 1000000.0)),
980                     ps_to_graph(kcount),
981                     time_to_graph(usecs / 1000000.0),
982                     ps_to_graph(1));
983
984                 /* label */
985                 svg("  <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
986                     time_to_graph(t - (usecs / 1000000.0)) + 5,
987                     ps_to_graph(kcount) + 15,
988                     func,
989                     usecs / 1000000.0);
990
991                 kcount++;
992         }
993 }
994
995 static void svg_ps_bars(void) {
996         struct ps_struct *ps;
997         int i = 0;
998         int j = 0;
999         int pid;
1000         double w = 0.0;
1001
1002         svg("<!-- Process graph -->\n");
1003
1004         svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1005
1006         /* surrounding box */
1007         svg_graph_box(pcount);
1008
1009         /* pass 2 - ps boxes */
1010         ps = ps_first;
1011         while ((ps = get_next_ps(ps))) {
1012                 _cleanup_free_ char *enc_name = NULL;
1013                 double endtime;
1014                 double starttime;
1015                 int t;
1016
1017                 enc_name = xml_comment_encode(ps->name);
1018                 if(!enc_name)
1019                         continue;
1020
1021                 /* leave some trace of what we actually filtered etc. */
1022                 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1023                     ps->ppid, ps->total);
1024
1025                 starttime = ps->first->sampledata->sampletime;
1026
1027                 if (!ps_filter(ps)) {
1028                         /* remember where _to_ our children need to draw a line */
1029                         ps->pos_x = time_to_graph(starttime - graph_start);
1030                         ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
1031                 } else if (ps->parent){
1032                         /* hook children to our parent coords instead */
1033                         ps->pos_x = ps->parent->pos_x;
1034                         ps->pos_y = ps->parent->pos_y;
1035
1036                         /* if this is the last child, we might still need to draw a connecting line */
1037                         if ((!ps->next) && (ps->parent))
1038                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1039                                     ps->parent->pos_x,
1040                                     ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
1041                                     ps->parent->pos_x,
1042                                     ps->parent->pos_y);
1043                         continue;
1044                 }
1045
1046                 endtime = ps->last->sampledata->sampletime;
1047                 svg("  <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1048                     time_to_graph(starttime - graph_start),
1049                     ps_to_graph(j),
1050                     time_to_graph(ps->last->sampledata->sampletime - starttime),
1051                     ps_to_graph(1));
1052
1053                 /* paint cpu load over these */
1054                 ps->sample = ps->first;
1055                 t = 1;
1056                 while (ps->sample->next) {
1057                         double rt, prt;
1058                         double wt, wrt;
1059                         struct ps_sched_struct *prev;
1060
1061                         prev = ps->sample;
1062                         ps->sample = ps->sample->next;
1063
1064                         /* calculate over interval */
1065                         rt = ps->sample->runtime - prev->runtime;
1066                         wt = ps->sample->waittime - prev->waittime;
1067
1068                         prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1069                         wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1070
1071                         /* this can happen if timekeeping isn't accurate enough */
1072                         if (prt > 1.0)
1073                                 prt = 1.0;
1074                         if (wrt > 1.0)
1075                                 wrt = 1.0;
1076
1077                         if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
1078                                 continue;
1079
1080                         svg("    <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1081                             time_to_graph(prev->sampledata->sampletime - graph_start),
1082                             ps_to_graph(j),
1083                             time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1084                             ps_to_graph(wrt));
1085
1086                         /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1087                         svg("    <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1088                             time_to_graph(prev->sampledata->sampletime - graph_start),
1089                             ps_to_graph(j + (1.0 - prt)),
1090                             time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime),
1091                             ps_to_graph(prt));
1092                         t++;
1093                 }
1094
1095                 /* determine where to display the process name */
1096                 if ((endtime - starttime) < 1.5)
1097                         /* too small to fit label inside the box */
1098                         w = endtime;
1099                 else
1100                         w = starttime;
1101
1102                 /* text label of process name */
1103                 svg("  <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
1104                     time_to_graph(w - graph_start) + 5.0,
1105                     ps_to_graph(j) + 14.0,
1106                     ps->name,
1107                     ps->pid,
1108                     (ps->last->runtime - ps->first->runtime) / 1000000000.0);
1109                 /* paint lines to the parent process */
1110                 if (ps->parent) {
1111                         /* horizontal part */
1112                         svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1113                             time_to_graph(starttime - graph_start),
1114                             ps_to_graph(j) + 10.0,
1115                             ps->parent->pos_x,
1116                             ps_to_graph(j) + 10.0);
1117
1118                         /* one vertical line connecting all the horizontal ones up */
1119                         if (!ps->next)
1120                                 svg("  <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1121                                     ps->parent->pos_x,
1122                                     ps_to_graph(j) + 10.0,
1123                                     ps->parent->pos_x,
1124                                     ps->parent->pos_y);
1125                 }
1126
1127                 j++; /* count boxes */
1128
1129                 svg("\n");
1130         }
1131
1132         /* last pass - determine when idle */
1133         pid = getpid();
1134         /* make sure we start counting from the point where we actually have
1135          * data: assume that bootchart's first sample is when data started
1136          */
1137
1138         ps = ps_first;
1139         while (ps->next_ps) {
1140                 ps = ps->next_ps;
1141                 if (ps->pid == pid)
1142                         break;
1143         }
1144
1145         /* need to know last node first */
1146         ps->sample = ps->first;
1147         i = ps->sample->next->sampledata->counter;
1148
1149         while (ps->sample->next && i<(samples-(arg_hz/2))) {
1150                 double crt;
1151                 double brt;
1152                 int c;
1153                 int ii;
1154                 struct ps_sched_struct *sample_hz;
1155
1156                 ps->sample = ps->sample->next;
1157                 sample_hz = ps->sample;
1158                 for (ii=0;((ii<(int)arg_hz/2)&&(ps->sample->next));ii++)
1159                         sample_hz = sample_hz->next;
1160
1161                 /* subtract bootchart cpu utilization from total */
1162                 crt = 0.0;
1163                 for (c = 0; c < cpus; c++)
1164                         crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
1165                 brt = sample_hz->runtime - ps->sample->runtime;
1166                 /*
1167                  * our definition of "idle":
1168                  *
1169                  * if for (hz / 2) we've used less CPU than (interval / 2) ...
1170                  * defaults to 4.0%, which experimentally, is where atom idles
1171                  */
1172                 if ((crt - brt) < (interval / 2.0)) {
1173                         idletime = ps->sample->sampledata->sampletime - graph_start;
1174                         svg("\n<!-- idle detected at %.03f seconds -->\n",
1175                             idletime);
1176                         svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1177                             time_to_graph(idletime),
1178                             -arg_scale_y,
1179                             time_to_graph(idletime),
1180                             ps_to_graph(pcount) + arg_scale_y);
1181                         svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1182                             time_to_graph(idletime) + 5.0,
1183                             ps_to_graph(pcount) + arg_scale_y,
1184                             idletime);
1185                         break;
1186                 }
1187                 i++;
1188         }
1189 }
1190
1191 static void svg_top_ten_cpu(void) {
1192         struct ps_struct *top[10];
1193         struct ps_struct emptyps = {};
1194         struct ps_struct *ps;
1195         int n, m;
1196
1197         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1198                 top[n] = &emptyps;
1199
1200         /* walk all ps's and setup ptrs */
1201         ps = ps_first;
1202         while ((ps = get_next_ps(ps))) {
1203                 for (n = 0; n < 10; n++) {
1204                         if (ps->total <= top[n]->total)
1205                                 continue;
1206                         /* cascade insert */
1207                         for (m = 9; m > n; m--)
1208                                 top[m] = top[m-1];
1209                         top[n] = ps;
1210                         break;
1211                 }
1212         }
1213
1214         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1215         for (n = 0; n < 10; n++)
1216                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1217                     20 + (n * 13),
1218                     top[n]->total,
1219                     top[n]->name,
1220                     top[n]->pid);
1221 }
1222
1223 static void svg_top_ten_pss(void) {
1224         struct ps_struct *top[10];
1225         struct ps_struct emptyps = {};
1226         struct ps_struct *ps;
1227         int n, m;
1228
1229         for (n = 0; n < (int) ELEMENTSOF(top); n++)
1230                 top[n] = &emptyps;
1231
1232         /* walk all ps's and setup ptrs */
1233         ps = ps_first;
1234         while ((ps = get_next_ps(ps))) {
1235                 for (n = 0; n < 10; n++) {
1236                         if (ps->pss_max <= top[n]->pss_max)
1237                                 continue;
1238                         /* cascade insert */
1239                         for (m = 9; m > n; m--)
1240                                 top[m] = top[m-1];
1241                         top[n] = ps;
1242                         break;
1243                 }
1244         }
1245
1246         svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1247         for (n = 0; n < 10; n++)
1248                 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1249                     20 + (n * 13),
1250                     top[n]->pss_max,
1251                     top[n]->name,
1252                     top[n]->pid);
1253 }
1254
1255 void svg_do(const char *build) {
1256         struct ps_struct *ps;
1257
1258         memset(&str, 0, sizeof(str));
1259
1260         ps = ps_first;
1261
1262         /* count initcall thread count first */
1263         svg_do_initcall(1);
1264         ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
1265
1266         /* then count processes */
1267         while ((ps = get_next_ps(ps))) {
1268                 if (!ps_filter(ps))
1269                         pcount++;
1270                 else
1271                         pfiltered++;
1272         }
1273         psize = ps_to_graph(pcount) + (arg_scale_y * 2);
1274
1275         esize = (arg_entropy ? arg_scale_y * 7 : 0);
1276
1277         /* after this, we can draw the header with proper sizing */
1278         svg_header();
1279
1280         svg("<g transform=\"translate(10,400)\">\n");
1281         svg_io_bi_bar();
1282         svg("</g>\n\n");
1283
1284         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
1285         svg_io_bo_bar();
1286         svg("</g>\n\n");
1287
1288         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
1289         svg_cpu_bar();
1290         svg("</g>\n\n");
1291
1292         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
1293         svg_wait_bar();
1294         svg("</g>\n\n");
1295
1296         if (kcount) {
1297                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
1298                 svg_do_initcall(0);
1299                 svg("</g>\n\n");
1300         }
1301
1302         svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
1303         svg_ps_bars();
1304         svg("</g>\n\n");
1305
1306         svg("<g transform=\"translate(10,  0)\">\n");
1307         svg_title(build);
1308         svg("</g>\n\n");
1309
1310         svg("<g transform=\"translate(10,200)\">\n");
1311         svg_top_ten_cpu();
1312         svg("</g>\n\n");
1313
1314         if (arg_entropy) {
1315                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
1316                 svg_entropy_bar();
1317                 svg("</g>\n\n");
1318         }
1319
1320         if (arg_pss) {
1321                 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
1322                 svg_pss_graph();
1323                 svg("</g>\n\n");
1324
1325                 svg("<g transform=\"translate(410,200)\">\n");
1326                 svg_top_ten_pss();
1327                 svg("</g>\n\n");
1328         }
1329
1330         /* svg footer */
1331         svg("\n</svg>\n");
1332 }