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