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