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