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