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