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