1 // SPDX-License-Identifier: GPL-2.0 2 #include "../../util/util.h" 3 #include "../browser.h" 4 #include "../helpline.h" 5 #include "../ui.h" 6 #include "../util.h" 7 #include "../../util/annotate.h" 8 #include "../../util/hist.h" 9 #include "../../util/sort.h" 10 #include "../../util/symbol.h" 11 #include "../../util/evsel.h" 12 #include "../../util/config.h" 13 #include "../../util/evlist.h" 14 #include <inttypes.h> 15 #include <pthread.h> 16 #include <linux/kernel.h> 17 #include <linux/string.h> 18 #include <sys/ttydefaults.h> 19 20 struct disasm_line_samples { 21 double percent; 22 struct sym_hist_entry he; 23 }; 24 25 #define IPC_WIDTH 6 26 #define CYCLES_WIDTH 6 27 28 struct browser_line { 29 u32 idx; 30 int idx_asm; 31 int jump_sources; 32 }; 33 34 static struct annotate_browser_opt { 35 bool hide_src_code, 36 use_offset, 37 jump_arrows, 38 show_linenr, 39 show_nr_jumps, 40 show_nr_samples, 41 show_total_period; 42 } annotate_browser__opts = { 43 .use_offset = true, 44 .jump_arrows = true, 45 }; 46 47 struct arch; 48 49 struct annotate_browser { 50 struct ui_browser b; 51 struct rb_root entries; 52 struct rb_node *curr_hot; 53 struct annotation_line *selection; 54 struct annotation_line **offsets; 55 struct arch *arch; 56 int nr_events; 57 u64 start; 58 int nr_asm_entries; 59 int nr_entries; 60 int max_jump_sources; 61 int nr_jumps; 62 bool searching_backwards; 63 bool have_cycles; 64 u8 addr_width; 65 u8 jumps_width; 66 u8 target_width; 67 u8 min_addr_width; 68 u8 max_addr_width; 69 char search_bf[128]; 70 }; 71 72 static inline struct browser_line *browser_line(struct annotation_line *al) 73 { 74 void *ptr = al; 75 76 ptr = container_of(al, struct disasm_line, al); 77 return ptr - sizeof(struct browser_line); 78 } 79 80 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, 81 void *entry) 82 { 83 if (annotate_browser__opts.hide_src_code) { 84 struct annotation_line *al = list_entry(entry, struct annotation_line, node); 85 86 return al->offset == -1; 87 } 88 89 return false; 90 } 91 92 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, 93 int nr, bool current) 94 { 95 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) 96 return HE_COLORSET_SELECTED; 97 if (nr == browser->max_jump_sources) 98 return HE_COLORSET_TOP; 99 if (nr > 1) 100 return HE_COLORSET_MEDIUM; 101 return HE_COLORSET_NORMAL; 102 } 103 104 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, 105 int nr, bool current) 106 { 107 int color = annotate_browser__jumps_percent_color(browser, nr, current); 108 return ui_browser__set_color(&browser->b, color); 109 } 110 111 static int annotate_browser__pcnt_width(struct annotate_browser *ab) 112 { 113 return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events; 114 } 115 116 static int annotate_browser__cycles_width(struct annotate_browser *ab) 117 { 118 return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0; 119 } 120 121 static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser, 122 char *bf, size_t size) 123 { 124 if (dl->ins.ops && dl->ins.ops->scnprintf) { 125 if (ins__is_jump(&dl->ins)) { 126 bool fwd = dl->ops.target.offset > dl->al.offset; 127 128 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : 129 SLSMG_UARROW_CHAR); 130 SLsmg_write_char(' '); 131 } else if (ins__is_call(&dl->ins)) { 132 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); 133 SLsmg_write_char(' '); 134 } else if (ins__is_ret(&dl->ins)) { 135 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); 136 SLsmg_write_char(' '); 137 } else { 138 ui_browser__write_nstring(browser, " ", 2); 139 } 140 } else { 141 ui_browser__write_nstring(browser, " ", 2); 142 } 143 144 disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset); 145 } 146 147 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) 148 { 149 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 150 struct annotation_line *al = list_entry(entry, struct annotation_line, node); 151 struct browser_line *bl = browser_line(al); 152 bool current_entry = ui_browser__is_current_entry(browser, row); 153 bool change_color = (!annotate_browser__opts.hide_src_code && 154 (!current_entry || (browser->use_navkeypressed && 155 !browser->navkeypressed))); 156 int width = browser->width, printed; 157 int i, pcnt_width = annotate_browser__pcnt_width(ab), 158 cycles_width = annotate_browser__cycles_width(ab); 159 double percent_max = 0.0; 160 char bf[256]; 161 bool show_title = false; 162 163 for (i = 0; i < ab->nr_events; i++) { 164 if (al->samples[i].percent > percent_max) 165 percent_max = al->samples[i].percent; 166 } 167 168 if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) { 169 if (ab->have_cycles) { 170 if (al->ipc == 0.0 && al->cycles == 0) 171 show_title = true; 172 } else 173 show_title = true; 174 } 175 176 if (al->offset != -1 && percent_max != 0.0) { 177 for (i = 0; i < ab->nr_events; i++) { 178 ui_browser__set_percent_color(browser, 179 al->samples[i].percent, 180 current_entry); 181 if (annotate_browser__opts.show_total_period) { 182 ui_browser__printf(browser, "%11" PRIu64 " ", 183 al->samples[i].he.period); 184 } else if (annotate_browser__opts.show_nr_samples) { 185 ui_browser__printf(browser, "%6" PRIu64 " ", 186 al->samples[i].he.nr_samples); 187 } else { 188 ui_browser__printf(browser, "%6.2f ", 189 al->samples[i].percent); 190 } 191 } 192 } else { 193 ui_browser__set_percent_color(browser, 0, current_entry); 194 195 if (!show_title) 196 ui_browser__write_nstring(browser, " ", pcnt_width); 197 else { 198 ui_browser__printf(browser, "%*s", pcnt_width, 199 annotate_browser__opts.show_total_period ? "Period" : 200 annotate_browser__opts.show_nr_samples ? "Samples" : "Percent"); 201 } 202 } 203 if (ab->have_cycles) { 204 if (al->ipc) 205 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc); 206 else if (!show_title) 207 ui_browser__write_nstring(browser, " ", IPC_WIDTH); 208 else 209 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC"); 210 211 if (al->cycles) 212 ui_browser__printf(browser, "%*" PRIu64 " ", 213 CYCLES_WIDTH - 1, al->cycles); 214 else if (!show_title) 215 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH); 216 else 217 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle"); 218 } 219 220 SLsmg_write_char(' '); 221 222 /* The scroll bar isn't being used */ 223 if (!browser->navkeypressed) 224 width += 1; 225 226 if (!*al->line) 227 ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width); 228 else if (al->offset == -1) { 229 if (al->line_nr && annotate_browser__opts.show_linenr) 230 printed = scnprintf(bf, sizeof(bf), "%-*d ", 231 ab->addr_width + 1, al->line_nr); 232 else 233 printed = scnprintf(bf, sizeof(bf), "%*s ", 234 ab->addr_width, " "); 235 ui_browser__write_nstring(browser, bf, printed); 236 ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1); 237 } else { 238 u64 addr = al->offset; 239 int color = -1; 240 241 if (!annotate_browser__opts.use_offset) 242 addr += ab->start; 243 244 if (!annotate_browser__opts.use_offset) { 245 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); 246 } else { 247 if (bl->jump_sources) { 248 if (annotate_browser__opts.show_nr_jumps) { 249 int prev; 250 printed = scnprintf(bf, sizeof(bf), "%*d ", 251 ab->jumps_width, 252 bl->jump_sources); 253 prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources, 254 current_entry); 255 ui_browser__write_nstring(browser, bf, printed); 256 ui_browser__set_color(browser, prev); 257 } 258 259 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", 260 ab->target_width, addr); 261 } else { 262 printed = scnprintf(bf, sizeof(bf), "%*s ", 263 ab->addr_width, " "); 264 } 265 } 266 267 if (change_color) 268 color = ui_browser__set_color(browser, HE_COLORSET_ADDR); 269 ui_browser__write_nstring(browser, bf, printed); 270 if (change_color) 271 ui_browser__set_color(browser, color); 272 273 disasm_line__write(disasm_line(al), browser, bf, sizeof(bf)); 274 275 ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed); 276 } 277 278 if (current_entry) 279 ab->selection = al; 280 } 281 282 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) 283 { 284 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) 285 || !disasm_line__has_offset(dl) 286 || dl->ops.target.offset < 0 287 || dl->ops.target.offset >= (s64)symbol__size(sym)) 288 return false; 289 290 return true; 291 } 292 293 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor) 294 { 295 struct disasm_line *pos = list_prev_entry(cursor, al.node); 296 const char *name; 297 298 if (!pos) 299 return false; 300 301 if (ins__is_lock(&pos->ins)) 302 name = pos->ops.locked.ins.name; 303 else 304 name = pos->ins.name; 305 306 if (!name || !cursor->ins.name) 307 return false; 308 309 return ins__is_fused(ab->arch, name, cursor->ins.name); 310 } 311 312 static void annotate_browser__draw_current_jump(struct ui_browser *browser) 313 { 314 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 315 struct disasm_line *cursor = disasm_line(ab->selection); 316 struct annotation_line *target; 317 struct browser_line *btarget, *bcursor; 318 unsigned int from, to; 319 struct map_symbol *ms = ab->b.priv; 320 struct symbol *sym = ms->sym; 321 u8 pcnt_width = annotate_browser__pcnt_width(ab); 322 int width = 0; 323 324 /* PLT symbols contain external offsets */ 325 if (strstr(sym->name, "@plt")) 326 return; 327 328 if (!disasm_line__is_valid_jump(cursor, sym)) 329 return; 330 331 /* 332 * This first was seen with a gcc function, _cpp_lex_token, that 333 * has the usual jumps: 334 * 335 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> 336 * 337 * I.e. jumps to a label inside that function (_cpp_lex_token), and 338 * those works, but also this kind: 339 * 340 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> 341 * 342 * I.e. jumps to another function, outside _cpp_lex_token, which 343 * are not being correctly handled generating as a side effect references 344 * to ab->offset[] entries that are set to NULL, so to make this code 345 * more robust, check that here. 346 * 347 * A proper fix for will be put in place, looking at the function 348 * name right after the '<' token and probably treating this like a 349 * 'call' instruction. 350 */ 351 target = ab->offsets[cursor->ops.target.offset]; 352 if (target == NULL) { 353 ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n", 354 cursor->ops.target.offset); 355 return; 356 } 357 358 bcursor = browser_line(&cursor->al); 359 btarget = browser_line(target); 360 361 if (annotate_browser__opts.hide_src_code) { 362 from = bcursor->idx_asm; 363 to = btarget->idx_asm; 364 } else { 365 from = (u64)bcursor->idx; 366 to = (u64)btarget->idx; 367 } 368 369 if (ab->have_cycles) 370 width = IPC_WIDTH + CYCLES_WIDTH; 371 372 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); 373 __ui_browser__line_arrow(browser, 374 pcnt_width + 2 + ab->addr_width + width, 375 from, to); 376 377 if (is_fused(ab, cursor)) { 378 ui_browser__mark_fused(browser, 379 pcnt_width + 3 + ab->addr_width + width, 380 from - 1, 381 to > from ? true : false); 382 } 383 } 384 385 static unsigned int annotate_browser__refresh(struct ui_browser *browser) 386 { 387 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 388 int ret = ui_browser__list_head_refresh(browser); 389 int pcnt_width = annotate_browser__pcnt_width(ab); 390 391 if (annotate_browser__opts.jump_arrows) 392 annotate_browser__draw_current_jump(browser); 393 394 ui_browser__set_color(browser, HE_COLORSET_NORMAL); 395 __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1); 396 return ret; 397 } 398 399 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b) 400 { 401 int i; 402 403 for (i = 0; i < a->samples_nr; i++) { 404 if (a->samples[i].percent == b->samples[i].percent) 405 continue; 406 return a->samples[i].percent < b->samples[i].percent; 407 } 408 return 0; 409 } 410 411 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al) 412 { 413 struct rb_node **p = &root->rb_node; 414 struct rb_node *parent = NULL; 415 struct annotation_line *l; 416 417 while (*p != NULL) { 418 parent = *p; 419 l = rb_entry(parent, struct annotation_line, rb_node); 420 421 if (disasm__cmp(al, l)) 422 p = &(*p)->rb_left; 423 else 424 p = &(*p)->rb_right; 425 } 426 rb_link_node(&al->rb_node, parent, p); 427 rb_insert_color(&al->rb_node, root); 428 } 429 430 static void annotate_browser__set_top(struct annotate_browser *browser, 431 struct annotation_line *pos, u32 idx) 432 { 433 unsigned back; 434 435 ui_browser__refresh_dimensions(&browser->b); 436 back = browser->b.height / 2; 437 browser->b.top_idx = browser->b.index = idx; 438 439 while (browser->b.top_idx != 0 && back != 0) { 440 pos = list_entry(pos->node.prev, struct annotation_line, node); 441 442 if (disasm_line__filter(&browser->b, &pos->node)) 443 continue; 444 445 --browser->b.top_idx; 446 --back; 447 } 448 449 browser->b.top = pos; 450 browser->b.navkeypressed = true; 451 } 452 453 static void annotate_browser__set_rb_top(struct annotate_browser *browser, 454 struct rb_node *nd) 455 { 456 struct browser_line *bpos; 457 struct annotation_line *pos; 458 u32 idx; 459 460 pos = rb_entry(nd, struct annotation_line, rb_node); 461 bpos = browser_line(pos); 462 463 idx = bpos->idx; 464 if (annotate_browser__opts.hide_src_code) 465 idx = bpos->idx_asm; 466 annotate_browser__set_top(browser, pos, idx); 467 browser->curr_hot = nd; 468 } 469 470 static void annotate_browser__calc_percent(struct annotate_browser *browser, 471 struct perf_evsel *evsel) 472 { 473 struct map_symbol *ms = browser->b.priv; 474 struct symbol *sym = ms->sym; 475 struct annotation *notes = symbol__annotation(sym); 476 struct disasm_line *pos; 477 478 browser->entries = RB_ROOT; 479 480 pthread_mutex_lock(¬es->lock); 481 482 symbol__calc_percent(sym, evsel); 483 484 list_for_each_entry(pos, ¬es->src->source, al.node) { 485 double max_percent = 0.0; 486 int i; 487 488 if (pos->al.offset == -1) { 489 RB_CLEAR_NODE(&pos->al.rb_node); 490 continue; 491 } 492 493 for (i = 0; i < pos->al.samples_nr; i++) { 494 struct annotation_data *sample = &pos->al.samples[i]; 495 496 if (max_percent < sample->percent) 497 max_percent = sample->percent; 498 } 499 500 if (max_percent < 0.01 && pos->al.ipc == 0) { 501 RB_CLEAR_NODE(&pos->al.rb_node); 502 continue; 503 } 504 disasm_rb_tree__insert(&browser->entries, &pos->al); 505 } 506 pthread_mutex_unlock(¬es->lock); 507 508 browser->curr_hot = rb_last(&browser->entries); 509 } 510 511 static bool annotate_browser__toggle_source(struct annotate_browser *browser) 512 { 513 struct annotation_line *al; 514 struct browser_line *bl; 515 off_t offset = browser->b.index - browser->b.top_idx; 516 517 browser->b.seek(&browser->b, offset, SEEK_CUR); 518 al = list_entry(browser->b.top, struct annotation_line, node); 519 bl = browser_line(al); 520 521 if (annotate_browser__opts.hide_src_code) { 522 if (bl->idx_asm < offset) 523 offset = bl->idx; 524 525 browser->b.nr_entries = browser->nr_entries; 526 annotate_browser__opts.hide_src_code = false; 527 browser->b.seek(&browser->b, -offset, SEEK_CUR); 528 browser->b.top_idx = bl->idx - offset; 529 browser->b.index = bl->idx; 530 } else { 531 if (bl->idx_asm < 0) { 532 ui_helpline__puts("Only available for assembly lines."); 533 browser->b.seek(&browser->b, -offset, SEEK_CUR); 534 return false; 535 } 536 537 if (bl->idx_asm < offset) 538 offset = bl->idx_asm; 539 540 browser->b.nr_entries = browser->nr_asm_entries; 541 annotate_browser__opts.hide_src_code = true; 542 browser->b.seek(&browser->b, -offset, SEEK_CUR); 543 browser->b.top_idx = bl->idx_asm - offset; 544 browser->b.index = bl->idx_asm; 545 } 546 547 return true; 548 } 549 550 static void annotate_browser__init_asm_mode(struct annotate_browser *browser) 551 { 552 ui_browser__reset_index(&browser->b); 553 browser->b.nr_entries = browser->nr_asm_entries; 554 } 555 556 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) 557 558 static int sym_title(struct symbol *sym, struct map *map, char *title, 559 size_t sz) 560 { 561 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name); 562 } 563 564 static bool annotate_browser__callq(struct annotate_browser *browser, 565 struct perf_evsel *evsel, 566 struct hist_browser_timer *hbt) 567 { 568 struct map_symbol *ms = browser->b.priv; 569 struct disasm_line *dl = disasm_line(browser->selection); 570 struct annotation *notes; 571 char title[SYM_TITLE_MAX_SIZE]; 572 573 if (!ins__is_call(&dl->ins)) 574 return false; 575 576 if (!dl->ops.target.sym) { 577 ui_helpline__puts("The called function was not found."); 578 return true; 579 } 580 581 notes = symbol__annotation(dl->ops.target.sym); 582 pthread_mutex_lock(¬es->lock); 583 584 if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) { 585 pthread_mutex_unlock(¬es->lock); 586 ui__warning("Not enough memory for annotating '%s' symbol!\n", 587 dl->ops.target.sym->name); 588 return true; 589 } 590 591 pthread_mutex_unlock(¬es->lock); 592 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt); 593 sym_title(ms->sym, ms->map, title, sizeof(title)); 594 ui_browser__show_title(&browser->b, title); 595 return true; 596 } 597 598 static 599 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 600 s64 offset, s64 *idx) 601 { 602 struct map_symbol *ms = browser->b.priv; 603 struct symbol *sym = ms->sym; 604 struct annotation *notes = symbol__annotation(sym); 605 struct disasm_line *pos; 606 607 *idx = 0; 608 list_for_each_entry(pos, ¬es->src->source, al.node) { 609 if (pos->al.offset == offset) 610 return pos; 611 if (!disasm_line__filter(&browser->b, &pos->al.node)) 612 ++*idx; 613 } 614 615 return NULL; 616 } 617 618 static bool annotate_browser__jump(struct annotate_browser *browser) 619 { 620 struct disasm_line *dl = disasm_line(browser->selection); 621 u64 offset; 622 s64 idx; 623 624 if (!ins__is_jump(&dl->ins)) 625 return false; 626 627 offset = dl->ops.target.offset; 628 dl = annotate_browser__find_offset(browser, offset, &idx); 629 if (dl == NULL) { 630 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 631 return true; 632 } 633 634 annotate_browser__set_top(browser, &dl->al, idx); 635 636 return true; 637 } 638 639 static 640 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, 641 char *s, s64 *idx) 642 { 643 struct map_symbol *ms = browser->b.priv; 644 struct symbol *sym = ms->sym; 645 struct annotation *notes = symbol__annotation(sym); 646 struct annotation_line *al = browser->selection; 647 648 *idx = browser->b.index; 649 list_for_each_entry_continue(al, ¬es->src->source, node) { 650 if (disasm_line__filter(&browser->b, &al->node)) 651 continue; 652 653 ++*idx; 654 655 if (al->line && strstr(al->line, s) != NULL) 656 return al; 657 } 658 659 return NULL; 660 } 661 662 static bool __annotate_browser__search(struct annotate_browser *browser) 663 { 664 struct annotation_line *al; 665 s64 idx; 666 667 al = annotate_browser__find_string(browser, browser->search_bf, &idx); 668 if (al == NULL) { 669 ui_helpline__puts("String not found!"); 670 return false; 671 } 672 673 annotate_browser__set_top(browser, al, idx); 674 browser->searching_backwards = false; 675 return true; 676 } 677 678 static 679 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 680 char *s, s64 *idx) 681 { 682 struct map_symbol *ms = browser->b.priv; 683 struct symbol *sym = ms->sym; 684 struct annotation *notes = symbol__annotation(sym); 685 struct annotation_line *al = browser->selection; 686 687 *idx = browser->b.index; 688 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { 689 if (disasm_line__filter(&browser->b, &al->node)) 690 continue; 691 692 --*idx; 693 694 if (al->line && strstr(al->line, s) != NULL) 695 return al; 696 } 697 698 return NULL; 699 } 700 701 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 702 { 703 struct annotation_line *al; 704 s64 idx; 705 706 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 707 if (al == NULL) { 708 ui_helpline__puts("String not found!"); 709 return false; 710 } 711 712 annotate_browser__set_top(browser, al, idx); 713 browser->searching_backwards = true; 714 return true; 715 } 716 717 static bool annotate_browser__search_window(struct annotate_browser *browser, 718 int delay_secs) 719 { 720 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 721 "ENTER: OK, ESC: Cancel", 722 delay_secs * 2) != K_ENTER || 723 !*browser->search_bf) 724 return false; 725 726 return true; 727 } 728 729 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 730 { 731 if (annotate_browser__search_window(browser, delay_secs)) 732 return __annotate_browser__search(browser); 733 734 return false; 735 } 736 737 static bool annotate_browser__continue_search(struct annotate_browser *browser, 738 int delay_secs) 739 { 740 if (!*browser->search_bf) 741 return annotate_browser__search(browser, delay_secs); 742 743 return __annotate_browser__search(browser); 744 } 745 746 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 747 int delay_secs) 748 { 749 if (annotate_browser__search_window(browser, delay_secs)) 750 return __annotate_browser__search_reverse(browser); 751 752 return false; 753 } 754 755 static 756 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 757 int delay_secs) 758 { 759 if (!*browser->search_bf) 760 return annotate_browser__search_reverse(browser, delay_secs); 761 762 return __annotate_browser__search_reverse(browser); 763 } 764 765 static void annotate_browser__update_addr_width(struct annotate_browser *browser) 766 { 767 if (annotate_browser__opts.use_offset) 768 browser->target_width = browser->min_addr_width; 769 else 770 browser->target_width = browser->max_addr_width; 771 772 browser->addr_width = browser->target_width; 773 774 if (annotate_browser__opts.show_nr_jumps) 775 browser->addr_width += browser->jumps_width + 1; 776 } 777 778 static int annotate_browser__run(struct annotate_browser *browser, 779 struct perf_evsel *evsel, 780 struct hist_browser_timer *hbt) 781 { 782 struct rb_node *nd = NULL; 783 struct map_symbol *ms = browser->b.priv; 784 struct symbol *sym = ms->sym; 785 const char *help = "Press 'h' for help on key bindings"; 786 int delay_secs = hbt ? hbt->refresh : 0; 787 int key; 788 char title[SYM_TITLE_MAX_SIZE]; 789 790 sym_title(sym, ms->map, title, sizeof(title)); 791 if (ui_browser__show(&browser->b, title, help) < 0) 792 return -1; 793 794 annotate_browser__calc_percent(browser, evsel); 795 796 if (browser->curr_hot) { 797 annotate_browser__set_rb_top(browser, browser->curr_hot); 798 browser->b.navkeypressed = false; 799 } 800 801 nd = browser->curr_hot; 802 803 while (1) { 804 key = ui_browser__run(&browser->b, delay_secs); 805 806 if (delay_secs != 0) { 807 annotate_browser__calc_percent(browser, evsel); 808 /* 809 * Current line focus got out of the list of most active 810 * lines, NULL it so that if TAB|UNTAB is pressed, we 811 * move to curr_hot (current hottest line). 812 */ 813 if (nd != NULL && RB_EMPTY_NODE(nd)) 814 nd = NULL; 815 } 816 817 switch (key) { 818 case K_TIMER: 819 if (hbt) 820 hbt->timer(hbt->arg); 821 822 if (delay_secs != 0) 823 symbol__annotate_decay_histogram(sym, evsel->idx); 824 continue; 825 case K_TAB: 826 if (nd != NULL) { 827 nd = rb_prev(nd); 828 if (nd == NULL) 829 nd = rb_last(&browser->entries); 830 } else 831 nd = browser->curr_hot; 832 break; 833 case K_UNTAB: 834 if (nd != NULL) { 835 nd = rb_next(nd); 836 if (nd == NULL) 837 nd = rb_first(&browser->entries); 838 } else 839 nd = browser->curr_hot; 840 break; 841 case K_F1: 842 case 'h': 843 ui_browser__help_window(&browser->b, 844 "UP/DOWN/PGUP\n" 845 "PGDN/SPACE Navigate\n" 846 "q/ESC/CTRL+C Exit\n\n" 847 "ENTER Go to target\n" 848 "ESC Exit\n" 849 "H Go to hottest instruction\n" 850 "TAB/shift+TAB Cycle thru hottest instructions\n" 851 "j Toggle showing jump to target arrows\n" 852 "J Toggle showing number of jump sources on targets\n" 853 "n Search next string\n" 854 "o Toggle disassembler output/simplified view\n" 855 "s Toggle source code view\n" 856 "t Circulate percent, total period, samples view\n" 857 "/ Search string\n" 858 "k Toggle line numbers\n" 859 "r Run available scripts\n" 860 "? Search string backwards\n"); 861 continue; 862 case 'r': 863 { 864 script_browse(NULL); 865 continue; 866 } 867 case 'k': 868 annotate_browser__opts.show_linenr = 869 !annotate_browser__opts.show_linenr; 870 break; 871 case 'H': 872 nd = browser->curr_hot; 873 break; 874 case 's': 875 if (annotate_browser__toggle_source(browser)) 876 ui_helpline__puts(help); 877 continue; 878 case 'o': 879 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; 880 annotate_browser__update_addr_width(browser); 881 continue; 882 case 'j': 883 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; 884 continue; 885 case 'J': 886 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; 887 annotate_browser__update_addr_width(browser); 888 continue; 889 case '/': 890 if (annotate_browser__search(browser, delay_secs)) { 891 show_help: 892 ui_helpline__puts(help); 893 } 894 continue; 895 case 'n': 896 if (browser->searching_backwards ? 897 annotate_browser__continue_search_reverse(browser, delay_secs) : 898 annotate_browser__continue_search(browser, delay_secs)) 899 goto show_help; 900 continue; 901 case '?': 902 if (annotate_browser__search_reverse(browser, delay_secs)) 903 goto show_help; 904 continue; 905 case 'D': { 906 static int seq; 907 ui_helpline__pop(); 908 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 909 seq++, browser->b.nr_entries, 910 browser->b.height, 911 browser->b.index, 912 browser->b.top_idx, 913 browser->nr_asm_entries); 914 } 915 continue; 916 case K_ENTER: 917 case K_RIGHT: 918 { 919 struct disasm_line *dl = disasm_line(browser->selection); 920 921 if (browser->selection == NULL) 922 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 923 else if (browser->selection->offset == -1) 924 ui_helpline__puts("Actions are only available for assembly lines."); 925 else if (!dl->ins.ops) 926 goto show_sup_ins; 927 else if (ins__is_ret(&dl->ins)) 928 goto out; 929 else if (!(annotate_browser__jump(browser) || 930 annotate_browser__callq(browser, evsel, hbt))) { 931 show_sup_ins: 932 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 933 } 934 continue; 935 } 936 case 't': 937 if (annotate_browser__opts.show_total_period) { 938 annotate_browser__opts.show_total_period = false; 939 annotate_browser__opts.show_nr_samples = true; 940 } else if (annotate_browser__opts.show_nr_samples) 941 annotate_browser__opts.show_nr_samples = false; 942 else 943 annotate_browser__opts.show_total_period = true; 944 annotate_browser__update_addr_width(browser); 945 continue; 946 case K_LEFT: 947 case K_ESC: 948 case 'q': 949 case CTRL('c'): 950 goto out; 951 default: 952 continue; 953 } 954 955 if (nd != NULL) 956 annotate_browser__set_rb_top(browser, nd); 957 } 958 out: 959 ui_browser__hide(&browser->b); 960 return key; 961 } 962 963 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, 964 struct hist_browser_timer *hbt) 965 { 966 /* Set default value for show_total_period and show_nr_samples */ 967 annotate_browser__opts.show_total_period = 968 symbol_conf.show_total_period; 969 annotate_browser__opts.show_nr_samples = 970 symbol_conf.show_nr_samples; 971 972 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt); 973 } 974 975 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, 976 struct hist_browser_timer *hbt) 977 { 978 /* reset abort key so that it can get Ctrl-C as a key */ 979 SLang_reset_tty(); 980 SLang_init_tty(0, 0, 0); 981 982 return map_symbol__tui_annotate(&he->ms, evsel, hbt); 983 } 984 985 986 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end) 987 { 988 unsigned n_insn = 0; 989 u64 offset; 990 991 for (offset = start; offset <= end; offset++) { 992 if (browser->offsets[offset]) 993 n_insn++; 994 } 995 return n_insn; 996 } 997 998 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end, 999 struct cyc_hist *ch) 1000 { 1001 unsigned n_insn; 1002 u64 offset; 1003 1004 n_insn = count_insn(browser, start, end); 1005 if (n_insn && ch->num && ch->cycles) { 1006 float ipc = n_insn / ((double)ch->cycles / (double)ch->num); 1007 1008 /* Hide data when there are too many overlaps. */ 1009 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2) 1010 return; 1011 1012 for (offset = start; offset <= end; offset++) { 1013 struct annotation_line *al = browser->offsets[offset]; 1014 1015 if (al) 1016 al->ipc = ipc; 1017 } 1018 } 1019 } 1020 1021 /* 1022 * This should probably be in util/annotate.c to share with the tty 1023 * annotate, but right now we need the per byte offsets arrays, 1024 * which are only here. 1025 */ 1026 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size, 1027 struct symbol *sym) 1028 { 1029 u64 offset; 1030 struct annotation *notes = symbol__annotation(sym); 1031 1032 if (!notes->src || !notes->src->cycles_hist) 1033 return; 1034 1035 pthread_mutex_lock(¬es->lock); 1036 for (offset = 0; offset < size; ++offset) { 1037 struct cyc_hist *ch; 1038 1039 ch = ¬es->src->cycles_hist[offset]; 1040 if (ch && ch->cycles) { 1041 struct annotation_line *al; 1042 1043 if (ch->have_start) 1044 count_and_fill(browser, ch->start, offset, ch); 1045 al = browser->offsets[offset]; 1046 if (al && ch->num_aggr) 1047 al->cycles = ch->cycles_aggr / ch->num_aggr; 1048 browser->have_cycles = true; 1049 } 1050 } 1051 pthread_mutex_unlock(¬es->lock); 1052 } 1053 1054 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, 1055 size_t size) 1056 { 1057 u64 offset; 1058 struct map_symbol *ms = browser->b.priv; 1059 struct symbol *sym = ms->sym; 1060 1061 /* PLT symbols contain external offsets */ 1062 if (strstr(sym->name, "@plt")) 1063 return; 1064 1065 for (offset = 0; offset < size; ++offset) { 1066 struct annotation_line *al = browser->offsets[offset]; 1067 struct disasm_line *dl; 1068 struct browser_line *blt; 1069 1070 dl = disasm_line(al); 1071 1072 if (!disasm_line__is_valid_jump(dl, sym)) 1073 continue; 1074 1075 al = browser->offsets[dl->ops.target.offset]; 1076 1077 /* 1078 * FIXME: Oops, no jump target? Buggy disassembler? Or do we 1079 * have to adjust to the previous offset? 1080 */ 1081 if (al == NULL) 1082 continue; 1083 1084 blt = browser_line(al); 1085 if (++blt->jump_sources > browser->max_jump_sources) 1086 browser->max_jump_sources = blt->jump_sources; 1087 1088 ++browser->nr_jumps; 1089 } 1090 } 1091 1092 static inline int width_jumps(int n) 1093 { 1094 if (n >= 100) 1095 return 5; 1096 if (n / 10) 1097 return 2; 1098 return 1; 1099 } 1100 1101 int symbol__tui_annotate(struct symbol *sym, struct map *map, 1102 struct perf_evsel *evsel, 1103 struct hist_browser_timer *hbt) 1104 { 1105 struct annotation_line *al; 1106 struct annotation *notes; 1107 size_t size; 1108 struct map_symbol ms = { 1109 .map = map, 1110 .sym = sym, 1111 }; 1112 struct annotate_browser browser = { 1113 .b = { 1114 .refresh = annotate_browser__refresh, 1115 .seek = ui_browser__list_head_seek, 1116 .write = annotate_browser__write, 1117 .filter = disasm_line__filter, 1118 .priv = &ms, 1119 .use_navkeypressed = true, 1120 }, 1121 }; 1122 int ret = -1, err; 1123 int nr_pcnt = 1; 1124 1125 if (sym == NULL) 1126 return -1; 1127 1128 size = symbol__size(sym); 1129 1130 if (map->dso->annotate_warned) 1131 return -1; 1132 1133 browser.offsets = zalloc(size * sizeof(struct annotation_line *)); 1134 if (browser.offsets == NULL) { 1135 ui__error("Not enough memory!"); 1136 return -1; 1137 } 1138 1139 if (perf_evsel__is_group_event(evsel)) 1140 nr_pcnt = evsel->nr_members; 1141 1142 err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch); 1143 if (err) { 1144 char msg[BUFSIZ]; 1145 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); 1146 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 1147 goto out_free_offsets; 1148 } 1149 1150 symbol__calc_percent(sym, evsel); 1151 1152 ui_helpline__push("Press ESC to exit"); 1153 1154 notes = symbol__annotation(sym); 1155 browser.start = map__rip_2objdump(map, sym->start); 1156 1157 list_for_each_entry(al, ¬es->src->source, node) { 1158 struct browser_line *bpos; 1159 size_t line_len = strlen(al->line); 1160 1161 if (browser.b.width < line_len) 1162 browser.b.width = line_len; 1163 bpos = browser_line(al); 1164 bpos->idx = browser.nr_entries++; 1165 if (al->offset != -1) { 1166 bpos->idx_asm = browser.nr_asm_entries++; 1167 /* 1168 * FIXME: short term bandaid to cope with assembly 1169 * routines that comes with labels in the same column 1170 * as the address in objdump, sigh. 1171 * 1172 * E.g. copy_user_generic_unrolled 1173 */ 1174 if (al->offset < (s64)size) 1175 browser.offsets[al->offset] = al; 1176 } else 1177 bpos->idx_asm = -1; 1178 } 1179 1180 annotate_browser__mark_jump_targets(&browser, size); 1181 annotate__compute_ipc(&browser, size, sym); 1182 1183 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); 1184 browser.max_addr_width = hex_width(sym->end); 1185 browser.jumps_width = width_jumps(browser.max_jump_sources); 1186 browser.nr_events = nr_pcnt; 1187 browser.b.nr_entries = browser.nr_entries; 1188 browser.b.entries = ¬es->src->source, 1189 browser.b.width += 18; /* Percentage */ 1190 1191 if (annotate_browser__opts.hide_src_code) 1192 annotate_browser__init_asm_mode(&browser); 1193 1194 annotate_browser__update_addr_width(&browser); 1195 1196 ret = annotate_browser__run(&browser, evsel, hbt); 1197 1198 annotated_source__purge(notes->src); 1199 1200 out_free_offsets: 1201 free(browser.offsets); 1202 return ret; 1203 } 1204 1205 #define ANNOTATE_CFG(n) \ 1206 { .name = #n, .value = &annotate_browser__opts.n, } 1207 1208 /* 1209 * Keep the entries sorted, they are bsearch'ed 1210 */ 1211 static struct annotate_config { 1212 const char *name; 1213 bool *value; 1214 } annotate__configs[] = { 1215 ANNOTATE_CFG(hide_src_code), 1216 ANNOTATE_CFG(jump_arrows), 1217 ANNOTATE_CFG(show_linenr), 1218 ANNOTATE_CFG(show_nr_jumps), 1219 ANNOTATE_CFG(show_nr_samples), 1220 ANNOTATE_CFG(show_total_period), 1221 ANNOTATE_CFG(use_offset), 1222 }; 1223 1224 #undef ANNOTATE_CFG 1225 1226 static int annotate_config__cmp(const void *name, const void *cfgp) 1227 { 1228 const struct annotate_config *cfg = cfgp; 1229 1230 return strcmp(name, cfg->name); 1231 } 1232 1233 static int annotate__config(const char *var, const char *value, 1234 void *data __maybe_unused) 1235 { 1236 struct annotate_config *cfg; 1237 const char *name; 1238 1239 if (!strstarts(var, "annotate.")) 1240 return 0; 1241 1242 name = var + 9; 1243 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), 1244 sizeof(struct annotate_config), annotate_config__cmp); 1245 1246 if (cfg == NULL) 1247 ui__warning("%s variable unknown, ignoring...", var); 1248 else 1249 *cfg->value = perf_config_bool(name, value); 1250 return 0; 1251 } 1252 1253 void annotate_browser__init(void) 1254 { 1255 perf_config(annotate__config, NULL); 1256 } 1257