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