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