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