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