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