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