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