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