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