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