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