]>
git.ipfire.org Git - thirdparty/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 Coproration
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"
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 #define max(x, y) (((x) > (y)) ? (x) : (y))
48 #define min(x, y) (((x) < (y)) ? (x) : (y))
50 static char str
[8092];
52 #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
54 static const char * const colorwheel
[12] = {
55 "rgb(255,32,32)", // red
56 "rgb(32,192,192)", // cyan
57 "rgb(255,128,32)", // orange
58 "rgb(128,32,192)", // blue-violet
59 "rgb(255,255,32)", // yellow
60 "rgb(192,32,128)", // red-violet
61 "rgb(32,255,32)", // green
62 "rgb(255,64,32)", // red-orange
63 "rgb(32,32,255)", // blue
64 "rgb(255,192,32)", // yellow-orange
65 "rgb(192,32,192)", // violet
66 "rgb(32,192,32)" // yellow-green
69 static double idletime
= -1.0;
70 static int pfiltered
= 0;
71 static int pcount
= 0;
72 static int kcount
= 0;
73 static float psize
= 0;
74 static float ksize
= 0;
75 static float esize
= 0;
77 static void svg_header(void) {
81 /* min width is about 1600px due to the label */
82 w
= 150.0 + 10.0 + time_to_graph(sampletime
[samples
-1] - graph_start
);
83 w
= ((w
< 1600.0) ? 1600.0 : w
);
85 /* height is variable based on pss, psize, ksize */
86 h
= 400.0 + (arg_scale_y
* 30.0) /* base graphs and title */
87 + (arg_pss
? (100.0 * arg_scale_y
) + (arg_scale_y
* 7.0) : 0.0) /* pss estimate */
88 + psize
+ ksize
+ esize
;
90 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
91 svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
92 svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
94 //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
95 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
97 svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
99 /* write some basic info as a comment, including some help */
100 svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
101 svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
102 svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
103 svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
104 svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
106 svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION
);
107 svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz
, arg_samples_len
);
108 svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x
, arg_scale_y
);
109 svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative
, arg_filter
);
110 svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss
, arg_entropy
);
111 svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path
, arg_init_path
);
114 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
116 svg(" rect { stroke-width: 1; }\n");
117 svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
118 svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
119 svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
120 svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
121 svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
122 svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
123 svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
124 svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
125 svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
126 svg("// line.sec1 { }\n");
127 svg(" line.sec5 { stroke-width: 2; }\n");
128 svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
129 svg(" line.dot { stroke-dasharray: 2 4; }\n");
130 svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
132 svg(" .run { font-size: 8; font-style: italic; }\n");
133 svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
134 svg(" text.sec { font-size: 8; }\n");
135 svg(" text.t1 { font-size: 24; }\n");
136 svg(" text.t2 { font-size: 12; }\n");
137 svg(" text.idle { font-size: 18; }\n");
139 svg(" ]]>\n </style>\n</defs>\n\n");
142 static void svg_title(const char *build
) {
143 char cmdline
[256] = "";
144 char filename
[PATH_MAX
];
146 char rootbdev
[16] = "Unknown";
147 char model
[256] = "Unknown";
148 char date
[256] = "Unknown";
149 char cpu
[256] = "Unknown";
156 /* grab /proc/cmdline */
157 fd
= openat(procfd
, "cmdline", O_RDONLY
);
160 if (!fgets(cmdline
, 255, f
))
161 sprintf(cmdline
, "Unknown");
165 /* extract root fs so we can find disk model name in sysfs */
166 /* FIXME: this works only in the simple case */
167 c
= strstr(cmdline
, "root=/dev/");
169 strncpy(rootbdev
, &c
[10], 3);
171 sprintf(filename
, "block/%s/device/model", rootbdev
);
172 fd
= openat(sysfd
, filename
, O_RDONLY
);
175 if (!fgets(model
, 255, f
))
176 fprintf(stderr
, "Error reading disk model for %s\n", rootbdev
);
181 /* various utsname parameters */
183 fprintf(stderr
, "Error getting uname info\n");
187 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", localtime(&t
));
190 fd
= openat(procfd
, "cpuinfo", O_RDONLY
);
193 while (fgets(buf
, 255, f
)) {
194 if (strstr(buf
, "model name")) {
195 strncpy(cpu
, &buf
[13], 255);
202 svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
204 svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
205 uts
.sysname
, uts
.release
, uts
.version
, uts
.machine
);
206 svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
208 svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
210 svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
212 svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
214 svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start
);
215 svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
218 svg("%.03fs", idletime
);
222 svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
223 arg_hz
, arg_samples_len
, overrun
, pscount
, pfiltered
);
226 static void svg_graph_box(int height
) {
230 /* outside box, fill */
231 svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
233 time_to_graph(sampletime
[samples
-1] - graph_start
),
234 ps_to_graph(height
));
236 for (d
= graph_start
; d
<= sampletime
[samples
-1];
237 d
+= (arg_scale_x
< 2.0 ? 60.0 : arg_scale_x
< 10.0 ? 1.0 : 0.1)) {
238 /* lines for each second */
240 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
241 time_to_graph(d
- graph_start
),
242 time_to_graph(d
- graph_start
),
243 ps_to_graph(height
));
244 else if (i
% 10 == 0)
245 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
246 time_to_graph(d
- graph_start
),
247 time_to_graph(d
- graph_start
),
248 ps_to_graph(height
));
250 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
251 time_to_graph(d
- graph_start
),
252 time_to_graph(d
- graph_start
),
253 ps_to_graph(height
));
257 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
258 time_to_graph(d
- graph_start
),
266 /* xml comments must not contain "--" */
267 static char* xml_comment_encode(const char* name
) {
270 enc_name
= strdup(name
);
274 for (p
= enc_name
; *p
; p
++)
275 if (p
[0] == '-' && p
[1] == '-')
281 static void svg_pss_graph(void) {
282 struct ps_struct
*ps
;
285 svg("\n\n<!-- Pss memory size graph -->\n");
287 svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
289 /* vsize 1000 == 1000mb */
291 /* draw some hlines for usable memory sizes */
292 for (i
= 100000; i
< 1000000; i
+= 100000) {
293 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
296 time_to_graph(sampletime
[samples
-1] - graph_start
),
298 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
299 time_to_graph(sampletime
[samples
-1] - graph_start
) + 5,
300 kb_to_graph(i
), (1000000 - i
) / 1000);
304 /* now plot the graph itself */
305 for (i
= 1; i
< samples
; i
++) {
312 /* put all the small pss blocks into the bottom */
314 while (ps
->next_ps
) {
318 if (ps
->sample
[i
].pss
<= (100 * arg_scale_y
))
319 top
+= ps
->sample
[i
].pss
;
321 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
323 time_to_graph(sampletime
[i
- 1] - graph_start
),
324 kb_to_graph(1000000.0 - top
),
325 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
326 kb_to_graph(top
- bottom
));
330 /* now plot the ones that are of significant size */
332 while (ps
->next_ps
) {
336 /* don't draw anything smaller than 2mb */
337 if (ps
->sample
[i
].pss
> (100 * arg_scale_y
)) {
338 top
= bottom
+ ps
->sample
[i
].pss
;
339 svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
340 colorwheel
[ps
->pid
% 12],
341 time_to_graph(sampletime
[i
- 1] - graph_start
),
342 kb_to_graph(1000000.0 - top
),
343 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
344 kb_to_graph(top
- bottom
));
350 /* overlay all the text labels */
351 for (i
= 1; i
< samples
; i
++) {
358 /* put all the small pss blocks into the bottom */
360 while (ps
->next_ps
) {
364 if (ps
->sample
[i
].pss
<= (100 * arg_scale_y
))
365 top
+= ps
->sample
[i
].pss
;
370 /* now plot the ones that are of significant size */
372 while (ps
->next_ps
) {
376 /* don't draw anything smaller than 2mb */
377 if (ps
->sample
[i
].pss
> (100 * arg_scale_y
)) {
378 top
= bottom
+ ps
->sample
[i
].pss
;
379 /* draw a label with the process / PID */
380 if ((i
== 1) || (ps
->sample
[i
- 1].pss
<= (100 * arg_scale_y
)))
381 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
382 time_to_graph(sampletime
[i
] - graph_start
),
383 kb_to_graph(1000000.0 - bottom
- ((top
- bottom
) / 2)),
391 /* debug output - full data dump */
392 svg("\n\n<!-- PSS map - csv format -->\n");
394 while (ps
->next_ps
) {
395 char _cleanup_free_
*enc_name
;
400 enc_name
= xml_comment_encode(ps
->name
);
404 svg("<!-- %s [%d] pss=", enc_name
, ps
->pid
);
406 for (i
= 0; i
< samples
; i
++) {
407 svg("%d," , ps
->sample
[i
].pss
);
414 static void svg_io_bi_bar(void) {
420 svg("<!-- IO utilization graph - In -->\n");
422 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
425 * calculate rounding range
427 * We need to round IO data since IO block data is not updated on
428 * each poll. Applying a smoothing function loses some burst data,
429 * so keep the smoothing range short.
431 range
= 0.25 / (1.0 / arg_hz
);
433 range
= 2.0; /* no smoothing */
435 /* surrounding box */
438 /* find the max IO first */
439 for (i
= 1; i
< samples
; i
++) {
444 start
= max(i
- ((range
/ 2) - 1), 0);
445 stop
= min(i
+ (range
/ 2), samples
- 1);
447 tot
= (double)(blockstat
[stop
].bi
- blockstat
[start
].bi
)
453 tot
= (double)(blockstat
[stop
].bo
- blockstat
[start
].bo
)
460 for (i
= 1; i
< samples
; i
++) {
466 start
= max(i
- ((range
/ 2) - 1), 0);
467 stop
= min(i
+ (range
/ 2), samples
);
469 tot
= (double)(blockstat
[stop
].bi
- blockstat
[start
].bi
)
474 svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
475 time_to_graph(sampletime
[i
- 1] - graph_start
),
476 (arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5)),
477 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
478 pbi
* (arg_scale_y
* 5));
480 /* labels around highest value */
482 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
483 time_to_graph(sampletime
[i
] - graph_start
) + 5,
484 ((arg_scale_y
* 5) - (pbi
* (arg_scale_y
* 5))) + 15,
485 max
/ 1024.0 / (interval
/ 1000000000.0));
490 static void svg_io_bo_bar(void) {
496 svg("<!-- IO utilization graph - out -->\n");
498 svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
501 * calculate rounding range
503 * We need to round IO data since IO block data is not updated on
504 * each poll. Applying a smoothing function loses some burst data,
505 * so keep the smoothing range short.
507 range
= 0.25 / (1.0 / arg_hz
);
509 range
= 2.0; /* no smoothing */
511 /* surrounding box */
514 /* find the max IO first */
515 for (i
= 1; i
< samples
; i
++) {
520 start
= max(i
- ((range
/ 2) - 1), 0);
521 stop
= min(i
+ (range
/ 2), samples
- 1);
523 tot
= (double)(blockstat
[stop
].bi
- blockstat
[start
].bi
)
527 tot
= (double)(blockstat
[stop
].bo
- blockstat
[start
].bo
)
536 for (i
= 1; i
< samples
; i
++) {
542 start
= max(i
- ((range
/ 2) - 1), 0);
543 stop
= min(i
+ (range
/ 2), samples
);
545 tot
= (double)(blockstat
[stop
].bo
- blockstat
[start
].bo
)
550 svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
551 time_to_graph(sampletime
[i
- 1] - graph_start
),
552 (arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5)),
553 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
554 pbo
* (arg_scale_y
* 5));
556 /* labels around highest bo value */
558 svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
559 time_to_graph(sampletime
[i
] - graph_start
) + 5,
560 ((arg_scale_y
* 5) - (pbo
* (arg_scale_y
* 5))),
561 max
/ 1024.0 / (interval
/ 1000000000.0));
566 static void svg_cpu_bar(void) {
569 svg("<!-- CPU utilization graph -->\n");
571 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
572 /* surrounding box */
575 /* bars for each sample, proportional to the CPU util. */
576 for (i
= 1; i
< samples
; i
++) {
583 for (c
= 0; c
< cpus
; c
++)
584 trt
+= cpustat
[c
].sample
[i
].runtime
- cpustat
[c
].sample
[i
- 1].runtime
;
586 trt
= trt
/ 1000000000.0;
588 trt
= trt
/ (double)cpus
;
591 ptrt
= trt
/ (sampletime
[i
] - sampletime
[i
- 1]);
597 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
598 time_to_graph(sampletime
[i
- 1] - graph_start
),
599 (arg_scale_y
* 5) - (ptrt
* (arg_scale_y
* 5)),
600 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
601 ptrt
* (arg_scale_y
* 5));
606 static void svg_wait_bar(void) {
609 svg("<!-- Wait time aggregation box -->\n");
611 svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
613 /* surrounding box */
616 /* bars for each sample, proportional to the CPU util. */
617 for (i
= 1; i
< samples
; i
++) {
624 for (c
= 0; c
< cpus
; c
++)
625 twt
+= cpustat
[c
].sample
[i
].waittime
- cpustat
[c
].sample
[i
- 1].waittime
;
627 twt
= twt
/ 1000000000.0;
629 twt
= twt
/ (double)cpus
;
632 ptwt
= twt
/ (sampletime
[i
] - sampletime
[i
- 1]);
638 svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
639 time_to_graph(sampletime
[i
- 1] - graph_start
),
640 ((arg_scale_y
* 5) - (ptwt
* (arg_scale_y
* 5))),
641 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
642 ptwt
* (arg_scale_y
* 5));
648 static void svg_entropy_bar(void) {
651 svg("<!-- entropy pool graph -->\n");
653 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
654 /* surrounding box */
657 /* bars for each sample, scale 0-4096 */
658 for (i
= 1; i
< samples
; i
++) {
659 /* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
660 svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
661 time_to_graph(sampletime
[i
- 1] - graph_start
),
662 ((arg_scale_y
* 5) - ((entropy_avail
[i
] / 4096.) * (arg_scale_y
* 5))),
663 time_to_graph(sampletime
[i
] - sampletime
[i
- 1]),
664 (entropy_avail
[i
] / 4096.) * (arg_scale_y
* 5));
668 static struct ps_struct
*get_next_ps(struct ps_struct
*ps
) {
670 * walk the list of processes and return the next one to be
684 /* go back for parent siblings */
687 if (ps
->parent
->next
)
688 return ps
->parent
->next
;
697 static int ps_filter(struct ps_struct
*ps
) {
701 /* can't draw data when there is only 1 sample (need start + stop) */
702 if (ps
->first
== ps
->last
)
705 /* don't filter kthreadd */
709 /* drop stuff that doesn't use any real CPU time */
710 if (ps
->total
<= 0.001)
716 static void svg_do_initcall(int count_only
) {
717 FILE _cleanup_pclose_
*f
= NULL
;
723 /* can't plot initcall when disabled or in relative mode */
724 if (!initcall
|| arg_relative
) {
730 svg("<!-- initcall -->\n");
732 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
733 /* surrounding box */
734 svg_graph_box(kcount
);
740 * Initcall graphing - parses dmesg buffer and displays kernel threads
741 * This somewhat uses the same methods and scaling to show processes
742 * but looks a lot simpler. It's overlaid entirely onto the PS graph
746 f
= popen("dmesg", "r");
755 if (fgets(l
, sizeof(l
) - 1, f
) == NULL
)
758 c
= sscanf(l
, "[%lf] initcall %s %*s %d %*s %d %*s",
759 &t
, func
, &ret
, &usecs
);
761 /* also parse initcalls done by module loading */
762 c
= sscanf(l
, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
763 &t
, func
, &ret
, &usecs
);
768 /* chop the +0xXX/0xXX stuff */
769 while(func
[z
] != '+')
774 /* filter out irrelevant stuff */
780 svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
781 func
, t
, usecs
, ret
);
787 svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
788 time_to_graph(t
- (usecs
/ 1000000.0)),
790 time_to_graph(usecs
/ 1000000.0),
794 svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
795 time_to_graph(t
- (usecs
/ 1000000.0)) + 5,
796 ps_to_graph(kcount
) + 15,
804 static void svg_ps_bars(void) {
805 struct ps_struct
*ps
;
811 svg("<!-- Process graph -->\n");
813 svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
815 /* surrounding box */
816 svg_graph_box(pcount
);
818 /* pass 2 - ps boxes */
820 while ((ps
= get_next_ps(ps
))) {
821 char _cleanup_free_
*enc_name
;
829 enc_name
= xml_comment_encode(ps
->name
);
833 /* leave some trace of what we actually filtered etc. */
834 svg("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name
, ps
->pid
,
835 ps
->ppid
, ps
->total
);
837 /* it would be nice if we could use exec_start from /proc/pid/sched,
838 * but it's unreliable and gives bogus numbers */
839 starttime
= sampletime
[ps
->first
];
841 if (!ps_filter(ps
)) {
842 /* remember where _to_ our children need to draw a line */
843 ps
->pos_x
= time_to_graph(starttime
- graph_start
);
844 ps
->pos_y
= ps_to_graph(j
+1); /* bottom left corner */
846 /* hook children to our parent coords instead */
847 ps
->pos_x
= ps
->parent
->pos_x
;
848 ps
->pos_y
= ps
->parent
->pos_y
;
850 /* if this is the last child, we might still need to draw a connecting line */
851 if ((!ps
->next
) && (ps
->parent
))
852 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
854 ps_to_graph(j
-1) + 10.0, /* whee, use the last value here */
860 svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
861 time_to_graph(starttime
- graph_start
),
863 time_to_graph(sampletime
[ps
->last
] - starttime
),
866 /* paint cpu load over these */
867 for (t
= ps
->first
+ 1; t
< ps
->last
; t
++) {
871 /* calculate over interval */
872 rt
= ps
->sample
[t
].runtime
- ps
->sample
[t
-1].runtime
;
873 wt
= ps
->sample
[t
].waittime
- ps
->sample
[t
-1].waittime
;
875 prt
= (rt
/ 1000000000) / (sampletime
[t
] - sampletime
[t
-1]);
876 wrt
= (wt
/ 1000000000) / (sampletime
[t
] - sampletime
[t
-1]);
878 /* this can happen if timekeeping isn't accurate enough */
884 if ((prt
< 0.1) && (wrt
< 0.1)) /* =~ 26 (color threshold) */
887 svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
888 time_to_graph(sampletime
[t
- 1] - graph_start
),
890 time_to_graph(sampletime
[t
] - sampletime
[t
- 1]),
893 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
894 svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
895 time_to_graph(sampletime
[t
- 1] - graph_start
),
896 ps_to_graph(j
+ (1.0 - prt
)),
897 time_to_graph(sampletime
[t
] - sampletime
[t
- 1]),
901 /* determine where to display the process name */
902 if (sampletime
[ps
->last
] - sampletime
[ps
->first
] < 1.5)
903 /* too small to fit label inside the box */
908 /* text label of process name */
909 svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
910 time_to_graph(sampletime
[w
] - graph_start
) + 5.0,
911 ps_to_graph(j
) + 14.0,
914 (ps
->sample
[ps
->last
].runtime
- ps
->sample
[ps
->first
].runtime
) / 1000000000.0);
915 /* paint lines to the parent process */
917 /* horizontal part */
918 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
919 time_to_graph(starttime
- graph_start
),
920 ps_to_graph(j
) + 10.0,
922 ps_to_graph(j
) + 10.0);
924 /* one vertical line connecting all the horizontal ones up */
926 svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
928 ps_to_graph(j
) + 10.0,
933 j
++; /* count boxes */
938 /* last pass - determine when idle */
940 /* make sure we start counting from the point where we actually have
941 * data: assume that bootchart's first sample is when data started
944 while (ps
->next_ps
) {
950 for (i
= ps
->first
; i
< samples
- (arg_hz
/ 2); i
++) {
955 /* subtract bootchart cpu utilization from total */
957 for (c
= 0; c
< cpus
; c
++)
958 crt
+= cpustat
[c
].sample
[i
+ ((int)arg_hz
/ 2)].runtime
- cpustat
[c
].sample
[i
].runtime
;
959 brt
= ps
->sample
[i
+ ((int)arg_hz
/ 2)].runtime
- ps
->sample
[i
].runtime
;
962 * our definition of "idle":
964 * if for (hz / 2) we've used less CPU than (interval / 2) ...
965 * defaults to 4.0%, which experimentally, is where atom idles
967 if ((crt
- brt
) < (interval
/ 2.0)) {
968 idletime
= sampletime
[i
] - graph_start
;
969 svg("\n<!-- idle detected at %.03f seconds -->\n",
971 svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
972 time_to_graph(idletime
),
974 time_to_graph(idletime
),
975 ps_to_graph(pcount
) + arg_scale_y
);
976 svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
977 time_to_graph(idletime
) + 5.0,
978 ps_to_graph(pcount
) + arg_scale_y
,
985 static void svg_top_ten_cpu(void) {
986 struct ps_struct
*top
[10];
987 struct ps_struct emptyps
;
988 struct ps_struct
*ps
;
991 memset(&emptyps
, 0, sizeof(struct ps_struct
));
992 for (n
=0; n
< 10; n
++)
995 /* walk all ps's and setup ptrs */
997 while ((ps
= get_next_ps(ps
))) {
998 for (n
= 0; n
< 10; n
++) {
999 if (ps
->total
<= top
[n
]->total
)
1001 /* cascade insert */
1002 for (m
= 9; m
> n
; m
--)
1009 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
1010 for (n
= 0; n
< 10; n
++)
1011 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
1018 static void svg_top_ten_pss(void) {
1019 struct ps_struct
*top
[10];
1020 struct ps_struct emptyps
;
1021 struct ps_struct
*ps
;
1024 memset(&emptyps
, 0, sizeof(struct ps_struct
));
1025 for (n
=0; n
< 10; n
++)
1028 /* walk all ps's and setup ptrs */
1030 while ((ps
= get_next_ps(ps
))) {
1031 for (n
= 0; n
< 10; n
++) {
1032 if (ps
->pss_max
<= top
[n
]->pss_max
)
1034 /* cascade insert */
1035 for (m
= 9; m
> n
; m
--)
1042 svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
1043 for (n
= 0; n
< 10; n
++)
1044 svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
1051 void svg_do(const char *build
) {
1052 struct ps_struct
*ps
;
1054 memset(&str
, 0, sizeof(str
));
1058 /* count initcall thread count first */
1060 ksize
= (kcount
? ps_to_graph(kcount
) + (arg_scale_y
* 2) : 0);
1062 /* then count processes */
1063 while ((ps
= get_next_ps(ps
))) {
1069 psize
= ps_to_graph(pcount
) + (arg_scale_y
* 2);
1071 esize
= (arg_entropy
? arg_scale_y
* 7 : 0);
1073 /* after this, we can draw the header with proper sizing */
1076 svg("<g transform=\"translate(10,400)\">\n");
1080 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 7.0));
1084 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 14.0));
1088 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 21.0));
1093 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 28.0));
1098 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 28.0) + ksize
);
1102 svg("<g transform=\"translate(10, 0)\">\n");
1106 svg("<g transform=\"translate(10,200)\">\n");
1111 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 28.0) + ksize
+ psize
);
1117 svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y
* 28.0) + ksize
+ psize
+ esize
);
1121 svg("<g transform=\"translate(410,200)\">\n");