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