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