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