]>
git.proxmox.com Git - systemd.git/blob - src/bootchart/svg.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Corporation
9 Auke Kok <auke-jan.h.kok@intel.com>
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.
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.
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/>.
32 #include <sys/utsname.h>
40 #include "bootchart.h"
44 #define time_to_graph(t) ((t) * arg_scale_x)
45 #define ps_to_graph(n) ((n) * arg_scale_y)
46 #define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
47 #define to_color(n) (192.0 - ((n) * 192.0))
49 static char str
[8092];
51 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
53 static const char * const colorwheel
[12] = {
54 "rgb(255,32,32)", // red
55 "rgb(32,192,192)", // cyan
56 "rgb(255,128,32)", // orange
57 "rgb(128,32,192)", // blue-violet
58 "rgb(255,255,32)", // yellow
59 "rgb(192,32,128)", // red-violet
60 "rgb(32,255,32)", // green
61 "rgb(255,64,32)", // red-orange
62 "rgb(32,32,255)", // blue
63 "rgb(255,192,32)", // yellow-orange
64 "rgb(192,32,192)", // violet
65 "rgb(32,192,32)" // yellow-green
68 static double idletime
= -1.0;
69 static int pfiltered
= 0;
70 static int pcount
= 0;
71 static int kcount
= 0;
72 static double psize
= 0;
73 static double ksize
= 0;
74 static double esize
= 0;
75 static struct list_sample_data
*sampledata
;
76 static struct list_sample_data
*prev_sampledata
;
77 extern struct list_sample_data
*head
;
79 static void svg_header(void) {
82 struct list_sample_data
*sampledata_last
;
87 LIST_FIND_TAIL(link
, sampledata
, head
);
88 sampledata_last
= head
;
89 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
90 sampledata_last
= sampledata
;
93 /* min width is about 1600px due to the label */
94 w
= 150.0 + 10.0 + time_to_graph(sampledata_last
->sampletime
- graph_start
);
95 w
= ((w
< 1600.0) ? 1600.0 : w
);
97 /* height is variable based on pss, psize, ksize */
98 h
= 400.0 + (arg_scale_y
* 30.0) /* base graphs and title */
99 + (arg_pss
? (100.0 * arg_scale_y
) + (arg_scale_y
* 7.0) : 0.0) /* pss estimate */
100 + psize
+ ksize
+ esize
;
102 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
103 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
104 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
106 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
107 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
109 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
111 /* write some basic info as a comment, including some help */
112 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
113 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
114 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
115 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
116 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
118 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION
);
119 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz
, arg_samples_len
);
120 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x
, arg_scale_y
);
121 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative
, arg_filter
);
122 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss
, arg_entropy
);
123 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path
, arg_init_path
);
126 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
128 svg(" rect { stroke-width: 1; }\n");
129 svg(" rect.bg { fill: rgb(255,255,255); }\n");
130 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
131 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
132 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
133 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
134 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
135 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
136 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
137 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
138 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
139 svg("// line.sec1 { }\n");
140 svg(" line.sec5 { stroke-width: 2; }\n");
141 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
142 svg(" line.dot { stroke-dasharray: 2 4; }\n");
143 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
145 svg(" .run { font-size: 8; font-style: italic; }\n");
146 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
147 svg(" text.sec { font-size: 8; }\n");
148 svg(" text.t1 { font-size: 24; }\n");
149 svg(" text.t2 { font-size: 12; }\n");
150 svg(" text.idle { font-size: 18; }\n");
152 svg(" ]]>\n </style>\n</defs>\n\n");
155 static void svg_title(const char *build
) {
156 char cmdline
[256] = "";
157 char filename
[PATH_MAX
];
159 char rootbdev
[16] = "Unknown";
160 char model
[256] = "Unknown";
161 char date
[256] = "Unknown";
162 char cpu
[256] = "Unknown";
169 /* grab /proc/cmdline */
170 fd
= openat(procfd
, "cmdline", O_RDONLY
);
173 if (!fgets(cmdline
, 255, f
))
174 sprintf(cmdline
, "Unknown");
178 /* extract root fs so we can find disk model name in sysfs */
179 /* FIXME: this works only in the simple case */
180 c
= strstr(cmdline
, "root=/dev/");
182 strncpy(rootbdev
, &c
[10], 3);
184 sprintf(filename
, "block/%s/device/model", rootbdev
);
185 fd
= openat(sysfd
, filename
, O_RDONLY
);
188 if (!fgets(model
, 255, f
))
189 fprintf(stderr
, "Error reading disk model for %s\n", rootbdev
);
194 /* various utsname parameters */
196 fprintf(stderr
, "Error getting uname info\n");
200 r
= strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", localtime(&t
));
204 fd
= openat(procfd
, "cpuinfo", O_RDONLY
);
207 while (fgets(buf
, 255, f
)) {
208 if (strstr(buf
, "model name")) {
209 strncpy(cpu
, &buf
[13], 255);
216 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
218 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
219 uts
.sysname
, uts
.release
, uts
.version
, uts
.machine
);
220 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
222 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
224 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
226 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
228 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start
);
229 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
232 svg("%.03fs", idletime
);
236 svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
237 arg_hz
, arg_samples_len
, overrun
, pscount
, pfiltered
);
240 static void svg_graph_box(int height
) {
243 double finalsample
= 0.0;
244 struct list_sample_data
*sampledata_last
;
246 sampledata_last
= head
;
247 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
248 sampledata_last
= sampledata
;
251 finalsample
= sampledata_last
->sampletime
;
253 /* outside box, fill */
254 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
256 time_to_graph(finalsample
- graph_start
),
257 ps_to_graph(height
));
259 for (d
= graph_start
; d
<= finalsample
;
260 d
+= (arg_scale_x
< 2.0 ? 60.0 : arg_scale_x
< 10.0 ? 1.0 : 0.1)) {
261 /* lines for each second */
263 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
264 time_to_graph(d
- graph_start
),
265 time_to_graph(d
- graph_start
),
266 ps_to_graph(height
));
267 else if (i
% 10 == 0)
268 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
269 time_to_graph(d
- graph_start
),
270 time_to_graph(d
- graph_start
),
271 ps_to_graph(height
));
273 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
274 time_to_graph(d
- graph_start
),
275 time_to_graph(d
- graph_start
),
276 ps_to_graph(height
));
280 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
281 time_to_graph(d
- graph_start
),
289 /* xml comments must not contain "--" */
290 static char* xml_comment_encode(const char* name
) {
293 enc_name
= strdup(name
);
297 for (p
= enc_name
; *p
; p
++)
298 if (p
[0] == '-' && p
[1] == '-')
304 static void svg_pss_graph(void) {
305 struct ps_struct
*ps
;
307 struct list_sample_data
*sampledata_last
;
309 sampledata_last
= head
;
310 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
311 sampledata_last
= sampledata
;
315 svg("\n\n<!-- Pss memory size graph -->\n");
317 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
319 /* vsize 1000 == 1000mb */
321 /* draw some hlines for usable memory sizes */
322 for (i
= 100000; i
< 1000000; i
+= 100000) {
323 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
326 time_to_graph(sampledata_last
->sampletime
- graph_start
),
328 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
329 time_to_graph(sampledata_last
->sampletime
- graph_start
) + 5,
330 kb_to_graph(i
), (1000000 - i
) / 1000);
334 /* now plot the graph itself */
336 prev_sampledata
= head
;
337 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
340 struct ps_sched_struct
*cross_place
;
345 /* put all the small pss blocks into the bottom */
347 while (ps
->next_ps
) {
351 ps
->sample
= ps
->first
;
352 while (ps
->sample
->next
) {
353 ps
->sample
= ps
->sample
->next
;
354 if (ps
->sample
->sampledata
== sampledata
)
357 if (ps
->sample
->sampledata
== sampledata
) {
358 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
359 top
+= ps
->sample
->pss
;
363 while (ps
->sample
->cross
) {
364 cross_place
= ps
->sample
->cross
;
365 ps
= ps
->sample
->cross
->ps_new
;
366 ps
->sample
= cross_place
;
367 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
368 top
+= ps
->sample
->pss
;
371 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
373 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
374 kb_to_graph(1000000.0 - top
),
375 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
376 kb_to_graph(top
- bottom
));
379 /* now plot the ones that are of significant size */
381 while (ps
->next_ps
) {
385 ps
->sample
= ps
->first
;
386 while (ps
->sample
->next
) {
387 ps
->sample
= ps
->sample
->next
;
388 if (ps
->sample
->sampledata
== sampledata
)
391 /* don't draw anything smaller than 2mb */
392 if (ps
->sample
->sampledata
== sampledata
) {
393 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
394 top
= bottom
+ ps
->sample
->pss
;
395 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
396 colorwheel
[ps
->pid
% 12],
397 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
398 kb_to_graph(1000000.0 - top
),
399 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
400 kb_to_graph(top
- bottom
));
406 while ((cross_place
= ps
->sample
->cross
)) {
407 ps
= ps
->sample
->cross
->ps_new
;
408 ps
->sample
= cross_place
;
409 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
410 top
= bottom
+ ps
->sample
->pss
;
411 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
412 colorwheel
[ps
->pid
% 12],
413 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
414 kb_to_graph(1000000.0 - top
),
415 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
416 kb_to_graph(top
- bottom
));
420 prev_sampledata
= sampledata
;
424 /* overlay all the text labels */
426 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
429 struct ps_sched_struct
*prev_sample
;
430 struct ps_sched_struct
*cross_place
;
432 /* put all the small pss blocks into the bottom */
433 ps
= ps_first
->next_ps
;
434 while (ps
->next_ps
) {
438 ps
->sample
= ps
->first
;
439 while (ps
->sample
->next
) {
440 ps
->sample
= ps
->sample
->next
;
441 if (ps
->sample
->sampledata
== sampledata
)
444 if (ps
->sample
->sampledata
== sampledata
) {
445 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
446 top
+= ps
->sample
->pss
;
450 while ((cross_place
= ps
->sample
->cross
)) {
451 ps
= ps
->sample
->cross
->ps_new
;
452 ps
->sample
= cross_place
;
453 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
454 top
+= ps
->sample
->pss
;
458 /* now plot the ones that are of significant size */
460 while (ps
->next_ps
) {
461 prev_sample
= ps
->sample
;
465 ps
->sample
= ps
->first
;
466 while (ps
->sample
->next
) {
467 prev_sample
= ps
->sample
;
468 ps
->sample
= ps
->sample
->next
;
469 if (ps
->sample
->sampledata
== sampledata
)
472 /* don't draw anything smaller than 2mb */
473 if (ps
->sample
->sampledata
== sampledata
) {
474 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
475 top
= bottom
+ ps
->sample
->pss
;
476 /* draw a label with the process / PID */
477 if ((i
== 1) || (prev_sample
->pss
<= (100 * arg_scale_y
)))
478 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
479 time_to_graph(sampledata
->sampletime
- graph_start
),
480 kb_to_graph(1000000.0 - bottom
- ((top
- bottom
) / 2)),
488 while ((cross_place
= ps
->sample
->cross
)) {
489 ps
= ps
->sample
->cross
->ps_new
;
490 ps
->sample
= cross_place
;
491 prev_sample
= ps
->sample
->prev
;
492 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
493 top
= bottom
+ ps
->sample
->pss
;
494 /* draw a label with the process / PID */
495 if ((i
== 1) || (prev_sample
->pss
<= (100 * arg_scale_y
)))
496 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
497 time_to_graph(sampledata
->sampletime
- graph_start
),
498 kb_to_graph(1000000.0 - bottom
- ((top
- bottom
) / 2)),
507 /* debug output - full data dump */
508 svg("\n\n<!-- PSS map - csv format -->\n");
510 while (ps
->next_ps
) {
511 _cleanup_free_
char *enc_name
= NULL
;
516 enc_name
= xml_comment_encode(ps
->name
);
520 svg("<!-- %s [%d] pss=", enc_name
, ps
->pid
);
522 ps
->sample
= ps
->first
;
523 while (ps
->sample
->next
) {
524 ps
->sample
= ps
->sample
->next
;
525 svg("%d," , ps
->sample
->pss
);
532 static void svg_io_bi_bar(void) {
538 struct list_sample_data
*start_sampledata
;
539 struct list_sample_data
*stop_sampledata
;
541 svg("<!-- IO utilization graph - In -->\n");
543 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
546 * calculate rounding range
548 * We need to round IO data since IO block data is not updated on
549 * each poll. Applying a smoothing function loses some burst data,
550 * so keep the smoothing range short.
552 range
= 0.25 / (1.0 / arg_hz
);
554 range
= 2.0; /* no smoothing */
556 /* surrounding box */
559 /* find the max IO first */
561 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
567 start
= MAX(i
- ((range
/ 2) - 1), 0);
568 stop
= MIN(i
+ (range
/ 2), samples
- 1);
569 diff
= (stop
- start
);
571 start_sampledata
= sampledata
;
572 stop_sampledata
= sampledata
;
574 for (k
=0;(k
<((range
/2)-1))&&(start_sampledata
->link_next
);k
++)
575 start_sampledata
= start_sampledata
->link_next
;
576 for (k
=0;(k
<(range
/2))&&(stop_sampledata
->link_prev
);k
++)
577 stop_sampledata
= stop_sampledata
->link_prev
;
579 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
)
587 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
)
598 prev_sampledata
= head
;
599 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
606 start
= MAX(i
- ((range
/ 2) - 1), 0);
607 stop
= MIN(i
+ (range
/ 2), samples
);
608 diff
= (stop
- start
);
610 start_sampledata
= sampledata
;
611 stop_sampledata
= sampledata
;
613 for (k
=0;(k
<((range
/2)-1))&&(start_sampledata
->link_next
);k
++)
614 start_sampledata
= start_sampledata
->link_next
;
615 for (k
=0;(k
<(range
/2))&&(stop_sampledata
->link_prev
);k
++)
616 stop_sampledata
= stop_sampledata
->link_prev
;
618 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
)
625 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
626 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
627 (arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5)),
628 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
629 pbi
* (arg_scale_y
* 5));
631 /* labels around highest value */
633 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
634 time_to_graph(sampledata
->sampletime
- graph_start
) + 5,
635 ((arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5))) + 15,
636 max
/ 1024.0 / (interval
/ 1000000000.0));
639 prev_sampledata
= sampledata
;
643 static void svg_io_bo_bar(void) {
649 struct list_sample_data
*start_sampledata
;
650 struct list_sample_data
*stop_sampledata
;
652 svg("<!-- IO utilization graph - out -->\n");
654 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
657 * calculate rounding range
659 * We need to round IO data since IO block data is not updated on
660 * each poll. Applying a smoothing function loses some burst data,
661 * so keep the smoothing range short.
663 range
= 0.25 / (1.0 / arg_hz
);
665 range
= 2.0; /* no smoothing */
667 /* surrounding box */
670 /* find the max IO first */
672 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
678 start
= MAX(i
- ((range
/ 2) - 1), 0);
679 stop
= MIN(i
+ (range
/ 2), samples
- 1);
680 diff
= (stop
- start
);
682 start_sampledata
= sampledata
;
683 stop_sampledata
= sampledata
;
685 for (k
=0;(k
<((range
/2)-1))&&(start_sampledata
->link_next
);k
++)
686 start_sampledata
= start_sampledata
->link_next
;
687 for (k
=0;(k
<(range
/2))&&(stop_sampledata
->link_prev
);k
++)
688 stop_sampledata
= stop_sampledata
->link_prev
;
690 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
)
694 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
)
704 prev_sampledata
= head
;
706 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
715 start
= MAX(i
- ((range
/ 2) - 1), 0);
716 stop
= MIN(i
+ (range
/ 2), samples
);
717 diff
= (stop
- start
);
719 start_sampledata
= sampledata
;
720 stop_sampledata
= sampledata
;
722 for (k
=0;(k
<((range
/2)-1))&&(start_sampledata
->link_next
);k
++)
723 start_sampledata
= start_sampledata
->link_next
;
724 for (k
=0;(k
<(range
/2))&&(stop_sampledata
->link_prev
);k
++)
725 stop_sampledata
= stop_sampledata
->link_prev
;
727 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
)
734 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
735 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
736 (arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5)),
737 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
738 pbo
* (arg_scale_y
* 5));
740 /* labels around highest bo value */
742 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
743 time_to_graph(sampledata
->sampletime
- graph_start
) + 5,
744 ((arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5))),
745 max
/ 1024.0 / (interval
/ 1000000000.0));
748 prev_sampledata
= sampledata
;
752 static void svg_cpu_bar(int cpu_num
) {
754 svg("<!-- CPU utilization graph -->\n");
757 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
759 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num
);
760 /* surrounding box */
763 /* bars for each sample, proportional to the CPU util. */
764 prev_sampledata
= head
;
765 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
773 for (c
= 0; c
< cpus
; c
++)
774 trt
+= sampledata
->runtime
[c
] - prev_sampledata
->runtime
[c
];
776 trt
= sampledata
->runtime
[cpu_num
] - prev_sampledata
->runtime
[cpu_num
];
778 trt
= trt
/ 1000000000.0;
781 trt
= trt
/ (double)cpus
;
784 ptrt
= trt
/ (sampledata
->sampletime
- prev_sampledata
->sampletime
);
790 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
791 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
792 (arg_scale_y
* 5) - (ptrt
* (arg_scale_y
* 5)),
793 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
794 ptrt
* (arg_scale_y
* 5));
796 prev_sampledata
= sampledata
;
800 static void svg_wait_bar(int cpu_num
) {
802 svg("<!-- Wait time aggregation box -->\n");
805 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
807 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num
);
809 /* surrounding box */
812 /* bars for each sample, proportional to the CPU util. */
813 prev_sampledata
= head
;
814 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
822 for (c
= 0; c
< cpus
; c
++)
823 twt
+= sampledata
->waittime
[c
] - prev_sampledata
->waittime
[c
];
825 twt
= sampledata
->waittime
[cpu_num
] - prev_sampledata
->waittime
[cpu_num
];
827 twt
= twt
/ 1000000000.0;
830 twt
= twt
/ (double)cpus
;
833 ptwt
= twt
/ (sampledata
->sampletime
- prev_sampledata
->sampletime
);
839 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
840 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
841 ((arg_scale_y
* 5) - (ptwt
* (arg_scale_y
* 5))),
842 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
843 ptwt
* (arg_scale_y
* 5));
845 prev_sampledata
= sampledata
;
849 static void svg_entropy_bar(void) {
851 svg("<!-- entropy pool graph -->\n");
853 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
854 /* surrounding box */
857 /* bars for each sample, scale 0-4096 */
858 prev_sampledata
= head
;
859 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
860 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
861 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
862 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
863 ((arg_scale_y
* 5) - ((sampledata
->entropy_avail
/ 4096.) * (arg_scale_y
* 5))),
864 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
865 (sampledata
->entropy_avail
/ 4096.) * (arg_scale_y
* 5));
866 prev_sampledata
= sampledata
;
870 static struct ps_struct
*get_next_ps(struct ps_struct
*ps
) {
872 * walk the list of processes and return the next one to be
886 /* go back for parent siblings */
889 if (ps
->parent
->next
)
890 return ps
->parent
->next
;
899 static bool ps_filter(struct ps_struct
*ps
) {
903 /* can't draw data when there is only 1 sample (need start + stop) */
904 if (ps
->first
== ps
->last
)
907 /* don't filter kthreadd */
911 /* drop stuff that doesn't use any real CPU time */
912 if (ps
->total
<= 0.001)
918 static void svg_do_initcall(int count_only
) {
919 _cleanup_pclose_
FILE *f
= NULL
;
925 /* can't plot initcall when disabled or in relative mode */
926 if (!initcall
|| arg_relative
) {
932 svg("<!-- initcall -->\n");
934 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
935 /* surrounding box */
936 svg_graph_box(kcount
);
942 * Initcall graphing - parses dmesg buffer and displays kernel threads
943 * This somewhat uses the same methods and scaling to show processes
944 * but looks a lot simpler. It's overlaid entirely onto the PS graph
948 f
= popen("dmesg", "r");
957 if (fgets(l
, sizeof(l
) - 1, f
) == NULL
)
960 c
= sscanf(l
, "[%lf] initcall %s %*s %d %*s %d %*s",
961 &t
, func
, &ret
, &usecs
);
963 /* also parse initcalls done by module loading */
964 c
= sscanf(l
, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
965 &t
, func
, &ret
, &usecs
);
970 /* chop the +0xXX/0xXX stuff */
971 while(func
[z
] != '+')
976 /* filter out irrelevant stuff */
982 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
983 func
, t
, usecs
, ret
);
989 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
990 time_to_graph(t
- (usecs
/ 1000000.0)),
992 time_to_graph(usecs
/ 1000000.0),
996 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
997 time_to_graph(t
- (usecs
/ 1000000.0)) + 5,
998 ps_to_graph(kcount
) + 15,
1006 static void svg_ps_bars(void) {
1007 struct ps_struct
*ps
;
1013 svg("<!-- Process graph -->\n");
1015 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1017 /* surrounding box */
1018 svg_graph_box(pcount
);
1020 /* pass 2 - ps boxes */
1022 while ((ps
= get_next_ps(ps
))) {
1023 _cleanup_free_
char *enc_name
= NULL
, *escaped
= NULL
;
1028 if (!utf8_is_printable(ps
->name
, strlen(ps
->name
)))
1029 escaped
= utf8_escape_non_printable(ps
->name
);
1031 enc_name
= xml_comment_encode(escaped
? escaped
: ps
->name
);
1035 /* leave some trace of what we actually filtered etc. */
1036 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name
, ps
->pid
,
1037 ps
->ppid
, ps
->total
);
1039 starttime
= ps
->first
->sampledata
->sampletime
;
1041 if (!ps_filter(ps
)) {
1042 /* remember where _to_ our children need to draw a line */
1043 ps
->pos_x
= time_to_graph(starttime
- graph_start
);
1044 ps
->pos_y
= ps_to_graph(j
+1); /* bottom left corner */
1045 } else if (ps
->parent
){
1046 /* hook children to our parent coords instead */
1047 ps
->pos_x
= ps
->parent
->pos_x
;
1048 ps
->pos_y
= ps
->parent
->pos_y
;
1050 /* if this is the last child, we might still need to draw a connecting line */
1051 if ((!ps
->next
) && (ps
->parent
))
1052 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1054 ps_to_graph(j
-1) + 10.0, /* whee, use the last value here */
1060 endtime
= ps
->last
->sampledata
->sampletime
;
1061 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1062 time_to_graph(starttime
- graph_start
),
1064 time_to_graph(ps
->last
->sampledata
->sampletime
- starttime
),
1067 /* paint cpu load over these */
1068 ps
->sample
= ps
->first
;
1070 while (ps
->sample
->next
) {
1073 struct ps_sched_struct
*prev
;
1076 ps
->sample
= ps
->sample
->next
;
1078 /* calculate over interval */
1079 rt
= ps
->sample
->runtime
- prev
->runtime
;
1080 wt
= ps
->sample
->waittime
- prev
->waittime
;
1082 prt
= (rt
/ 1000000000) / (ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
);
1083 wrt
= (wt
/ 1000000000) / (ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
);
1085 /* this can happen if timekeeping isn't accurate enough */
1091 if ((prt
< 0.1) && (wrt
< 0.1)) /* =~ 26 (color threshold) */
1094 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1095 time_to_graph(prev
->sampledata
->sampletime
- graph_start
),
1097 time_to_graph(ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
),
1100 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1101 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1102 time_to_graph(prev
->sampledata
->sampletime
- graph_start
),
1103 ps_to_graph(j
+ (1.0 - prt
)),
1104 time_to_graph(ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
),
1109 /* determine where to display the process name */
1110 if ((endtime
- starttime
) < 1.5)
1111 /* too small to fit label inside the box */
1116 /* text label of process name */
1117 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1118 time_to_graph(w
- graph_start
) + 5.0,
1119 ps_to_graph(j
) + 14.0,
1120 escaped
? escaped
: ps
->name
,
1122 (ps
->last
->runtime
- ps
->first
->runtime
) / 1000000000.0,
1123 arg_show_cgroup
? ps
->cgroup
: "");
1124 /* paint lines to the parent process */
1126 /* horizontal part */
1127 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1128 time_to_graph(starttime
- graph_start
),
1129 ps_to_graph(j
) + 10.0,
1131 ps_to_graph(j
) + 10.0);
1133 /* one vertical line connecting all the horizontal ones up */
1135 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1137 ps_to_graph(j
) + 10.0,
1142 j
++; /* count boxes */
1147 /* last pass - determine when idle */
1149 /* make sure we start counting from the point where we actually have
1150 * data: assume that bootchart's first sample is when data started
1154 while (ps
->next_ps
) {
1160 /* need to know last node first */
1161 ps
->sample
= ps
->first
;
1162 i
= ps
->sample
->next
->sampledata
->counter
;
1164 while (ps
->sample
->next
&& i
<(samples
-(arg_hz
/2))) {
1169 struct ps_sched_struct
*sample_hz
;
1171 ps
->sample
= ps
->sample
->next
;
1172 sample_hz
= ps
->sample
;
1173 for (ii
=0;((ii
<(int)arg_hz
/2)&&(ps
->sample
->next
));ii
++)
1174 sample_hz
= sample_hz
->next
;
1176 /* subtract bootchart cpu utilization from total */
1178 for (c
= 0; c
< cpus
; c
++)
1179 crt
+= sample_hz
->sampledata
->runtime
[c
] - ps
->sample
->sampledata
->runtime
[c
];
1180 brt
= sample_hz
->runtime
- ps
->sample
->runtime
;
1182 * our definition of "idle":
1184 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1185 * defaults to 4.0%, which experimentally, is where atom idles
1187 if ((crt
- brt
) < (interval
/ 2.0)) {
1188 idletime
= ps
->sample
->sampledata
->sampletime
- graph_start
;
1189 svg("\n<!-- idle detected at %.03f seconds -->\n",
1191 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1192 time_to_graph(idletime
),
1194 time_to_graph(idletime
),
1195 ps_to_graph(pcount
) + arg_scale_y
);
1196 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1197 time_to_graph(idletime
) + 5.0,
1198 ps_to_graph(pcount
) + arg_scale_y
,
1206 static void svg_top_ten_cpu(void) {
1207 struct ps_struct
*top
[10];
1208 struct ps_struct emptyps
= {};
1209 struct ps_struct
*ps
;
1212 for (n
= 0; n
< (int) ELEMENTSOF(top
); n
++)
1215 /* walk all ps's and setup ptrs */
1217 while ((ps
= get_next_ps(ps
))) {
1218 for (n
= 0; n
< 10; n
++) {
1219 if (ps
->total
<= top
[n
]->total
)
1221 /* cascade insert */
1222 for (m
= 9; m
> n
; m
--)
1229 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1230 for (n
= 0; n
< 10; n
++)
1231 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1238 static void svg_top_ten_pss(void) {
1239 struct ps_struct
*top
[10];
1240 struct ps_struct emptyps
= {};
1241 struct ps_struct
*ps
;
1244 for (n
= 0; n
< (int) ELEMENTSOF(top
); n
++)
1247 /* walk all ps's and setup ptrs */
1249 while ((ps
= get_next_ps(ps
))) {
1250 for (n
= 0; n
< 10; n
++) {
1251 if (ps
->pss_max
<= top
[n
]->pss_max
)
1253 /* cascade insert */
1254 for (m
= 9; m
> n
; m
--)
1261 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1262 for (n
= 0; n
< 10; n
++)
1263 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1270 void svg_do(const char *build
) {
1271 struct ps_struct
*ps
;
1275 memzero(&str
, sizeof(str
));
1279 /* count initcall thread count first */
1281 ksize
= (kcount
? ps_to_graph(kcount
) + (arg_scale_y
* 2) : 0);
1283 /* then count processes */
1284 while ((ps
= get_next_ps(ps
))) {
1290 psize
= ps_to_graph(pcount
) + (arg_scale_y
* 2);
1292 esize
= (arg_entropy
? arg_scale_y
* 7 : 0);
1294 /* after this, we can draw the header with proper sizing */
1296 svg("<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1298 svg("<g transform=\"translate(10,400)\">\n");
1302 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1306 for (c
= -1; c
< (arg_percpu
? cpus
: 0); c
++) {
1308 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1313 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1320 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1326 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
);
1330 svg("<g transform=\"translate(10, 0)\">\n");
1334 svg("<g transform=\"translate(10,200)\">\n");
1339 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
+ psize
);
1345 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
+ psize
+ esize
);
1349 svg("<g transform=\"translate(410,200)\">\n");