]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bootchart/svg.c
util-lib: split out allocation calls into alloc-util.[ch]
[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 5
3c527fd1 6 Copyright (C) 2009-2013 Intel Corporation
cd3bccaa
AK
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>
83fdc450
AK
26#include <string.h>
27#include <time.h>
28#include <limits.h>
29#include <unistd.h>
30#include <sys/utsname.h>
b823b5e2 31#include <fcntl.h>
83fdc450 32
b5efdb8a 33#include "alloc-util.h"
f2d28e80 34#include "architecture.h"
e985665d 35#include "util.h"
f9178132 36#include "fileio.h"
e985665d 37#include "macro.h"
6d031c0b
LP
38#include "store.h"
39#include "svg.h"
40#include "bootchart.h"
8dfb6e71 41#include "list.h"
3f18c60b 42#include "utf8.h"
3ffd4af2 43#include "fd-util.h"
83fdc450 44
6d031c0b
LP
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)
83fdc450
AK
48#define to_color(n) (192.0 - ((n) * 192.0))
49
6d031c0b 50static const char * const colorwheel[12] = {
28989b63
TA
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
83fdc450
AK
63};
64
65static double idletime = -1.0;
66static int pfiltered = 0;
67static int pcount = 0;
68static int kcount = 0;
4987623d
DM
69static double psize = 0;
70static double ksize = 0;
71static double esize = 0;
8dfb6e71
NC
72static struct list_sample_data *sampledata;
73static struct list_sample_data *prev_sampledata;
83fdc450 74
75034e58 75static void svg_header(FILE *of, struct list_sample_data *head, double graph_start, int n_cpus) {
4987623d
DM
76 double w;
77 double h;
8dfb6e71
NC
78 struct list_sample_data *sampledata_last;
79
8271bd16
TA
80 assert(head);
81
8dfb6e71
NC
82 sampledata_last = head;
83 LIST_FOREACH_BEFORE(link, sampledata, head) {
84 sampledata_last = sampledata;
85 }
28989b63
TA
86
87 /* min width is about 1600px due to the label */
8dfb6e71 88 w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start);
28989b63
TA
89 w = ((w < 1600.0) ? 1600.0 : w);
90
91 /* height is variable based on pss, psize, ksize */
6d031c0b
LP
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 */
75034e58 94 + psize + ksize + esize + (n_cpus * 15 * arg_scale_y);
28989b63 95
1f2ecb03
DM
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");
28989b63 99
1f2ecb03
DM
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");
28989b63
TA
103
104 /* write some basic info as a comment, including some help */
1f2ecb03
DM
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);
28989b63
TA
117
118 /* style sheet */
1f2ecb03
DM
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");
83fdc450
AK
146}
147
1f2ecb03 148static int svg_title(FILE *of, const char *build, int pscount, double log_start, int overrun) {
f9178132
DM
149 _cleanup_free_ char *cmdline = NULL;
150 _cleanup_free_ char *model = NULL;
151 _cleanup_free_ char *buf = NULL;
28989b63 152 char date[256] = "Unknown";
f2d28e80 153 const char *cpu;
28989b63 154 char *c;
28989b63 155 time_t t;
f9178132 156 int r;
28989b63
TA
157 struct utsname uts;
158
f9178132
DM
159 r = read_one_line_file("/proc/cmdline", &cmdline);
160 if (r < 0) {
5d236c1f 161 log_error_errno(r, "Unable to read cmdline: %m");
f9178132 162 return r;
28989b63
TA
163 }
164
165 /* extract root fs so we can find disk model name in sysfs */
8846199d 166 /* FIXME: this works only in the simple case */
28989b63
TA
167 c = strstr(cmdline, "root=/dev/");
168 if (c) {
f9178132
DM
169 char rootbdev[4];
170 char filename[32];
171
172 strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
28989b63 173 rootbdev[3] = '\0';
f9178132
DM
174 snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
175
176 r = read_one_line_file(filename, &model);
1f2ecb03 177 if (r < 0)
4e535616 178 log_info("Error reading disk model for %s: %m\n", rootbdev);
28989b63
TA
179 }
180
181 /* various utsname parameters */
f9178132
DM
182 r = uname(&uts);
183 if (r < 0) {
03995863 184 log_error("Error getting uname info\n");
f9178132
DM
185 return -errno;
186 }
28989b63
TA
187
188 /* date */
189 t = time(NULL);
e931d3f4
TA
190 r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
191 assert_se(r > 0);
28989b63
TA
192
193 /* CPU type */
f2d28e80 194 r = get_proc_field("/proc/cpuinfo", PROC_CPUINFO_MODEL, "\n", &buf);
af672f03 195 if (r < 0)
f2d28e80
AK
196 cpu = "Unknown";
197 else
198 cpu = buf;
f9178132 199
1f2ecb03
DM
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);
4e535616
DJL
205 if (model)
206 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n", model);
1f2ecb03
DM
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: ");
28989b63
TA
211
212 if (idletime >= 0.0)
1f2ecb03 213 fprintf(of, "%.03fs", idletime);
28989b63 214 else
1f2ecb03
DM
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);
f9178132
DM
220
221 return 0;
83fdc450
AK
222}
223
1f2ecb03 224static void svg_graph_box(FILE *of, struct list_sample_data *head, int height, double graph_start) {
28989b63
TA
225 double d = 0.0;
226 int i = 0;
8dfb6e71
NC
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;
28989b63
TA
236
237 /* outside box, fill */
1f2ecb03
DM
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));
28989b63 242
8dfb6e71 243 for (d = graph_start; d <= finalsample;
6d031c0b 244 d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
28989b63
TA
245 /* lines for each second */
246 if (i % 50 == 0)
1f2ecb03
DM
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));
28989b63 251 else if (i % 10 == 0)
1f2ecb03
DM
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));
28989b63 256 else
1f2ecb03
DM
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));
28989b63
TA
261
262 /* time label */
263 if (i % 10 == 0)
1f2ecb03
DM
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);
28989b63
TA
267
268 i++;
269 }
83fdc450
AK
270}
271
e90f9fa4
HH
272/* xml comments must not contain "--" */
273static 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}
83fdc450 286
1f2ecb03
DM
287static void svg_pss_graph(FILE *of,
288 struct list_sample_data *head,
289 struct ps_struct *ps_first,
290 double graph_start) {
28989b63
TA
291 struct ps_struct *ps;
292 int i;
8dfb6e71
NC
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
28989b63 300
1f2ecb03 301 fprintf(of, "\n\n<!-- Pss memory size graph -->\n");
28989b63 302
1f2ecb03 303 fprintf(of, "\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
28989b63
TA
304
305 /* vsize 1000 == 1000mb */
1f2ecb03 306 svg_graph_box(of, head, 100, graph_start);
28989b63
TA
307 /* draw some hlines for usable memory sizes */
308 for (i = 100000; i < 1000000; i += 100000) {
1f2ecb03 309 fprintf(of, " <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
28989b63
TA
310 time_to_graph(.0),
311 kb_to_graph(i),
8dfb6e71 312 time_to_graph(sampledata_last->sampletime - graph_start),
28989b63 313 kb_to_graph(i));
1f2ecb03
DM
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);
28989b63 317 }
1f2ecb03 318 fprintf(of, "\n");
28989b63
TA
319
320 /* now plot the graph itself */
8dfb6e71
NC
321 i = 1;
322 prev_sampledata = head;
323 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63
TA
324 int bottom;
325 int top;
8dfb6e71 326 struct ps_sched_struct *cross_place;
28989b63
TA
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;
8dfb6e71
NC
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
1f2ecb03 357 fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
af672f03
DM
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));
28989b63
TA
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;
8dfb6e71
NC
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 }
28989b63 377 /* don't draw anything smaller than 2mb */
af672f03
DM
378 if (ps->sample->sampledata != sampledata)
379 continue;
380 if (ps->sample->pss > (100 * arg_scale_y)) {
8dfb6e71 381 top = bottom + ps->sample->pss;
1f2ecb03 382 fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
af672f03
DM
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));
8dfb6e71 388 bottom = top;
8dfb6e71 389 }
af672f03 390 break;
8dfb6e71 391 }
af672f03 392
8dfb6e71
NC
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;
1f2ecb03 398 fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
af672f03
DM
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));
28989b63
TA
404 bottom = top;
405 }
406 }
af672f03 407
8dfb6e71
NC
408 prev_sampledata = sampledata;
409 i++;
28989b63
TA
410 }
411
412 /* overlay all the text labels */
8dfb6e71
NC
413 i = 1;
414 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63 415 int bottom;
872c8faa 416 int top = 0;
8dfb6e71
NC
417 struct ps_sched_struct *prev_sample;
418 struct ps_sched_struct *cross_place;
28989b63 419
28989b63 420 /* put all the small pss blocks into the bottom */
8dfb6e71 421 ps = ps_first->next_ps;
28989b63
TA
422 while (ps->next_ps) {
423 ps = ps->next_ps;
424 if (!ps)
425 continue;
af672f03 426
8dfb6e71
NC
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 }
af672f03 433
8dfb6e71
NC
434 if (ps->sample->sampledata == sampledata) {
435 if (ps->sample->pss <= (100 * arg_scale_y))
436 top += ps->sample->pss;
af672f03 437
8dfb6e71
NC
438 break;
439 }
440 }
af672f03 441
8dfb6e71
NC
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 }
28989b63
TA
448 bottom = top;
449
450 /* now plot the ones that are of significant size */
451 ps = ps_first;
452 while (ps->next_ps) {
8dfb6e71 453 prev_sample = ps->sample;
28989b63
TA
454 ps = ps->next_ps;
455 if (!ps)
456 continue;
8dfb6e71
NC
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 }
28989b63 464 /* don't draw anything smaller than 2mb */
8dfb6e71
NC
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)))
1f2ecb03 470 fprintf(of, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
af672f03
DM
471 time_to_graph(sampledata->sampletime - graph_start),
472 kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
473 ps->name, ps->pid);
8dfb6e71
NC
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;
28989b63 485 /* draw a label with the process / PID */
8dfb6e71 486 if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y)))
1f2ecb03
DM
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);
28989b63
TA
491 bottom = top;
492 }
493 }
af672f03 494
8dfb6e71 495 i++;
28989b63
TA
496 }
497
498 /* debug output - full data dump */
1f2ecb03 499 fprintf(of, "\n\n<!-- PSS map - csv format -->\n");
28989b63
TA
500 ps = ps_first;
501 while (ps->next_ps) {
7fd1b19b 502 _cleanup_free_ char *enc_name = NULL;
28989b63
TA
503 ps = ps->next_ps;
504 if (!ps)
505 continue;
e90f9fa4
HH
506
507 enc_name = xml_comment_encode(ps->name);
f168c273 508 if (!enc_name)
e90f9fa4
HH
509 continue;
510
1f2ecb03 511 fprintf(of, "<!-- %s [%d] pss=", enc_name, ps->pid);
e90f9fa4 512
8dfb6e71
NC
513 ps->sample = ps->first;
514 while (ps->sample->next) {
515 ps->sample = ps->sample->next;
1f2ecb03 516 fprintf(of, "%d," , ps->sample->pss);
28989b63 517 }
af672f03 518
1f2ecb03 519 fprintf(of, " -->\n");
28989b63 520 }
83fdc450
AK
521
522}
523
1f2ecb03
DM
524static 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
28989b63
TA
530 double max = 0.0;
531 double range;
532 int max_here = 0;
533 int i;
8dfb6e71 534 int k;
872c8faa
ZJS
535 struct list_sample_data *start_sampledata;
536 struct list_sample_data *stop_sampledata;
28989b63 537
1f2ecb03
DM
538 fprintf(of, "<!-- IO utilization graph - In -->\n");
539 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
28989b63
TA
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 */
6d031c0b 548 range = 0.25 / (1.0 / arg_hz);
28989b63
TA
549 if (range < 2.0)
550 range = 2.0; /* no smoothing */
551
552 /* surrounding box */
1f2ecb03 553 svg_graph_box(of, head, 5, graph_start);
28989b63
TA
554
555 /* find the max IO first */
8dfb6e71
NC
556 i = 1;
557 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63
TA
558 int start;
559 int stop;
8dfb6e71 560 int diff;
28989b63
TA
561 double tot;
562
9607d947 563 start = MAX(i - ((range / 2) - 1), 0);
1f2ecb03 564 stop = MIN(i + (range / 2), n_samples - 1);
8dfb6e71
NC
565 diff = (stop - start);
566
567 start_sampledata = sampledata;
568 stop_sampledata = sampledata;
569
af672f03 570 for (k = 0; k < ((range/2) - 1) && start_sampledata->link_next; k++)
8dfb6e71 571 start_sampledata = start_sampledata->link_next;
af672f03
DM
572
573 for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++)
8dfb6e71
NC
574 stop_sampledata = stop_sampledata->link_prev;
575
af672f03 576 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff;
28989b63 577
28989b63
TA
578 if (tot > max) {
579 max = tot;
580 max_here = i;
581 }
8dfb6e71 582
af672f03 583 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) / diff;
8dfb6e71 584
28989b63
TA
585 if (tot > max)
586 max = tot;
8dfb6e71
NC
587
588 i++;
28989b63
TA
589 }
590
591 /* plot bi */
8dfb6e71
NC
592 i = 1;
593 prev_sampledata = head;
594 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63
TA
595 int start;
596 int stop;
8dfb6e71 597 int diff;
28989b63 598 double tot;
872c8faa 599 double pbi = 0;
8dfb6e71 600
9607d947 601 start = MAX(i - ((range / 2) - 1), 0);
1f2ecb03 602 stop = MIN(i + (range / 2), n_samples);
8dfb6e71
NC
603 diff = (stop - start);
604
605 start_sampledata = sampledata;
606 stop_sampledata = sampledata;
607
af672f03 608 for (k = 0; k < ((range/2)-1) && start_sampledata->link_next; k++)
8dfb6e71 609 start_sampledata = start_sampledata->link_next;
af672f03
DM
610
611 for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++)
8dfb6e71 612 stop_sampledata = stop_sampledata->link_prev;
28989b63 613
af672f03 614 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff;
8dfb6e71
NC
615
616 if (max > 0)
617 pbi = tot / max;
28989b63
TA
618
619 if (pbi > 0.001)
1f2ecb03
DM
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));
28989b63
TA
625
626 /* labels around highest value */
ece174c5 627 if (i == max_here)
1f2ecb03
DM
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));
af672f03 632
8dfb6e71
NC
633 i++;
634 prev_sampledata = sampledata;
28989b63 635 }
83fdc450
AK
636}
637
1f2ecb03
DM
638static void svg_io_bo_bar(FILE *of,
639 struct list_sample_data *head,
640 int n_samples,
641 double graph_start,
642 double interval) {
28989b63
TA
643 double max = 0.0;
644 double range;
645 int max_here = 0;
646 int i;
8dfb6e71 647 int k;
872c8faa
ZJS
648 struct list_sample_data *start_sampledata;
649 struct list_sample_data *stop_sampledata;
28989b63 650
1f2ecb03
DM
651 fprintf(of, "<!-- IO utilization graph - out -->\n");
652 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
28989b63
TA
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 */
6d031c0b 661 range = 0.25 / (1.0 / arg_hz);
28989b63
TA
662 if (range < 2.0)
663 range = 2.0; /* no smoothing */
664
665 /* surrounding box */
1f2ecb03 666 svg_graph_box(of, head, 5, graph_start);
28989b63
TA
667
668 /* find the max IO first */
8dfb6e71
NC
669 i = 0;
670 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63
TA
671 int start;
672 int stop;
8dfb6e71 673 int diff;
28989b63
TA
674 double tot;
675
9607d947 676 start = MAX(i - ((range / 2) - 1), 0);
1f2ecb03 677 stop = MIN(i + (range / 2), n_samples - 1);
8dfb6e71
NC
678 diff = (stop - start);
679
680 start_sampledata = sampledata;
681 stop_sampledata = sampledata;
682
af672f03 683 for (k = 0; k < (range/2) - 1 && start_sampledata->link_next; k++)
8dfb6e71 684 start_sampledata = start_sampledata->link_next;
af672f03
DM
685
686 for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++)
8dfb6e71 687 stop_sampledata = stop_sampledata->link_prev;
28989b63 688
af672f03 689 tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff;
28989b63
TA
690 if (tot > max)
691 max = tot;
af672f03
DM
692
693 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) / diff;
28989b63
TA
694 if (tot > max) {
695 max = tot;
696 max_here = i;
697 }
af672f03 698
8dfb6e71 699 i++;
28989b63
TA
700 }
701
702 /* plot bo */
8dfb6e71 703 prev_sampledata = head;
af672f03
DM
704 i = 1;
705
8dfb6e71 706 LIST_FOREACH_BEFORE(link, sampledata, head) {
af672f03
DM
707 int start, stop, diff;
708 double tot, pbo;
28989b63 709
8dfb6e71
NC
710 pbo = 0;
711
9607d947 712 start = MAX(i - ((range / 2) - 1), 0);
1f2ecb03 713 stop = MIN(i + (range / 2), n_samples);
8dfb6e71
NC
714 diff = (stop - start);
715
716 start_sampledata = sampledata;
717 stop_sampledata = sampledata;
718
af672f03 719 for (k = 0; k < ((range/2)-1) && start_sampledata->link_next; k++)
8dfb6e71 720 start_sampledata = start_sampledata->link_next;
af672f03
DM
721
722 for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++)
8dfb6e71 723 stop_sampledata = stop_sampledata->link_prev;
28989b63 724
8dfb6e71
NC
725 tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo)
726 / diff;
727
728 if (max > 0)
729 pbo = tot / max;
28989b63
TA
730
731 if (pbo > 0.001)
1f2ecb03
DM
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));
28989b63
TA
737
738 /* labels around highest bo value */
ece174c5 739 if (i == max_here)
1f2ecb03
DM
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));
af672f03 744
8dfb6e71
NC
745 i++;
746 prev_sampledata = sampledata;
28989b63 747 }
83fdc450
AK
748}
749
1f2ecb03 750static void svg_cpu_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
83fdc450 751
1f2ecb03 752 fprintf(of, "<!-- CPU utilization graph -->\n");
83fdc450 753
749806b3 754 if (cpu_num < 0)
1f2ecb03 755 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n");
749806b3 756 else
1f2ecb03 757 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num);
af672f03 758
28989b63 759 /* surrounding box */
1f2ecb03 760 svg_graph_box(of, head, 5, graph_start);
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
749806b3 771 if (cpu_num < 0)
1f2ecb03 772 for (c = 0; c < n_cpus; c++)
749806b3
WC
773 trt += sampledata->runtime[c] - prev_sampledata->runtime[c];
774 else
775 trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num];
83fdc450 776
28989b63 777 trt = trt / 1000000000.0;
83fdc450 778
749806b3 779 if (cpu_num < 0)
1f2ecb03 780 trt = trt / (double)n_cpus;
83fdc450 781
28989b63 782 if (trt > 0.0)
8dfb6e71 783 ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime);
83fdc450 784
28989b63
TA
785 if (ptrt > 1.0)
786 ptrt = 1.0;
83fdc450 787
af672f03 788 if (ptrt > 0.001)
1f2ecb03
DM
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));
af672f03 794
8dfb6e71 795 prev_sampledata = sampledata;
28989b63 796 }
83fdc450
AK
797}
798
1f2ecb03 799static void svg_wait_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) {
83fdc450 800
1f2ecb03 801 fprintf(of, "<!-- Wait time aggregation box -->\n");
83fdc450 802
749806b3 803 if (cpu_num < 0)
1f2ecb03 804 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n");
749806b3 805 else
1f2ecb03 806 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num);
83fdc450 807
28989b63 808 /* surrounding box */
1f2ecb03 809 svg_graph_box(of, head, 5, graph_start);
83fdc450 810
28989b63 811 /* bars for each sample, proportional to the CPU util. */
8dfb6e71
NC
812 prev_sampledata = head;
813 LIST_FOREACH_BEFORE(link, sampledata, head) {
28989b63
TA
814 int c;
815 double twt;
816 double ptwt;
83fdc450 817
28989b63 818 ptwt = twt = 0.0;
83fdc450 819
749806b3 820 if (cpu_num < 0)
1f2ecb03 821 for (c = 0; c < n_cpus; c++)
749806b3
WC
822 twt += sampledata->waittime[c] - prev_sampledata->waittime[c];
823 else
824 twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num];
83fdc450 825
28989b63 826 twt = twt / 1000000000.0;
83fdc450 827
749806b3 828 if (cpu_num < 0)
1f2ecb03 829 twt = twt / (double)n_cpus;
83fdc450 830
28989b63 831 if (twt > 0.0)
8dfb6e71 832 ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime);
83fdc450 833
28989b63
TA
834 if (ptwt > 1.0)
835 ptwt = 1.0;
83fdc450 836
af672f03 837 if (ptwt > 0.001)
1f2ecb03
DM
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));
af672f03 843
8dfb6e71 844 prev_sampledata = sampledata;
28989b63 845 }
83fdc450
AK
846}
847
1f2ecb03 848static void svg_entropy_bar(FILE *of, struct list_sample_data *head, double graph_start) {
28989b63 849
1f2ecb03 850 fprintf(of, "<!-- entropy pool graph -->\n");
28989b63 851
1f2ecb03 852 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
28989b63 853 /* surrounding box */
1f2ecb03 854 svg_graph_box(of, head, 5, graph_start);
28989b63
TA
855
856 /* bars for each sample, scale 0-4096 */
8dfb6e71
NC
857 prev_sampledata = head;
858 LIST_FOREACH_BEFORE(link, sampledata, head) {
1f2ecb03
DM
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));
8dfb6e71 864 prev_sampledata = sampledata;
28989b63 865 }
83fdc450
AK
866}
867
1f2ecb03 868static struct ps_struct *get_next_ps(struct ps_struct *ps, struct ps_struct *ps_first) {
28989b63
TA
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 */
57255510 885 for (;;) {
af672f03
DM
886 if (ps->parent && ps->parent->next)
887 return ps->parent->next;
888
28989b63
TA
889 ps = ps->parent;
890 if (!ps)
891 return ps;
892 }
893
894 return NULL;
83fdc450
AK
895}
896
a2715692 897static bool ps_filter(struct ps_struct *ps) {
6d031c0b 898 if (!arg_filter)
a2715692 899 return false;
83fdc450 900
28989b63
TA
901 /* can't draw data when there is only 1 sample (need start + stop) */
902 if (ps->first == ps->last)
a2715692 903 return true;
83fdc450 904
28989b63
TA
905 /* don't filter kthreadd */
906 if (ps->pid == 2)
a2715692 907 return false;
83fdc450 908
28989b63
TA
909 /* drop stuff that doesn't use any real CPU time */
910 if (ps->total <= 0.001)
a2715692 911 return true;
83fdc450 912
28989b63 913 return 0;
83fdc450
AK
914}
915
1f2ecb03 916static void svg_do_initcall(FILE *of, struct list_sample_data *head, int count_only, double graph_start) {
7fd1b19b 917 _cleanup_pclose_ FILE *f = NULL;
28989b63
TA
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 */
1f2ecb03 924 if (!arg_initcall || arg_relative) {
28989b63
TA
925 kcount = 0;
926 return;
927 }
928
929 if (!count_only) {
1f2ecb03
DM
930 fprintf(of, "<!-- initcall -->\n");
931 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
28989b63 932 /* surrounding box */
1f2ecb03 933 svg_graph_box(of, head, kcount, graph_start);
28989b63
TA
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
1f2ecb03
DM
979 fprintf(of, "<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
980 func, t, usecs, ret);
28989b63
TA
981
982 if (usecs < 1000)
983 continue;
984
985 /* rect */
1f2ecb03
DM
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));
28989b63
TA
991
992 /* label */
1f2ecb03
DM
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);
28989b63
TA
997
998 kcount++;
999 }
83fdc450
AK
1000}
1001
1f2ecb03
DM
1002static 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
28989b63
TA
1010 struct ps_struct *ps;
1011 int i = 0;
1012 int j = 0;
28989b63 1013 int pid;
8dfb6e71 1014 double w = 0.0;
28989b63 1015
1f2ecb03
DM
1016 fprintf(of, "<!-- Process graph -->\n");
1017 fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
28989b63
TA
1018
1019 /* surrounding box */
1f2ecb03 1020 svg_graph_box(of, head, pcount, graph_start);
28989b63
TA
1021
1022 /* pass 2 - ps boxes */
1023 ps = ps_first;
1f2ecb03 1024 while ((ps = get_next_ps(ps, ps_first))) {
3f18c60b 1025 _cleanup_free_ char *enc_name = NULL, *escaped = NULL;
8dfb6e71 1026 double endtime;
28989b63
TA
1027 double starttime;
1028 int t;
1029
3f18c60b
WC
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);
f168c273 1034 if (!enc_name)
e90f9fa4
HH
1035 continue;
1036
28989b63 1037 /* leave some trace of what we actually filtered etc. */
1f2ecb03
DM
1038 fprintf(of, "<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
1039 ps->ppid, ps->total);
28989b63 1040
8dfb6e71 1041 starttime = ps->first->sampledata->sampletime;
28989b63
TA
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 */
8dfb6e71 1047 } else if (ps->parent){
28989b63
TA
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))
1f2ecb03
DM
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);
28989b63
TA
1059 continue;
1060 }
1061
8dfb6e71 1062 endtime = ps->last->sampledata->sampletime;
1f2ecb03
DM
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));
28989b63
TA
1068
1069 /* paint cpu load over these */
8dfb6e71
NC
1070 ps->sample = ps->first;
1071 t = 1;
1072 while (ps->sample->next) {
28989b63
TA
1073 double rt, prt;
1074 double wt, wrt;
8dfb6e71
NC
1075 struct ps_sched_struct *prev;
1076
1077 prev = ps->sample;
1078 ps->sample = ps->sample->next;
28989b63
TA
1079
1080 /* calculate over interval */
8dfb6e71
NC
1081 rt = ps->sample->runtime - prev->runtime;
1082 wt = ps->sample->waittime - prev->waittime;
28989b63 1083
8dfb6e71
NC
1084 prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
1085 wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime);
28989b63
TA
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
1f2ecb03
DM
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));
28989b63
TA
1101
1102 /* draw cpu over wait - TODO figure out how/why run + wait > interval */
1f2ecb03
DM
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));
8dfb6e71 1108 t++;
28989b63
TA
1109 }
1110
1111 /* determine where to display the process name */
8dfb6e71 1112 if ((endtime - starttime) < 1.5)
28989b63 1113 /* too small to fit label inside the box */
8dfb6e71 1114 w = endtime;
28989b63 1115 else
8dfb6e71 1116 w = starttime;
28989b63
TA
1117
1118 /* text label of process name */
1f2ecb03
DM
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 : "");
28989b63
TA
1126 /* paint lines to the parent process */
1127 if (ps->parent) {
1128 /* horizontal part */
1f2ecb03
DM
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);
28989b63
TA
1134
1135 /* one vertical line connecting all the horizontal ones up */
1136 if (!ps->next)
1f2ecb03
DM
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);
28989b63
TA
1142 }
1143
1144 j++; /* count boxes */
1145
1f2ecb03 1146 fprintf(of, "\n");
28989b63
TA
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 */
8dfb6e71 1154
28989b63
TA
1155 ps = ps_first;
1156 while (ps->next_ps) {
1157 ps = ps->next_ps;
1158 if (ps->pid == pid)
1159 break;
1160 }
1161
8dfb6e71
NC
1162 /* need to know last node first */
1163 ps->sample = ps->first;
1164 i = ps->sample->next->sampledata->counter;
1165
1f2ecb03 1166 while (ps->sample->next && i<(n_samples-(arg_hz/2))) {
28989b63
TA
1167 double crt;
1168 double brt;
1169 int c;
8dfb6e71
NC
1170 int ii;
1171 struct ps_sched_struct *sample_hz;
1172
1173 ps->sample = ps->sample->next;
1174 sample_hz = ps->sample;
af672f03 1175 for (ii = 0; (ii < (int)arg_hz/2) && sample_hz->next; ii++)
8dfb6e71 1176 sample_hz = sample_hz->next;
28989b63
TA
1177
1178 /* subtract bootchart cpu utilization from total */
1179 crt = 0.0;
1f2ecb03 1180 for (c = 0; c < n_cpus; c++)
8dfb6e71 1181 crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c];
af672f03 1182
8dfb6e71 1183 brt = sample_hz->runtime - ps->sample->runtime;
28989b63
TA
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)) {
8dfb6e71 1191 idletime = ps->sample->sampledata->sampletime - graph_start;
1f2ecb03
DM
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);
28989b63
TA
1202 break;
1203 }
af672f03 1204
8dfb6e71 1205 i++;
28989b63 1206 }
83fdc450
AK
1207}
1208
1f2ecb03 1209static void svg_top_ten_cpu(FILE *of, struct ps_struct *ps_first) {
28989b63 1210 struct ps_struct *top[10];
1c633045 1211 struct ps_struct emptyps = {};
28989b63
TA
1212 struct ps_struct *ps;
1213 int n, m;
1214
1c633045 1215 for (n = 0; n < (int) ELEMENTSOF(top); n++)
28989b63
TA
1216 top[n] = &emptyps;
1217
1218 /* walk all ps's and setup ptrs */
1219 ps = ps_first;
1f2ecb03 1220 while ((ps = get_next_ps(ps, ps_first))) {
28989b63
TA
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
1f2ecb03 1232 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
28989b63 1233 for (n = 0; n < 10; n++)
1f2ecb03
DM
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);
83fdc450
AK
1239}
1240
1f2ecb03 1241static void svg_top_ten_pss(FILE *of, struct ps_struct *ps_first) {
28989b63 1242 struct ps_struct *top[10];
1c633045 1243 struct ps_struct emptyps = {};
28989b63
TA
1244 struct ps_struct *ps;
1245 int n, m;
1246
1c633045 1247 for (n = 0; n < (int) ELEMENTSOF(top); n++)
28989b63
TA
1248 top[n] = &emptyps;
1249
1250 /* walk all ps's and setup ptrs */
1251 ps = ps_first;
1f2ecb03 1252 while ((ps = get_next_ps(ps, ps_first))) {
28989b63
TA
1253 for (n = 0; n < 10; n++) {
1254 if (ps->pss_max <= top[n]->pss_max)
1255 continue;
af672f03 1256
28989b63
TA
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
1f2ecb03 1265 fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
28989b63 1266 for (n = 0; n < 10; n++)
1f2ecb03
DM
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);
83fdc450
AK
1272}
1273
1f2ecb03
DM
1274int 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
28989b63 1286 struct ps_struct *ps;
749806b3 1287 double offset = 7;
f9178132 1288 int r, c;
28989b63 1289
ae87a4a9
GM
1290 sampledata = head;
1291 LIST_FIND_TAIL(link, sampledata, head);
28989b63
TA
1292 ps = ps_first;
1293
1294 /* count initcall thread count first */
1f2ecb03 1295 svg_do_initcall(of, head, 1, graph_start);
af672f03 1296 ksize = kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0;
28989b63
TA
1297
1298 /* then count processes */
1f2ecb03 1299 while ((ps = get_next_ps(ps, ps_first))) {
28989b63
TA
1300 if (!ps_filter(ps))
1301 pcount++;
1302 else
1303 pfiltered++;
1304 }
6d031c0b 1305 psize = ps_to_graph(pcount) + (arg_scale_y * 2);
28989b63 1306
6d031c0b 1307 esize = (arg_entropy ? arg_scale_y * 7 : 0);
28989b63
TA
1308
1309 /* after this, we can draw the header with proper sizing */
75034e58 1310 svg_header(of, head, graph_start, arg_percpu ? n_cpus : 0);
1f2ecb03 1311 fprintf(of, "<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n");
28989b63 1312
1f2ecb03
DM
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");
28989b63 1316
1f2ecb03
DM
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");
28989b63 1320
1f2ecb03 1321 for (c = -1; c < (arg_percpu ? n_cpus : 0); c++) {
749806b3 1322 offset += 7;
1f2ecb03
DM
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");
28989b63 1326
749806b3 1327 offset += 7;
1f2ecb03
DM
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");
749806b3 1331 }
28989b63
TA
1332
1333 if (kcount) {
749806b3 1334 offset += 7;
1f2ecb03
DM
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");
28989b63
TA
1338 }
1339
749806b3 1340 offset += 7;
1f2ecb03
DM
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");
28989b63 1344
1f2ecb03
DM
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");
28989b63 1348
f9178132
DM
1349 if (r < 0)
1350 return r;
1351
1f2ecb03
DM
1352 fprintf(of, "<g transform=\"translate(10,200)\">\n");
1353 svg_top_ten_cpu(of, ps_first);
1354 fprintf(of, "</g>\n\n");
28989b63 1355
6d031c0b 1356 if (arg_entropy) {
1f2ecb03
DM
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");
28989b63
TA
1360 }
1361
6d031c0b 1362 if (arg_pss) {
1f2ecb03
DM
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");
28989b63 1366
1f2ecb03
DM
1367 fprintf(of, "<g transform=\"translate(410,200)\">\n");
1368 svg_top_ten_pss(of, ps_first);
1369 fprintf(of, "</g>\n\n");
28989b63
TA
1370 }
1371
1f2ecb03
DM
1372 /* fprintf footer */
1373 fprintf(of, "\n</svg>\n");
f9178132
DM
1374
1375 return 0;
83fdc450 1376}