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