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