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