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