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