]>
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/>.
30 #include <sys/utsname.h>
38 #include "bootchart.h"
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))
47 static const char * const colorwheel
[12] = {
48 "rgb(255,32,32)", // red
49 "rgb(32,192,192)", // cyan
50 "rgb(255,128,32)", // orange
51 "rgb(128,32,192)", // blue-violet
52 "rgb(255,255,32)", // yellow
53 "rgb(192,32,128)", // red-violet
54 "rgb(32,255,32)", // green
55 "rgb(255,64,32)", // red-orange
56 "rgb(32,32,255)", // blue
57 "rgb(255,192,32)", // yellow-orange
58 "rgb(192,32,192)", // violet
59 "rgb(32,192,32)" // yellow-green
62 static double idletime
= -1.0;
63 static int pfiltered
= 0;
64 static int pcount
= 0;
65 static int kcount
= 0;
66 static double psize
= 0;
67 static double ksize
= 0;
68 static double esize
= 0;
69 static struct list_sample_data
*sampledata
;
70 static struct list_sample_data
*prev_sampledata
;
72 static void svg_header(FILE *of
, struct list_sample_data
*head
, double graph_start
) {
75 struct list_sample_data
*sampledata_last
;
80 LIST_FIND_TAIL(link
, sampledata
, head
);
81 sampledata_last
= head
;
82 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
83 sampledata_last
= sampledata
;
86 /* min width is about 1600px due to the label */
87 w
= 150.0 + 10.0 + time_to_graph(sampledata_last
->sampletime
- graph_start
);
88 w
= ((w
< 1600.0) ? 1600.0 : w
);
90 /* height is variable based on pss, psize, ksize */
91 h
= 400.0 + (arg_scale_y
* 30.0) /* base graphs and title */
92 + (arg_pss
? (100.0 * arg_scale_y
) + (arg_scale_y
* 7.0) : 0.0) /* pss estimate */
93 + psize
+ ksize
+ esize
;
95 fprintf(of
, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
96 fprintf(of
, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
97 fprintf(of
, "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
99 //fprintf(of, "<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
100 fprintf(of
, "<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ", w
, h
);
101 fprintf(of
, "xmlns=\"http://www.w3.org/2000/svg\">\n\n");
103 /* write some basic info as a comment, including some help */
104 fprintf(of
, "<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
105 fprintf(of
, "<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
106 fprintf(of
, "<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
107 fprintf(of
, "<!-- inkscape, etc. To display the files on your system, just point -->\n");
108 fprintf(of
, "<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
110 fprintf(of
, "<!-- generated by bootchart version %s, running with options: -->\n", VERSION
);
111 fprintf(of
, "<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz
, arg_samples_len
);
112 fprintf(of
, "<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x
, arg_scale_y
);
113 fprintf(of
, "<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative
, arg_filter
);
114 fprintf(of
, "<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss
, arg_entropy
);
115 fprintf(of
, "<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path
, arg_init_path
);
118 fprintf(of
, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
120 fprintf(of
, " rect { stroke-width: 1; }\n");
121 fprintf(of
, " rect.bg { fill: rgb(255,255,255); }\n");
122 fprintf(of
, " rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
123 fprintf(of
, " rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
124 fprintf(of
, " rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
125 fprintf(of
, " rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
126 fprintf(of
, " rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
127 fprintf(of
, " rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
128 fprintf(of
, " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
129 fprintf(of
, " rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
130 fprintf(of
, " line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
131 fprintf(of
, "// line.sec1 { }\n");
132 fprintf(of
, " line.sec5 { stroke-width: 2; }\n");
133 fprintf(of
, " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
134 fprintf(of
, " line.dot { stroke-dasharray: 2 4; }\n");
135 fprintf(of
, " line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
137 fprintf(of
, " .run { font-size: 8; font-style: italic; }\n");
138 fprintf(of
, " text { font-family: Verdana, Helvetica; font-size: 10; }\n");
139 fprintf(of
, " text.sec { font-size: 8; }\n");
140 fprintf(of
, " text.t1 { font-size: 24; }\n");
141 fprintf(of
, " text.t2 { font-size: 12; }\n");
142 fprintf(of
, " text.idle { font-size: 18; }\n");
144 fprintf(of
, " ]]>\n </style>\n</defs>\n\n");
147 static int svg_title(FILE *of
, const char *build
, int pscount
, double log_start
, int overrun
) {
148 _cleanup_free_
char *cmdline
= NULL
;
149 _cleanup_free_
char *model
= NULL
;
150 _cleanup_free_
char *buf
= NULL
;
151 char date
[256] = "Unknown";
158 r
= read_one_line_file("/proc/cmdline", &cmdline
);
160 log_error_errno(r
, "Unable to read cmdline: %m");
164 /* extract root fs so we can find disk model name in sysfs */
165 /* FIXME: this works only in the simple case */
166 c
= strstr(cmdline
, "root=/dev/");
171 strncpy(rootbdev
, &c
[10], sizeof(rootbdev
) - 1);
173 snprintf(filename
, sizeof(filename
), "/sys/block/%s/device/model", rootbdev
);
175 r
= read_one_line_file(filename
, &model
);
177 log_warning("Error reading disk model for %s: %m\n", rootbdev
);
180 /* various utsname parameters */
183 log_error("Error getting uname info\n");
189 r
= strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", localtime(&t
));
193 r
= read_full_file("/proc/cpuinfo", &buf
, NULL
);
195 return log_error_errno(r
, "Unable to read cpuinfo: %m");
197 cpu
= strstr(buf
, "model name");
199 log_error("Unable to read module name from cpuinfo.\n");
204 c
= strchr(cpu
, '\n');
208 fprintf(of
, "<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
210 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
211 uts
.sysname
, uts
.release
, uts
.version
, uts
.machine
);
212 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n", cpu
);
213 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n", model
);
214 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n", cmdline
);
215 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n", build
);
216 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start
);
217 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
220 fprintf(of
, "%.03fs", idletime
);
222 fprintf(of
, "Not detected");
224 fprintf(of
, "</text>\n");
225 fprintf(of
, "<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
226 arg_hz
, arg_samples_len
, overrun
, pscount
, pfiltered
);
231 static void svg_graph_box(FILE *of
, struct list_sample_data
*head
, int height
, double graph_start
) {
234 double finalsample
= 0.0;
235 struct list_sample_data
*sampledata_last
;
237 sampledata_last
= head
;
238 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
239 sampledata_last
= sampledata
;
242 finalsample
= sampledata_last
->sampletime
;
244 /* outside box, fill */
245 fprintf(of
, "<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
247 time_to_graph(finalsample
- graph_start
),
248 ps_to_graph(height
));
250 for (d
= graph_start
; d
<= finalsample
;
251 d
+= (arg_scale_x
< 2.0 ? 60.0 : arg_scale_x
< 10.0 ? 1.0 : 0.1)) {
252 /* lines for each second */
254 fprintf(of
, " <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
255 time_to_graph(d
- graph_start
),
256 time_to_graph(d
- graph_start
),
257 ps_to_graph(height
));
258 else if (i
% 10 == 0)
259 fprintf(of
, " <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
260 time_to_graph(d
- graph_start
),
261 time_to_graph(d
- graph_start
),
262 ps_to_graph(height
));
264 fprintf(of
, " <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
265 time_to_graph(d
- graph_start
),
266 time_to_graph(d
- graph_start
),
267 ps_to_graph(height
));
271 fprintf(of
, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
272 time_to_graph(d
- graph_start
),
273 -5.0, d
- graph_start
);
279 /* xml comments must not contain "--" */
280 static char* xml_comment_encode(const char* name
) {
283 enc_name
= strdup(name
);
287 for (p
= enc_name
; *p
; p
++)
288 if (p
[0] == '-' && p
[1] == '-')
294 static void svg_pss_graph(FILE *of
,
295 struct list_sample_data
*head
,
296 struct ps_struct
*ps_first
,
297 double graph_start
) {
298 struct ps_struct
*ps
;
300 struct list_sample_data
*sampledata_last
;
302 sampledata_last
= head
;
303 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
304 sampledata_last
= sampledata
;
308 fprintf(of
, "\n\n<!-- Pss memory size graph -->\n");
310 fprintf(of
, "\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
312 /* vsize 1000 == 1000mb */
313 svg_graph_box(of
, head
, 100, graph_start
);
314 /* draw some hlines for usable memory sizes */
315 for (i
= 100000; i
< 1000000; i
+= 100000) {
316 fprintf(of
, " <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
319 time_to_graph(sampledata_last
->sampletime
- graph_start
),
321 fprintf(of
, " <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
322 time_to_graph(sampledata_last
->sampletime
- graph_start
) + 5,
323 kb_to_graph(i
), (1000000 - i
) / 1000);
327 /* now plot the graph itself */
329 prev_sampledata
= head
;
330 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
333 struct ps_sched_struct
*cross_place
;
338 /* put all the small pss blocks into the bottom */
340 while (ps
->next_ps
) {
344 ps
->sample
= ps
->first
;
345 while (ps
->sample
->next
) {
346 ps
->sample
= ps
->sample
->next
;
347 if (ps
->sample
->sampledata
== sampledata
)
350 if (ps
->sample
->sampledata
== sampledata
) {
351 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
352 top
+= ps
->sample
->pss
;
356 while (ps
->sample
->cross
) {
357 cross_place
= ps
->sample
->cross
;
358 ps
= ps
->sample
->cross
->ps_new
;
359 ps
->sample
= cross_place
;
360 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
361 top
+= ps
->sample
->pss
;
364 fprintf(of
, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
366 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
367 kb_to_graph(1000000.0 - top
),
368 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
369 kb_to_graph(top
- bottom
));
372 /* now plot the ones that are of significant size */
374 while (ps
->next_ps
) {
378 ps
->sample
= ps
->first
;
379 while (ps
->sample
->next
) {
380 ps
->sample
= ps
->sample
->next
;
381 if (ps
->sample
->sampledata
== sampledata
)
384 /* don't draw anything smaller than 2mb */
385 if (ps
->sample
->sampledata
!= sampledata
)
387 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
388 top
= bottom
+ ps
->sample
->pss
;
389 fprintf(of
, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
390 colorwheel
[ps
->pid
% 12],
391 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
392 kb_to_graph(1000000.0 - top
),
393 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
394 kb_to_graph(top
- bottom
));
400 while ((cross_place
= ps
->sample
->cross
)) {
401 ps
= ps
->sample
->cross
->ps_new
;
402 ps
->sample
= cross_place
;
403 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
404 top
= bottom
+ ps
->sample
->pss
;
405 fprintf(of
, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
406 colorwheel
[ps
->pid
% 12],
407 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
408 kb_to_graph(1000000.0 - top
),
409 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
410 kb_to_graph(top
- bottom
));
415 prev_sampledata
= sampledata
;
419 /* overlay all the text labels */
421 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
424 struct ps_sched_struct
*prev_sample
;
425 struct ps_sched_struct
*cross_place
;
427 /* put all the small pss blocks into the bottom */
428 ps
= ps_first
->next_ps
;
429 while (ps
->next_ps
) {
434 ps
->sample
= ps
->first
;
435 while (ps
->sample
->next
) {
436 ps
->sample
= ps
->sample
->next
;
437 if (ps
->sample
->sampledata
== sampledata
)
441 if (ps
->sample
->sampledata
== sampledata
) {
442 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
443 top
+= ps
->sample
->pss
;
449 while ((cross_place
= ps
->sample
->cross
)) {
450 ps
= ps
->sample
->cross
->ps_new
;
451 ps
->sample
= cross_place
;
452 if (ps
->sample
->pss
<= (100 * arg_scale_y
))
453 top
+= ps
->sample
->pss
;
457 /* now plot the ones that are of significant size */
459 while (ps
->next_ps
) {
460 prev_sample
= ps
->sample
;
464 ps
->sample
= ps
->first
;
465 while (ps
->sample
->next
) {
466 prev_sample
= ps
->sample
;
467 ps
->sample
= ps
->sample
->next
;
468 if (ps
->sample
->sampledata
== sampledata
)
471 /* don't draw anything smaller than 2mb */
472 if (ps
->sample
->sampledata
== sampledata
) {
473 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
474 top
= bottom
+ ps
->sample
->pss
;
475 /* draw a label with the process / PID */
476 if ((i
== 1) || (prev_sample
->pss
<= (100 * arg_scale_y
)))
477 fprintf(of
, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
478 time_to_graph(sampledata
->sampletime
- graph_start
),
479 kb_to_graph(1000000.0 - bottom
- ((top
- bottom
) / 2)),
486 while ((cross_place
= ps
->sample
->cross
)) {
487 ps
= ps
->sample
->cross
->ps_new
;
488 ps
->sample
= cross_place
;
489 prev_sample
= ps
->sample
->prev
;
490 if (ps
->sample
->pss
> (100 * arg_scale_y
)) {
491 top
= bottom
+ ps
->sample
->pss
;
492 /* draw a label with the process / PID */
493 if ((i
== 1) || (prev_sample
->pss
<= (100 * arg_scale_y
)))
494 fprintf(of
, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
495 time_to_graph(sampledata
->sampletime
- graph_start
),
496 kb_to_graph(1000000.0 - bottom
- ((top
- bottom
) / 2)),
505 /* debug output - full data dump */
506 fprintf(of
, "\n\n<!-- PSS map - csv format -->\n");
508 while (ps
->next_ps
) {
509 _cleanup_free_
char *enc_name
= NULL
;
514 enc_name
= xml_comment_encode(ps
->name
);
518 fprintf(of
, "<!-- %s [%d] pss=", enc_name
, ps
->pid
);
520 ps
->sample
= ps
->first
;
521 while (ps
->sample
->next
) {
522 ps
->sample
= ps
->sample
->next
;
523 fprintf(of
, "%d," , ps
->sample
->pss
);
526 fprintf(of
, " -->\n");
531 static void svg_io_bi_bar(FILE *of
,
532 struct list_sample_data
*head
,
542 struct list_sample_data
*start_sampledata
;
543 struct list_sample_data
*stop_sampledata
;
545 fprintf(of
, "<!-- IO utilization graph - In -->\n");
546 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
549 * calculate rounding range
551 * We need to round IO data since IO block data is not updated on
552 * each poll. Applying a smoothing function loses some burst data,
553 * so keep the smoothing range short.
555 range
= 0.25 / (1.0 / arg_hz
);
557 range
= 2.0; /* no smoothing */
559 /* surrounding box */
560 svg_graph_box(of
, head
, 5, graph_start
);
562 /* find the max IO first */
564 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
570 start
= MAX(i
- ((range
/ 2) - 1), 0);
571 stop
= MIN(i
+ (range
/ 2), n_samples
- 1);
572 diff
= (stop
- start
);
574 start_sampledata
= sampledata
;
575 stop_sampledata
= sampledata
;
577 for (k
= 0; k
< ((range
/2) - 1) && start_sampledata
->link_next
; k
++)
578 start_sampledata
= start_sampledata
->link_next
;
580 for (k
= 0; k
< (range
/2) && stop_sampledata
->link_prev
; k
++)
581 stop_sampledata
= stop_sampledata
->link_prev
;
583 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
) / diff
;
590 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
) / diff
;
600 prev_sampledata
= head
;
601 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
608 start
= MAX(i
- ((range
/ 2) - 1), 0);
609 stop
= MIN(i
+ (range
/ 2), n_samples
);
610 diff
= (stop
- start
);
612 start_sampledata
= sampledata
;
613 stop_sampledata
= sampledata
;
615 for (k
= 0; k
< ((range
/2)-1) && start_sampledata
->link_next
; k
++)
616 start_sampledata
= start_sampledata
->link_next
;
618 for (k
= 0; k
< (range
/2) && stop_sampledata
->link_prev
; k
++)
619 stop_sampledata
= stop_sampledata
->link_prev
;
621 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
) / diff
;
627 fprintf(of
, "<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
628 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
629 (arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5)),
630 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
631 pbi
* (arg_scale_y
* 5));
633 /* labels around highest value */
635 fprintf(of
, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
636 time_to_graph(sampledata
->sampletime
- graph_start
) + 5,
637 ((arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5))) + 15,
638 max
/ 1024.0 / (interval
/ 1000000000.0));
642 prev_sampledata
= sampledata
;
646 static void svg_io_bo_bar(FILE *of
,
647 struct list_sample_data
*head
,
656 struct list_sample_data
*start_sampledata
;
657 struct list_sample_data
*stop_sampledata
;
659 fprintf(of
, "<!-- IO utilization graph - out -->\n");
660 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
663 * calculate rounding range
665 * We need to round IO data since IO block data is not updated on
666 * each poll. Applying a smoothing function loses some burst data,
667 * so keep the smoothing range short.
669 range
= 0.25 / (1.0 / arg_hz
);
671 range
= 2.0; /* no smoothing */
673 /* surrounding box */
674 svg_graph_box(of
, head
, 5, graph_start
);
676 /* find the max IO first */
678 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
684 start
= MAX(i
- ((range
/ 2) - 1), 0);
685 stop
= MIN(i
+ (range
/ 2), n_samples
- 1);
686 diff
= (stop
- start
);
688 start_sampledata
= sampledata
;
689 stop_sampledata
= sampledata
;
691 for (k
= 0; k
< (range
/2) - 1 && start_sampledata
->link_next
; k
++)
692 start_sampledata
= start_sampledata
->link_next
;
694 for (k
= 0; k
< (range
/2) && stop_sampledata
->link_prev
; k
++)
695 stop_sampledata
= stop_sampledata
->link_prev
;
697 tot
= (double)(stop_sampledata
->blockstat
.bi
- start_sampledata
->blockstat
.bi
) / diff
;
701 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
) / diff
;
711 prev_sampledata
= head
;
714 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
715 int start
, stop
, diff
;
720 start
= MAX(i
- ((range
/ 2) - 1), 0);
721 stop
= MIN(i
+ (range
/ 2), n_samples
);
722 diff
= (stop
- start
);
724 start_sampledata
= sampledata
;
725 stop_sampledata
= sampledata
;
727 for (k
= 0; k
< ((range
/2)-1) && start_sampledata
->link_next
; k
++)
728 start_sampledata
= start_sampledata
->link_next
;
730 for (k
= 0; k
< (range
/2) && stop_sampledata
->link_prev
; k
++)
731 stop_sampledata
= stop_sampledata
->link_prev
;
733 tot
= (double)(stop_sampledata
->blockstat
.bo
- start_sampledata
->blockstat
.bo
)
740 fprintf(of
, "<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
741 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
742 (arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5)),
743 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
744 pbo
* (arg_scale_y
* 5));
746 /* labels around highest bo value */
748 fprintf(of
, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
749 time_to_graph(sampledata
->sampletime
- graph_start
) + 5,
750 ((arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5))),
751 max
/ 1024.0 / (interval
/ 1000000000.0));
755 prev_sampledata
= sampledata
;
759 static void svg_cpu_bar(FILE *of
, struct list_sample_data
*head
, int n_cpus
, int cpu_num
, double graph_start
) {
761 fprintf(of
, "<!-- CPU utilization graph -->\n");
764 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
766 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num
);
768 /* surrounding box */
769 svg_graph_box(of
, head
, 5, graph_start
);
771 /* bars for each sample, proportional to the CPU util. */
772 prev_sampledata
= head
;
773 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
781 for (c
= 0; c
< n_cpus
; c
++)
782 trt
+= sampledata
->runtime
[c
] - prev_sampledata
->runtime
[c
];
784 trt
= sampledata
->runtime
[cpu_num
] - prev_sampledata
->runtime
[cpu_num
];
786 trt
= trt
/ 1000000000.0;
789 trt
= trt
/ (double)n_cpus
;
792 ptrt
= trt
/ (sampledata
->sampletime
- prev_sampledata
->sampletime
);
798 fprintf(of
, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
799 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
800 (arg_scale_y
* 5) - (ptrt
* (arg_scale_y
* 5)),
801 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
802 ptrt
* (arg_scale_y
* 5));
804 prev_sampledata
= sampledata
;
808 static void svg_wait_bar(FILE *of
, struct list_sample_data
*head
, int n_cpus
, int cpu_num
, double graph_start
) {
810 fprintf(of
, "<!-- Wait time aggregation box -->\n");
813 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
815 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num
);
817 /* surrounding box */
818 svg_graph_box(of
, head
, 5, graph_start
);
820 /* bars for each sample, proportional to the CPU util. */
821 prev_sampledata
= head
;
822 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
830 for (c
= 0; c
< n_cpus
; c
++)
831 twt
+= sampledata
->waittime
[c
] - prev_sampledata
->waittime
[c
];
833 twt
= sampledata
->waittime
[cpu_num
] - prev_sampledata
->waittime
[cpu_num
];
835 twt
= twt
/ 1000000000.0;
838 twt
= twt
/ (double)n_cpus
;
841 ptwt
= twt
/ (sampledata
->sampletime
- prev_sampledata
->sampletime
);
847 fprintf(of
, "<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
848 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
849 ((arg_scale_y
* 5) - (ptwt
* (arg_scale_y
* 5))),
850 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
851 ptwt
* (arg_scale_y
* 5));
853 prev_sampledata
= sampledata
;
857 static void svg_entropy_bar(FILE *of
, struct list_sample_data
*head
, double graph_start
) {
859 fprintf(of
, "<!-- entropy pool graph -->\n");
861 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
862 /* surrounding box */
863 svg_graph_box(of
, head
, 5, graph_start
);
865 /* bars for each sample, scale 0-4096 */
866 prev_sampledata
= head
;
867 LIST_FOREACH_BEFORE(link
, sampledata
, head
) {
868 fprintf(of
, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
869 time_to_graph(prev_sampledata
->sampletime
- graph_start
),
870 ((arg_scale_y
* 5) - ((sampledata
->entropy_avail
/ 4096.) * (arg_scale_y
* 5))),
871 time_to_graph(sampledata
->sampletime
- prev_sampledata
->sampletime
),
872 (sampledata
->entropy_avail
/ 4096.) * (arg_scale_y
* 5));
873 prev_sampledata
= sampledata
;
877 static struct ps_struct
*get_next_ps(struct ps_struct
*ps
, struct ps_struct
*ps_first
) {
879 * walk the list of processes and return the next one to be
893 /* go back for parent siblings */
895 if (ps
->parent
&& ps
->parent
->next
)
896 return ps
->parent
->next
;
906 static bool ps_filter(struct ps_struct
*ps
) {
910 /* can't draw data when there is only 1 sample (need start + stop) */
911 if (ps
->first
== ps
->last
)
914 /* don't filter kthreadd */
918 /* drop stuff that doesn't use any real CPU time */
919 if (ps
->total
<= 0.001)
925 static void svg_do_initcall(FILE *of
, struct list_sample_data
*head
, int count_only
, double graph_start
) {
926 _cleanup_pclose_
FILE *f
= NULL
;
932 /* can't plot initcall when disabled or in relative mode */
933 if (!arg_initcall
|| arg_relative
) {
939 fprintf(of
, "<!-- initcall -->\n");
940 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
941 /* surrounding box */
942 svg_graph_box(of
, head
, kcount
, graph_start
);
948 * Initcall graphing - parses dmesg buffer and displays kernel threads
949 * This somewhat uses the same methods and scaling to show processes
950 * but looks a lot simpler. It's overlaid entirely onto the PS graph
954 f
= popen("dmesg", "r");
963 if (fgets(l
, sizeof(l
) - 1, f
) == NULL
)
966 c
= sscanf(l
, "[%lf] initcall %s %*s %d %*s %d %*s",
967 &t
, func
, &ret
, &usecs
);
969 /* also parse initcalls done by module loading */
970 c
= sscanf(l
, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
971 &t
, func
, &ret
, &usecs
);
976 /* chop the +0xXX/0xXX stuff */
977 while(func
[z
] != '+')
982 /* filter out irrelevant stuff */
988 fprintf(of
, "<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
989 func
, t
, usecs
, ret
);
995 fprintf(of
, " <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
996 time_to_graph(t
- (usecs
/ 1000000.0)),
998 time_to_graph(usecs
/ 1000000.0),
1002 fprintf(of
, " <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
1003 time_to_graph(t
- (usecs
/ 1000000.0)) + 5,
1004 ps_to_graph(kcount
) + 15,
1005 func
, usecs
/ 1000000.0);
1011 static void svg_ps_bars(FILE *of
,
1012 struct list_sample_data
*head
,
1015 struct ps_struct
*ps_first
,
1019 struct ps_struct
*ps
;
1025 fprintf(of
, "<!-- Process graph -->\n");
1026 fprintf(of
, "<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
1028 /* surrounding box */
1029 svg_graph_box(of
, head
, pcount
, graph_start
);
1031 /* pass 2 - ps boxes */
1033 while ((ps
= get_next_ps(ps
, ps_first
))) {
1034 _cleanup_free_
char *enc_name
= NULL
, *escaped
= NULL
;
1039 if (!utf8_is_printable(ps
->name
, strlen(ps
->name
)))
1040 escaped
= utf8_escape_non_printable(ps
->name
);
1042 enc_name
= xml_comment_encode(escaped
? escaped
: ps
->name
);
1046 /* leave some trace of what we actually filtered etc. */
1047 fprintf(of
, "<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name
, ps
->pid
,
1048 ps
->ppid
, ps
->total
);
1050 starttime
= ps
->first
->sampledata
->sampletime
;
1052 if (!ps_filter(ps
)) {
1053 /* remember where _to_ our children need to draw a line */
1054 ps
->pos_x
= time_to_graph(starttime
- graph_start
);
1055 ps
->pos_y
= ps_to_graph(j
+1); /* bottom left corner */
1056 } else if (ps
->parent
){
1057 /* hook children to our parent coords instead */
1058 ps
->pos_x
= ps
->parent
->pos_x
;
1059 ps
->pos_y
= ps
->parent
->pos_y
;
1061 /* if this is the last child, we might still need to draw a connecting line */
1062 if ((!ps
->next
) && (ps
->parent
))
1063 fprintf(of
, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1065 ps_to_graph(j
-1) + 10.0, /* whee, use the last value here */
1071 endtime
= ps
->last
->sampledata
->sampletime
;
1072 fprintf(of
, " <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1073 time_to_graph(starttime
- graph_start
),
1075 time_to_graph(ps
->last
->sampledata
->sampletime
- starttime
),
1078 /* paint cpu load over these */
1079 ps
->sample
= ps
->first
;
1081 while (ps
->sample
->next
) {
1084 struct ps_sched_struct
*prev
;
1087 ps
->sample
= ps
->sample
->next
;
1089 /* calculate over interval */
1090 rt
= ps
->sample
->runtime
- prev
->runtime
;
1091 wt
= ps
->sample
->waittime
- prev
->waittime
;
1093 prt
= (rt
/ 1000000000) / (ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
);
1094 wrt
= (wt
/ 1000000000) / (ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
);
1096 /* this can happen if timekeeping isn't accurate enough */
1102 if ((prt
< 0.1) && (wrt
< 0.1)) /* =~ 26 (color threshold) */
1105 fprintf(of
, " <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1106 time_to_graph(prev
->sampledata
->sampletime
- graph_start
),
1108 time_to_graph(ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
),
1111 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1112 fprintf(of
, " <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1113 time_to_graph(prev
->sampledata
->sampletime
- graph_start
),
1114 ps_to_graph(j
+ (1.0 - prt
)),
1115 time_to_graph(ps
->sample
->sampledata
->sampletime
- prev
->sampledata
->sampletime
),
1120 /* determine where to display the process name */
1121 if ((endtime
- starttime
) < 1.5)
1122 /* too small to fit label inside the box */
1127 /* text label of process name */
1128 fprintf(of
, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n",
1129 time_to_graph(w
- graph_start
) + 5.0,
1130 ps_to_graph(j
) + 14.0,
1131 escaped
? escaped
: ps
->name
,
1133 (ps
->last
->runtime
- ps
->first
->runtime
) / 1000000000.0,
1134 arg_show_cgroup
? ps
->cgroup
: "");
1135 /* paint lines to the parent process */
1137 /* horizontal part */
1138 fprintf(of
, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1139 time_to_graph(starttime
- graph_start
),
1140 ps_to_graph(j
) + 10.0,
1142 ps_to_graph(j
) + 10.0);
1144 /* one vertical line connecting all the horizontal ones up */
1146 fprintf(of
, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1148 ps_to_graph(j
) + 10.0,
1153 j
++; /* count boxes */
1158 /* last pass - determine when idle */
1160 /* make sure we start counting from the point where we actually have
1161 * data: assume that bootchart's first sample is when data started
1165 while (ps
->next_ps
) {
1171 /* need to know last node first */
1172 ps
->sample
= ps
->first
;
1173 i
= ps
->sample
->next
->sampledata
->counter
;
1175 while (ps
->sample
->next
&& i
<(n_samples
-(arg_hz
/2))) {
1180 struct ps_sched_struct
*sample_hz
;
1182 ps
->sample
= ps
->sample
->next
;
1183 sample_hz
= ps
->sample
;
1184 for (ii
= 0; (ii
< (int)arg_hz
/2) && sample_hz
->next
; ii
++)
1185 sample_hz
= sample_hz
->next
;
1187 /* subtract bootchart cpu utilization from total */
1189 for (c
= 0; c
< n_cpus
; c
++)
1190 crt
+= sample_hz
->sampledata
->runtime
[c
] - ps
->sample
->sampledata
->runtime
[c
];
1192 brt
= sample_hz
->runtime
- ps
->sample
->runtime
;
1194 * our definition of "idle":
1196 * if for (hz / 2) we've used less CPU than (interval / 2) ...
1197 * defaults to 4.0%, which experimentally, is where atom idles
1199 if ((crt
- brt
) < (interval
/ 2.0)) {
1200 idletime
= ps
->sample
->sampledata
->sampletime
- graph_start
;
1201 fprintf(of
, "\n<!-- idle detected at %.03f seconds -->\n", idletime
);
1202 fprintf(of
, "<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1203 time_to_graph(idletime
),
1205 time_to_graph(idletime
),
1206 ps_to_graph(pcount
) + arg_scale_y
);
1207 fprintf(of
, "<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
1208 time_to_graph(idletime
) + 5.0,
1209 ps_to_graph(pcount
) + arg_scale_y
,
1218 static void svg_top_ten_cpu(FILE *of
, struct ps_struct
*ps_first
) {
1219 struct ps_struct
*top
[10];
1220 struct ps_struct emptyps
= {};
1221 struct ps_struct
*ps
;
1224 for (n
= 0; n
< (int) ELEMENTSOF(top
); n
++)
1227 /* walk all ps's and setup ptrs */
1229 while ((ps
= get_next_ps(ps
, ps_first
))) {
1230 for (n
= 0; n
< 10; n
++) {
1231 if (ps
->total
<= top
[n
]->total
)
1233 /* cascade insert */
1234 for (m
= 9; m
> n
; m
--)
1241 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1242 for (n
= 0; n
< 10; n
++)
1243 fprintf(of
, "<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1250 static void svg_top_ten_pss(FILE *of
, struct ps_struct
*ps_first
) {
1251 struct ps_struct
*top
[10];
1252 struct ps_struct emptyps
= {};
1253 struct ps_struct
*ps
;
1256 for (n
= 0; n
< (int) ELEMENTSOF(top
); n
++)
1259 /* walk all ps's and setup ptrs */
1261 while ((ps
= get_next_ps(ps
, ps_first
))) {
1262 for (n
= 0; n
< 10; n
++) {
1263 if (ps
->pss_max
<= top
[n
]->pss_max
)
1266 /* cascade insert */
1267 for (m
= 9; m
> n
; m
--)
1274 fprintf(of
, "<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1275 for (n
= 0; n
< 10; n
++)
1276 fprintf(of
, "<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1283 int svg_do(FILE *of
,
1285 struct list_sample_data
*head
,
1286 struct ps_struct
*ps_first
,
1295 struct ps_struct
*ps
;
1301 /* count initcall thread count first */
1302 svg_do_initcall(of
, head
, 1, graph_start
);
1303 ksize
= kcount
? ps_to_graph(kcount
) + (arg_scale_y
* 2) : 0;
1305 /* then count processes */
1306 while ((ps
= get_next_ps(ps
, ps_first
))) {
1312 psize
= ps_to_graph(pcount
) + (arg_scale_y
* 2);
1314 esize
= (arg_entropy
? arg_scale_y
* 7 : 0);
1316 /* after this, we can draw the header with proper sizing */
1317 svg_header(of
, head
, graph_start
);
1318 fprintf(of
, "<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
1320 fprintf(of
, "<g transform=\"translate(10,400)\">\n");
1321 svg_io_bi_bar(of
, head
, n_samples
, graph_start
, interval
);
1322 fprintf(of
, "</g>\n\n");
1324 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1325 svg_io_bo_bar(of
, head
, n_samples
, graph_start
, interval
);
1326 fprintf(of
, "</g>\n\n");
1328 for (c
= -1; c
< (arg_percpu
? n_cpus
: 0); c
++) {
1330 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1331 svg_cpu_bar(of
, head
, n_cpus
, c
, graph_start
);
1332 fprintf(of
, "</g>\n\n");
1335 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1336 svg_wait_bar(of
, head
, n_cpus
, c
, graph_start
);
1337 fprintf(of
, "</g>\n\n");
1342 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
));
1343 svg_do_initcall(of
, head
, 0, graph_start
);
1344 fprintf(of
, "</g>\n\n");
1348 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
);
1349 svg_ps_bars(of
, head
, n_samples
, n_cpus
, ps_first
, graph_start
, interval
);
1350 fprintf(of
, "</g>\n\n");
1352 fprintf(of
, "<g transform=\"translate(10, 0)\">\n");
1353 r
= svg_title(of
, build
, pscount
, log_start
, overrun
);
1354 fprintf(of
, "</g>\n\n");
1359 fprintf(of
, "<g transform=\"translate(10,200)\">\n");
1360 svg_top_ten_cpu(of
, ps_first
);
1361 fprintf(of
, "</g>\n\n");
1364 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
+ psize
);
1365 svg_entropy_bar(of
, head
, graph_start
);
1366 fprintf(of
, "</g>\n\n");
1370 fprintf(of
, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* offset
) + ksize
+ psize
+ esize
);
1371 svg_pss_graph(of
, head
, ps_first
, graph_start
);
1372 fprintf(of
, "</g>\n\n");
1374 fprintf(of
, "<g transform=\"translate(410,200)\">\n");
1375 svg_top_ten_pss(of
, ps_first
);
1376 fprintf(of
, "</g>\n\n");
1379 /* fprintf footer */
1380 fprintf(of
, "\n</svg>\n");