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