]>
Commit | Line | Data |
---|---|---|
f48d55ce AV |
1 | /* |
2 | * svghelper.c - helper functions for outputting svg | |
3 | * | |
4 | * (C) Copyright 2009 Intel Corporation | |
5 | * | |
6 | * Authors: | |
7 | * Arjan van de Ven <arjan@linux.intel.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; version 2 | |
12 | * of the License. | |
13 | */ | |
14 | ||
9486aa38 | 15 | #include <inttypes.h> |
f48d55ce AV |
16 | #include <stdio.h> |
17 | #include <stdlib.h> | |
18 | #include <unistd.h> | |
19 | #include <string.h> | |
d944c4ee | 20 | #include <linux/bitmap.h> |
f48d55ce | 21 | |
c5079997 | 22 | #include "perf.h" |
f48d55ce | 23 | #include "svghelper.h" |
74cf249d | 24 | #include "util.h" |
c5079997 | 25 | #include "cpumap.h" |
f48d55ce AV |
26 | |
27 | static u64 first_time, last_time; | |
28 | static u64 turbo_frequency, max_freq; | |
29 | ||
30 | ||
31 | #define SLOT_MULT 30.0 | |
32 | #define SLOT_HEIGHT 25.0 | |
b97b59b9 | 33 | #define SLOT_HALF (SLOT_HEIGHT / 2) |
5094b655 AV |
34 | |
35 | int svg_page_width = 1000; | |
e57a2dff SF |
36 | u64 svg_highlight; |
37 | const char *svg_highlight_name; | |
f48d55ce | 38 | |
39a90a8e | 39 | #define MIN_TEXT_SIZE 0.01 |
964a0b3d | 40 | |
f48d55ce AV |
41 | static u64 total_height; |
42 | static FILE *svgfile; | |
43 | ||
44 | static double cpu2slot(int cpu) | |
45 | { | |
46 | return 2 * cpu + 1; | |
47 | } | |
48 | ||
c5079997 SF |
49 | static int *topology_map; |
50 | ||
f48d55ce AV |
51 | static double cpu2y(int cpu) |
52 | { | |
c5079997 SF |
53 | if (topology_map) |
54 | return cpu2slot(topology_map[cpu]) * SLOT_MULT; | |
55 | else | |
56 | return cpu2slot(cpu) * SLOT_MULT; | |
f48d55ce AV |
57 | } |
58 | ||
00e99a49 | 59 | static double time2pixels(u64 __time) |
f48d55ce AV |
60 | { |
61 | double X; | |
62 | ||
00e99a49 | 63 | X = 1.0 * svg_page_width * (__time - first_time) / (last_time - first_time); |
f48d55ce AV |
64 | return X; |
65 | } | |
66 | ||
611a546b AV |
67 | /* |
68 | * Round text sizes so that the svg viewer only needs a discrete | |
69 | * number of renderings of the font | |
70 | */ | |
71 | static double round_text_size(double size) | |
72 | { | |
73 | int loop = 100; | |
74 | double target = 10.0; | |
75 | ||
76 | if (size >= 10.0) | |
77 | return size; | |
78 | while (loop--) { | |
79 | if (size >= target) | |
80 | return target; | |
81 | target = target / 2.0; | |
82 | } | |
83 | return size; | |
84 | } | |
85 | ||
5094b655 | 86 | void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) |
f48d55ce | 87 | { |
5094b655 | 88 | int new_width; |
f48d55ce AV |
89 | |
90 | svgfile = fopen(filename, "w"); | |
91 | if (!svgfile) { | |
92 | fprintf(stderr, "Cannot open %s for output\n", filename); | |
93 | return; | |
94 | } | |
5094b655 AV |
95 | first_time = start; |
96 | first_time = first_time / 100000000 * 100000000; | |
97 | last_time = end; | |
98 | ||
99 | /* | |
100 | * if the recording is short, we default to a width of 1000, but | |
101 | * for longer recordings we want at least 200 units of width per second | |
102 | */ | |
103 | new_width = (last_time - first_time) / 5000000; | |
104 | ||
105 | if (new_width > svg_page_width) | |
106 | svg_page_width = new_width; | |
107 | ||
f48d55ce AV |
108 | total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; |
109 | fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); | |
cbb2e81e | 110 | fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); |
9486aa38 | 111 | fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height); |
f48d55ce AV |
112 | |
113 | fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n"); | |
114 | ||
115 | fprintf(svgfile, " rect { stroke-width: 1; }\n"); | |
116 | fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n"); | |
117 | fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | |
b97b59b9 | 118 | fprintf(svgfile, " rect.process3 { fill:rgb(180,180,180); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
f48d55ce | 119 | fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
e57a2dff | 120 | fprintf(svgfile, " rect.sample_hi{ fill:rgb(255,128, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
b97b59b9 SF |
121 | fprintf(svgfile, " rect.error { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
122 | fprintf(svgfile, " rect.net { fill:rgb( 0,128, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | |
123 | fprintf(svgfile, " rect.disk { fill:rgb( 0, 0,255); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | |
124 | fprintf(svgfile, " rect.sync { fill:rgb(128,128, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | |
125 | fprintf(svgfile, " rect.poll { fill:rgb( 0,128,128); fill-opacity:0.2; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | |
f48d55ce | 126 | fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
2e600d01 | 127 | fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
a92fe7b3 | 128 | fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
f48d55ce AV |
129 | fprintf(svgfile, " rect.cpu { fill:rgb(192,192,192); fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n"); |
130 | fprintf(svgfile, " rect.pstate { fill:rgb(128,128,128); fill-opacity:0.8; stroke-width:0; } \n"); | |
131 | fprintf(svgfile, " rect.c1 { fill:rgb(255,214,214); fill-opacity:0.5; stroke-width:0; } \n"); | |
132 | fprintf(svgfile, " rect.c2 { fill:rgb(255,172,172); fill-opacity:0.5; stroke-width:0; } \n"); | |
133 | fprintf(svgfile, " rect.c3 { fill:rgb(255,130,130); fill-opacity:0.5; stroke-width:0; } \n"); | |
134 | fprintf(svgfile, " rect.c4 { fill:rgb(255, 88, 88); fill-opacity:0.5; stroke-width:0; } \n"); | |
135 | fprintf(svgfile, " rect.c5 { fill:rgb(255, 44, 44); fill-opacity:0.5; stroke-width:0; } \n"); | |
136 | fprintf(svgfile, " rect.c6 { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; } \n"); | |
137 | fprintf(svgfile, " line.pstate { stroke:rgb(255,255, 0); stroke-opacity:0.8; stroke-width:2; } \n"); | |
138 | ||
139 | fprintf(svgfile, " ]]>\n </style>\n</defs>\n"); | |
140 | } | |
141 | ||
b97b59b9 SF |
142 | static double normalize_height(double height) |
143 | { | |
144 | if (height < 0.25) | |
145 | return 0.25; | |
146 | else if (height < 0.50) | |
147 | return 0.50; | |
148 | else if (height < 0.75) | |
149 | return 0.75; | |
150 | else | |
151 | return 0.100; | |
152 | } | |
153 | ||
154 | void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | |
155 | { | |
156 | double w = time2pixels(end) - time2pixels(start); | |
157 | height = normalize_height(height); | |
158 | ||
159 | if (!svgfile) | |
160 | return; | |
161 | ||
162 | fprintf(svgfile, "<g>\n"); | |
163 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | |
164 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | |
165 | time2pixels(start), | |
166 | w, | |
167 | Yslot * SLOT_MULT, | |
168 | SLOT_HALF * height, | |
169 | type); | |
170 | fprintf(svgfile, "</g>\n"); | |
171 | } | |
172 | ||
173 | void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | |
174 | { | |
175 | double w = time2pixels(end) - time2pixels(start); | |
176 | height = normalize_height(height); | |
177 | ||
178 | if (!svgfile) | |
179 | return; | |
180 | ||
181 | fprintf(svgfile, "<g>\n"); | |
182 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | |
183 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | |
184 | time2pixels(start), | |
185 | w, | |
186 | Yslot * SLOT_MULT + SLOT_HEIGHT - SLOT_HALF * height, | |
187 | SLOT_HALF * height, | |
188 | type); | |
189 | fprintf(svgfile, "</g>\n"); | |
190 | } | |
191 | ||
192 | void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | |
193 | { | |
194 | double w = time2pixels(end) - time2pixels(start); | |
195 | height = normalize_height(height); | |
196 | ||
197 | if (!svgfile) | |
198 | return; | |
199 | ||
200 | fprintf(svgfile, "<g>\n"); | |
201 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | |
202 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | |
203 | time2pixels(start), | |
204 | w, | |
205 | Yslot * SLOT_MULT + SLOT_HEIGHT - SLOT_HEIGHT * height, | |
206 | SLOT_HEIGHT * height, | |
207 | type); | |
208 | fprintf(svgfile, "</g>\n"); | |
209 | } | |
210 | ||
f48d55ce AV |
211 | void svg_box(int Yslot, u64 start, u64 end, const char *type) |
212 | { | |
213 | if (!svgfile) | |
214 | return; | |
215 | ||
f8dda74f | 216 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", |
f48d55ce AV |
217 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type); |
218 | } | |
219 | ||
cbb2e81e | 220 | static char *time_to_string(u64 duration); |
6f8d67fa | 221 | void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) |
cbb2e81e SF |
222 | { |
223 | if (!svgfile) | |
224 | return; | |
225 | ||
226 | fprintf(svgfile, "<g>\n"); | |
227 | fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu, | |
228 | time_to_string(end - start)); | |
6f8d67fa SF |
229 | if (backtrace) |
230 | fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace); | |
cbb2e81e SF |
231 | svg_box(Yslot, start, end, "blocked"); |
232 | fprintf(svgfile, "</g>\n"); | |
233 | } | |
234 | ||
6f8d67fa | 235 | void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) |
f48d55ce AV |
236 | { |
237 | double text_size; | |
e57a2dff SF |
238 | const char *type; |
239 | ||
f48d55ce AV |
240 | if (!svgfile) |
241 | return; | |
242 | ||
e57a2dff SF |
243 | if (svg_highlight && end - start > svg_highlight) |
244 | type = "sample_hi"; | |
245 | else | |
246 | type = "sample"; | |
cbb2e81e SF |
247 | fprintf(svgfile, "<g>\n"); |
248 | ||
249 | fprintf(svgfile, "<title>#%d running %s</title>\n", | |
250 | cpu, time_to_string(end - start)); | |
6f8d67fa SF |
251 | if (backtrace) |
252 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); | |
f8dda74f | 253 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", |
e57a2dff SF |
254 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, |
255 | type); | |
f48d55ce AV |
256 | |
257 | text_size = (time2pixels(end)-time2pixels(start)); | |
258 | if (cpu > 9) | |
259 | text_size = text_size/2; | |
260 | if (text_size > 1.25) | |
261 | text_size = 1.25; | |
611a546b AV |
262 | text_size = round_text_size(text_size); |
263 | ||
964a0b3d | 264 | if (text_size > MIN_TEXT_SIZE) |
f8dda74f | 265 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"%.8fpt\">%i</text>\n", |
f48d55ce AV |
266 | time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1); |
267 | ||
cbb2e81e | 268 | fprintf(svgfile, "</g>\n"); |
f48d55ce AV |
269 | } |
270 | ||
a92fe7b3 AV |
271 | static char *time_to_string(u64 duration) |
272 | { | |
273 | static char text[80]; | |
274 | ||
275 | text[0] = 0; | |
276 | ||
277 | if (duration < 1000) /* less than 1 usec */ | |
278 | return text; | |
279 | ||
280 | if (duration < 1000 * 1000) { /* less than 1 msec */ | |
f8dda74f | 281 | sprintf(text, "%.1f us", duration / 1000.0); |
a92fe7b3 AV |
282 | return text; |
283 | } | |
f8dda74f | 284 | sprintf(text, "%.1f ms", duration / 1000.0 / 1000); |
a92fe7b3 AV |
285 | |
286 | return text; | |
287 | } | |
288 | ||
6f8d67fa | 289 | void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) |
a92fe7b3 AV |
290 | { |
291 | char *text; | |
292 | const char *style; | |
293 | double font_size; | |
294 | ||
295 | if (!svgfile) | |
296 | return; | |
297 | ||
298 | style = "waiting"; | |
299 | ||
300 | if (end-start > 10 * 1000000) /* 10 msec */ | |
301 | style = "WAITING"; | |
302 | ||
303 | text = time_to_string(end-start); | |
304 | ||
611a546b | 305 | font_size = 1.0 * (time2pixels(end)-time2pixels(start)); |
a92fe7b3 | 306 | |
611a546b AV |
307 | if (font_size > 3) |
308 | font_size = 3; | |
a92fe7b3 | 309 | |
611a546b | 310 | font_size = round_text_size(font_size); |
a92fe7b3 | 311 | |
f8dda74f | 312 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); |
cbb2e81e | 313 | fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); |
6f8d67fa SF |
314 | if (backtrace) |
315 | fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace); | |
f8dda74f | 316 | fprintf(svgfile, "<rect x=\"0\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
611a546b | 317 | time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style); |
a92fe7b3 | 318 | if (font_size > MIN_TEXT_SIZE) |
f8dda74f | 319 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%.8fpt\"> %s</text>\n", |
611a546b AV |
320 | font_size, text); |
321 | fprintf(svgfile, "</g>\n"); | |
a92fe7b3 AV |
322 | } |
323 | ||
f48d55ce AV |
324 | static char *cpu_model(void) |
325 | { | |
326 | static char cpu_m[255]; | |
327 | char buf[256]; | |
328 | FILE *file; | |
329 | ||
330 | cpu_m[0] = 0; | |
331 | /* CPU type */ | |
332 | file = fopen("/proc/cpuinfo", "r"); | |
333 | if (file) { | |
334 | while (fgets(buf, 255, file)) { | |
335 | if (strstr(buf, "model name")) { | |
336 | strncpy(cpu_m, &buf[13], 255); | |
337 | break; | |
338 | } | |
339 | } | |
340 | fclose(file); | |
341 | } | |
39a90a8e AV |
342 | |
343 | /* CPU type */ | |
344 | file = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", "r"); | |
345 | if (file) { | |
346 | while (fgets(buf, 255, file)) { | |
347 | unsigned int freq; | |
348 | freq = strtoull(buf, NULL, 10); | |
349 | if (freq > max_freq) | |
350 | max_freq = freq; | |
351 | } | |
352 | fclose(file); | |
353 | } | |
f48d55ce AV |
354 | return cpu_m; |
355 | } | |
356 | ||
357 | void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) | |
358 | { | |
359 | char cpu_string[80]; | |
360 | if (!svgfile) | |
361 | return; | |
362 | ||
363 | max_freq = __max_freq; | |
364 | turbo_frequency = __turbo_freq; | |
365 | ||
cbb2e81e SF |
366 | fprintf(svgfile, "<g>\n"); |
367 | ||
f8dda74f | 368 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"cpu\"/>\n", |
f48d55ce AV |
369 | time2pixels(first_time), |
370 | time2pixels(last_time)-time2pixels(first_time), | |
371 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); | |
372 | ||
c5079997 | 373 | sprintf(cpu_string, "CPU %i", (int)cpu); |
f8dda74f | 374 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\">%s</text>\n", |
f48d55ce AV |
375 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); |
376 | ||
f8dda74f | 377 | fprintf(svgfile, "<text transform=\"translate(%.8f,%.8f)\" font-size=\"1.25pt\">%s</text>\n", |
f48d55ce | 378 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); |
cbb2e81e SF |
379 | |
380 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
381 | } |
382 | ||
e57a2dff | 383 | void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace) |
f48d55ce AV |
384 | { |
385 | double width; | |
e57a2dff | 386 | const char *type; |
f48d55ce AV |
387 | |
388 | if (!svgfile) | |
389 | return; | |
390 | ||
e57a2dff SF |
391 | if (svg_highlight && end - start >= svg_highlight) |
392 | type = "sample_hi"; | |
393 | else if (svg_highlight_name && strstr(name, svg_highlight_name)) | |
394 | type = "sample_hi"; | |
395 | else | |
396 | type = "sample"; | |
611a546b | 397 | |
f8dda74f | 398 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\">\n", time2pixels(start), cpu2y(cpu)); |
de996228 | 399 | fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); |
8b6dcca0 SF |
400 | if (backtrace) |
401 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); | |
f8dda74f | 402 | fprintf(svgfile, "<rect x=\"0\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
611a546b | 403 | time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type); |
f48d55ce AV |
404 | width = time2pixels(end)-time2pixels(start); |
405 | if (width > 6) | |
406 | width = 6; | |
407 | ||
611a546b AV |
408 | width = round_text_size(width); |
409 | ||
964a0b3d | 410 | if (width > MIN_TEXT_SIZE) |
f8dda74f | 411 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%.8fpt\">%s</text>\n", |
611a546b AV |
412 | width, name); |
413 | ||
414 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
415 | } |
416 | ||
417 | void svg_cstate(int cpu, u64 start, u64 end, int type) | |
418 | { | |
419 | double width; | |
420 | char style[128]; | |
421 | ||
422 | if (!svgfile) | |
423 | return; | |
424 | ||
425 | ||
cbb2e81e SF |
426 | fprintf(svgfile, "<g>\n"); |
427 | ||
f48d55ce AV |
428 | if (type > 6) |
429 | type = 6; | |
430 | sprintf(style, "c%i", type); | |
431 | ||
f8dda74f | 432 | fprintf(svgfile, "<rect class=\"%s\" x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\"/>\n", |
f48d55ce AV |
433 | style, |
434 | time2pixels(start), time2pixels(end)-time2pixels(start), | |
435 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); | |
436 | ||
611a546b | 437 | width = (time2pixels(end)-time2pixels(start))/2.0; |
f48d55ce AV |
438 | if (width > 6) |
439 | width = 6; | |
440 | ||
611a546b AV |
441 | width = round_text_size(width); |
442 | ||
964a0b3d | 443 | if (width > MIN_TEXT_SIZE) |
f8dda74f | 444 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"%.8fpt\">C%i</text>\n", |
611a546b | 445 | time2pixels(start), cpu2y(cpu)+width, width, type); |
cbb2e81e SF |
446 | |
447 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
448 | } |
449 | ||
450 | static char *HzToHuman(unsigned long hz) | |
451 | { | |
452 | static char buffer[1024]; | |
453 | unsigned long long Hz; | |
454 | ||
455 | memset(buffer, 0, 1024); | |
456 | ||
457 | Hz = hz; | |
458 | ||
459 | /* default: just put the Number in */ | |
460 | sprintf(buffer, "%9lli", Hz); | |
461 | ||
462 | if (Hz > 1000) | |
463 | sprintf(buffer, " %6lli Mhz", (Hz+500)/1000); | |
464 | ||
465 | if (Hz > 1500000) | |
466 | sprintf(buffer, " %6.2f Ghz", (Hz+5000.0)/1000000); | |
467 | ||
468 | if (Hz == turbo_frequency) | |
469 | sprintf(buffer, "Turbo"); | |
470 | ||
471 | return buffer; | |
472 | } | |
473 | ||
474 | void svg_pstate(int cpu, u64 start, u64 end, u64 freq) | |
475 | { | |
476 | double height = 0; | |
477 | ||
478 | if (!svgfile) | |
479 | return; | |
480 | ||
cbb2e81e SF |
481 | fprintf(svgfile, "<g>\n"); |
482 | ||
f48d55ce AV |
483 | if (max_freq) |
484 | height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT); | |
485 | height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; | |
f8dda74f | 486 | fprintf(svgfile, "<line x1=\"%.8f\" x2=\"%.8f\" y1=\"%.1f\" y2=\"%.1f\" class=\"pstate\"/>\n", |
f48d55ce | 487 | time2pixels(start), time2pixels(end), height, height); |
f8dda74f | 488 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"0.25pt\">%s</text>\n", |
f48d55ce AV |
489 | time2pixels(start), height+0.9, HzToHuman(freq)); |
490 | ||
cbb2e81e | 491 | fprintf(svgfile, "</g>\n"); |
f48d55ce AV |
492 | } |
493 | ||
494 | ||
6f8d67fa | 495 | void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace) |
f48d55ce AV |
496 | { |
497 | double height; | |
498 | ||
499 | if (!svgfile) | |
500 | return; | |
501 | ||
502 | ||
cbb2e81e SF |
503 | fprintf(svgfile, "<g>\n"); |
504 | ||
505 | fprintf(svgfile, "<title>%s wakes up %s</title>\n", | |
506 | desc1 ? desc1 : "?", | |
507 | desc2 ? desc2 : "?"); | |
508 | ||
6f8d67fa SF |
509 | if (backtrace) |
510 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); | |
511 | ||
f48d55ce | 512 | if (row1 < row2) { |
4f1202c8 | 513 | if (row1) { |
f8dda74f | 514 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce | 515 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); |
4f1202c8 | 516 | if (desc2) |
f8dda74f | 517 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", |
4f1202c8 AV |
518 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_HEIGHT/48, desc2); |
519 | } | |
520 | if (row2) { | |
f8dda74f | 521 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce | 522 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT); |
4f1202c8 | 523 | if (desc1) |
f8dda74f | 524 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", |
4f1202c8 AV |
525 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, desc1); |
526 | } | |
f48d55ce | 527 | } else { |
4f1202c8 | 528 | if (row2) { |
f8dda74f | 529 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce | 530 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); |
4f1202c8 | 531 | if (desc1) |
f8dda74f | 532 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", |
4f1202c8 AV |
533 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/48, desc1); |
534 | } | |
535 | if (row1) { | |
f8dda74f | 536 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce | 537 | time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT); |
4f1202c8 | 538 | if (desc2) |
f8dda74f | 539 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", |
4f1202c8 AV |
540 | time2pixels(start), row1 * SLOT_MULT - SLOT_HEIGHT/32, desc2); |
541 | } | |
f48d55ce AV |
542 | } |
543 | height = row1 * SLOT_MULT; | |
544 | if (row2 > row1) | |
545 | height += SLOT_HEIGHT; | |
546 | if (row1) | |
f8dda74f | 547 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", |
f48d55ce | 548 | time2pixels(start), height); |
cbb2e81e SF |
549 | |
550 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
551 | } |
552 | ||
6f8d67fa | 553 | void svg_wakeline(u64 start, int row1, int row2, const char *backtrace) |
f48d55ce AV |
554 | { |
555 | double height; | |
556 | ||
557 | if (!svgfile) | |
558 | return; | |
559 | ||
560 | ||
cbb2e81e SF |
561 | fprintf(svgfile, "<g>\n"); |
562 | ||
6f8d67fa SF |
563 | if (backtrace) |
564 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); | |
565 | ||
f48d55ce | 566 | if (row1 < row2) |
f8dda74f | 567 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce AV |
568 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT); |
569 | else | |
f8dda74f | 570 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
f48d55ce AV |
571 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT); |
572 | ||
573 | height = row1 * SLOT_MULT; | |
574 | if (row2 > row1) | |
575 | height += SLOT_HEIGHT; | |
f8dda74f | 576 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", |
f48d55ce | 577 | time2pixels(start), height); |
cbb2e81e SF |
578 | |
579 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
580 | } |
581 | ||
6f8d67fa | 582 | void svg_interrupt(u64 start, int row, const char *backtrace) |
f48d55ce AV |
583 | { |
584 | if (!svgfile) | |
585 | return; | |
586 | ||
cbb2e81e SF |
587 | fprintf(svgfile, "<g>\n"); |
588 | ||
589 | fprintf(svgfile, "<title>Wakeup from interrupt</title>\n"); | |
590 | ||
6f8d67fa SF |
591 | if (backtrace) |
592 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); | |
593 | ||
f8dda74f | 594 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", |
f48d55ce | 595 | time2pixels(start), row * SLOT_MULT); |
f8dda74f | 596 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", |
f48d55ce | 597 | time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); |
cbb2e81e SF |
598 | |
599 | fprintf(svgfile, "</g>\n"); | |
f48d55ce AV |
600 | } |
601 | ||
602 | void svg_text(int Yslot, u64 start, const char *text) | |
603 | { | |
604 | if (!svgfile) | |
605 | return; | |
606 | ||
f8dda74f | 607 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\">%s</text>\n", |
f48d55ce AV |
608 | time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text); |
609 | } | |
610 | ||
611 | static void svg_legenda_box(int X, const char *text, const char *style) | |
612 | { | |
613 | double boxsize; | |
614 | boxsize = SLOT_HEIGHT / 2; | |
615 | ||
f8dda74f | 616 | fprintf(svgfile, "<rect x=\"%i\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
f48d55ce | 617 | X, boxsize, boxsize, style); |
f8dda74f | 618 | fprintf(svgfile, "<text transform=\"translate(%.8f, %.8f)\" font-size=\"%.8fpt\">%s</text>\n", |
f48d55ce AV |
619 | X + boxsize + 5, boxsize, 0.8 * boxsize, text); |
620 | } | |
621 | ||
b97b59b9 SF |
622 | void svg_io_legenda(void) |
623 | { | |
624 | if (!svgfile) | |
625 | return; | |
626 | ||
627 | fprintf(svgfile, "<g>\n"); | |
628 | svg_legenda_box(0, "Disk", "disk"); | |
629 | svg_legenda_box(100, "Network", "net"); | |
630 | svg_legenda_box(200, "Sync", "sync"); | |
631 | svg_legenda_box(300, "Poll", "poll"); | |
632 | svg_legenda_box(400, "Error", "error"); | |
633 | fprintf(svgfile, "</g>\n"); | |
634 | } | |
635 | ||
f48d55ce AV |
636 | void svg_legenda(void) |
637 | { | |
638 | if (!svgfile) | |
639 | return; | |
640 | ||
cbb2e81e | 641 | fprintf(svgfile, "<g>\n"); |
f48d55ce | 642 | svg_legenda_box(0, "Running", "sample"); |
e8530720 TR |
643 | svg_legenda_box(100, "Idle","c1"); |
644 | svg_legenda_box(200, "Deeper Idle", "c3"); | |
645 | svg_legenda_box(350, "Deepest Idle", "c6"); | |
f48d55ce AV |
646 | svg_legenda_box(550, "Sleeping", "process2"); |
647 | svg_legenda_box(650, "Waiting for cpu", "waiting"); | |
648 | svg_legenda_box(800, "Blocked on IO", "blocked"); | |
cbb2e81e | 649 | fprintf(svgfile, "</g>\n"); |
f48d55ce AV |
650 | } |
651 | ||
b97b59b9 | 652 | void svg_time_grid(double min_thickness) |
f48d55ce AV |
653 | { |
654 | u64 i; | |
655 | ||
f48d55ce AV |
656 | if (!svgfile) |
657 | return; | |
658 | ||
659 | i = first_time; | |
660 | while (i < last_time) { | |
661 | int color = 220; | |
662 | double thickness = 0.075; | |
663 | if ((i % 100000000) == 0) { | |
664 | thickness = 0.5; | |
665 | color = 192; | |
666 | } | |
667 | if ((i % 1000000000) == 0) { | |
668 | thickness = 2.0; | |
669 | color = 128; | |
670 | } | |
671 | ||
b97b59b9 SF |
672 | if (thickness >= min_thickness) |
673 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%" PRIu64 "\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%.3f\"/>\n", | |
674 | time2pixels(i), SLOT_MULT/2, time2pixels(i), | |
675 | total_height, color, color, color, thickness); | |
f48d55ce AV |
676 | |
677 | i += 10000000; | |
678 | } | |
679 | } | |
680 | ||
681 | void svg_close(void) | |
682 | { | |
683 | if (svgfile) { | |
684 | fprintf(svgfile, "</svg>\n"); | |
685 | fclose(svgfile); | |
686 | svgfile = NULL; | |
687 | } | |
688 | } | |
c5079997 SF |
689 | |
690 | #define cpumask_bits(maskp) ((maskp)->bits) | |
691 | typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; | |
692 | ||
693 | struct topology { | |
694 | cpumask_t *sib_core; | |
695 | int sib_core_nr; | |
696 | cpumask_t *sib_thr; | |
697 | int sib_thr_nr; | |
698 | }; | |
699 | ||
700 | static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) | |
701 | { | |
702 | int i; | |
703 | int thr; | |
704 | ||
705 | for (i = 0; i < t->sib_thr_nr; i++) { | |
706 | if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) | |
707 | continue; | |
708 | ||
709 | for_each_set_bit(thr, | |
710 | cpumask_bits(&t->sib_thr[i]), | |
711 | MAX_NR_CPUS) | |
712 | if (map[thr] == -1) | |
713 | map[thr] = (*pos)++; | |
714 | } | |
715 | } | |
716 | ||
717 | static void scan_core_topology(int *map, struct topology *t) | |
718 | { | |
719 | int pos = 0; | |
720 | int i; | |
721 | int cpu; | |
722 | ||
723 | for (i = 0; i < t->sib_core_nr; i++) | |
724 | for_each_set_bit(cpu, | |
725 | cpumask_bits(&t->sib_core[i]), | |
726 | MAX_NR_CPUS) | |
727 | scan_thread_topology(map, t, cpu, &pos); | |
728 | } | |
729 | ||
730 | static int str_to_bitmap(char *s, cpumask_t *b) | |
731 | { | |
732 | int i; | |
733 | int ret = 0; | |
734 | struct cpu_map *m; | |
735 | int c; | |
736 | ||
737 | m = cpu_map__new(s); | |
738 | if (!m) | |
739 | return -1; | |
740 | ||
741 | for (i = 0; i < m->nr; i++) { | |
742 | c = m->map[i]; | |
743 | if (c >= MAX_NR_CPUS) { | |
744 | ret = -1; | |
745 | break; | |
746 | } | |
747 | ||
748 | set_bit(c, cpumask_bits(b)); | |
749 | } | |
750 | ||
f30a79b0 | 751 | cpu_map__put(m); |
c5079997 SF |
752 | |
753 | return ret; | |
754 | } | |
755 | ||
756 | int svg_build_topology_map(char *sib_core, int sib_core_nr, | |
757 | char *sib_thr, int sib_thr_nr) | |
758 | { | |
759 | int i; | |
760 | struct topology t; | |
761 | ||
762 | t.sib_core_nr = sib_core_nr; | |
763 | t.sib_thr_nr = sib_thr_nr; | |
764 | t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); | |
765 | t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); | |
766 | ||
767 | if (!t.sib_core || !t.sib_thr) { | |
768 | fprintf(stderr, "topology: no memory\n"); | |
769 | goto exit; | |
770 | } | |
771 | ||
772 | for (i = 0; i < sib_core_nr; i++) { | |
773 | if (str_to_bitmap(sib_core, &t.sib_core[i])) { | |
774 | fprintf(stderr, "topology: can't parse siblings map\n"); | |
775 | goto exit; | |
776 | } | |
777 | ||
778 | sib_core += strlen(sib_core) + 1; | |
779 | } | |
780 | ||
781 | for (i = 0; i < sib_thr_nr; i++) { | |
782 | if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { | |
783 | fprintf(stderr, "topology: can't parse siblings map\n"); | |
784 | goto exit; | |
785 | } | |
786 | ||
787 | sib_thr += strlen(sib_thr) + 1; | |
788 | } | |
789 | ||
790 | topology_map = malloc(sizeof(int) * MAX_NR_CPUS); | |
791 | if (!topology_map) { | |
792 | fprintf(stderr, "topology: no memory\n"); | |
793 | goto exit; | |
794 | } | |
795 | ||
796 | for (i = 0; i < MAX_NR_CPUS; i++) | |
797 | topology_map[i] = -1; | |
798 | ||
799 | scan_core_topology(topology_map, &t); | |
800 | ||
801 | return 0; | |
802 | ||
803 | exit: | |
74cf249d ACM |
804 | zfree(&t.sib_core); |
805 | zfree(&t.sib_thr); | |
c5079997 SF |
806 | |
807 | return -1; | |
808 | } |