1 // SPDX-License-Identifier: GPL-2.0 2 #include "../browser.h" 3 #include "../helpline.h" 4 #include "../ui.h" 5 #include "../../util/annotate.h" 6 #include "../../util/debug.h" 7 #include "../../util/debuginfo.h" 8 #include "../../util/dso.h" 9 #include "../../util/hashmap.h" 10 #include "../../util/hist.h" 11 #include "../../util/sort.h" 12 #include "../../util/map.h" 13 #include "../../util/mutex.h" 14 #include "../../util/symbol.h" 15 #include "../../util/evsel.h" 16 #include "../../util/evlist.h" 17 #include "../../util/thread.h" 18 #include <inttypes.h> 19 #include <linux/err.h> 20 #include <linux/kernel.h> 21 #include <linux/string.h> 22 #include <linux/zalloc.h> 23 #include <sys/ttydefaults.h> 24 #include <asm/bug.h> 25 26 struct arch; 27 28 struct annotate_browser { 29 struct ui_browser b; 30 struct rb_root entries; 31 struct rb_node *curr_hot; 32 struct annotation_line *selection; 33 struct arch *arch; 34 /* 35 * perf top can delete hist_entry anytime. Callers should make sure 36 * its lifetime. 37 */ 38 struct hist_entry *he; 39 struct debuginfo *dbg; 40 struct evsel *evsel; 41 struct hashmap *type_hash; 42 bool searching_backwards; 43 char search_bf[128]; 44 }; 45 46 /* A copy of target hist_entry for perf top. */ 47 static struct hist_entry annotate_he; 48 49 static size_t type_hash(long key, void *ctx __maybe_unused) 50 { 51 return key; 52 } 53 54 static bool type_equal(long key1, long key2, void *ctx __maybe_unused) 55 { 56 return key1 == key2; 57 } 58 59 static inline struct annotation *browser__annotation(struct ui_browser *browser) 60 { 61 struct map_symbol *ms = browser->priv; 62 return symbol__annotation(ms->sym); 63 } 64 65 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry) 66 { 67 struct annotation_line *al = list_entry(entry, struct annotation_line, node); 68 return annotation_line__filter(al); 69 } 70 71 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current) 72 { 73 struct annotation *notes = browser__annotation(browser); 74 75 if (current && (!browser->use_navkeypressed || browser->navkeypressed)) 76 return HE_COLORSET_SELECTED; 77 if (nr == notes->src->max_jump_sources) 78 return HE_COLORSET_TOP; 79 if (nr > 1) 80 return HE_COLORSET_MEDIUM; 81 return HE_COLORSET_NORMAL; 82 } 83 84 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current) 85 { 86 int color = ui_browser__jumps_percent_color(browser, nr, current); 87 return ui_browser__set_color(browser, color); 88 } 89 90 static int annotate_browser__set_color(void *browser, int color) 91 { 92 return ui_browser__set_color(browser, color); 93 } 94 95 static void annotate_browser__write_graph(void *browser, int graph) 96 { 97 ui_browser__write_graph(browser, graph); 98 } 99 100 static void annotate_browser__set_percent_color(void *browser, double percent, bool current) 101 { 102 ui_browser__set_percent_color(browser, percent, current); 103 } 104 105 static void annotate_browser__printf(void *browser, const char *fmt, ...) 106 { 107 va_list args; 108 109 va_start(args, fmt); 110 ui_browser__vprintf(browser, fmt, args); 111 va_end(args); 112 } 113 114 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) 115 { 116 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 117 struct annotation *notes = browser__annotation(browser); 118 struct annotation_line *al = list_entry(entry, struct annotation_line, node); 119 const bool is_current_entry = ui_browser__is_current_entry(browser, row); 120 struct annotation_write_ops ops = { 121 .first_line = row == 0, 122 .current_entry = is_current_entry, 123 .change_color = (!annotate_opts.hide_src_code && 124 (!is_current_entry || 125 (browser->use_navkeypressed && 126 !browser->navkeypressed))), 127 .width = browser->width, 128 .obj = browser, 129 .set_color = annotate_browser__set_color, 130 .set_percent_color = annotate_browser__set_percent_color, 131 .set_jumps_percent_color = ui_browser__set_jumps_percent_color, 132 .printf = annotate_browser__printf, 133 .write_graph = annotate_browser__write_graph, 134 }; 135 struct annotation_print_data apd = { 136 .he = ab->he, 137 .arch = ab->arch, 138 .evsel = ab->evsel, 139 .dbg = ab->dbg, 140 }; 141 142 /* The scroll bar isn't being used */ 143 if (!browser->navkeypressed) 144 ops.width += 1; 145 146 if (!IS_ERR_OR_NULL(ab->type_hash)) 147 apd.type_hash = ab->type_hash; 148 149 annotation_line__write(al, notes, &ops, &apd); 150 151 if (ops.current_entry) 152 ab->selection = al; 153 } 154 155 static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor) 156 { 157 struct disasm_line *pos = list_prev_entry(cursor, al.node); 158 const char *name; 159 int diff = 1; 160 161 while (pos && pos->al.offset == -1) { 162 pos = list_prev_entry(pos, al.node); 163 if (!annotate_opts.hide_src_code) 164 diff++; 165 } 166 167 if (!pos) 168 return 0; 169 170 if (ins__is_lock(&pos->ins)) 171 name = pos->ops.locked.ins.name; 172 else 173 name = pos->ins.name; 174 175 if (!name || !cursor->ins.name) 176 return 0; 177 178 if (ins__is_fused(ab->arch, name, cursor->ins.name)) 179 return diff; 180 return 0; 181 } 182 183 static void annotate_browser__draw_current_jump(struct ui_browser *browser) 184 { 185 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 186 struct disasm_line *cursor = disasm_line(ab->selection); 187 struct annotation_line *target; 188 unsigned int from, to; 189 struct map_symbol *ms = ab->b.priv; 190 struct symbol *sym = ms->sym; 191 struct annotation *notes = symbol__annotation(sym); 192 u8 pcnt_width = annotation__pcnt_width(notes); 193 u8 cntr_width = annotation__br_cntr_width(); 194 int width; 195 int diff = 0; 196 197 /* PLT symbols contain external offsets */ 198 if (strstr(sym->name, "@plt")) 199 return; 200 201 if (!disasm_line__is_valid_local_jump(cursor, sym)) 202 return; 203 204 /* 205 * This first was seen with a gcc function, _cpp_lex_token, that 206 * has the usual jumps: 207 * 208 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> 209 * 210 * I.e. jumps to a label inside that function (_cpp_lex_token), and 211 * those works, but also this kind: 212 * 213 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> 214 * 215 * I.e. jumps to another function, outside _cpp_lex_token, which 216 * are not being correctly handled generating as a side effect references 217 * to ab->offset[] entries that are set to NULL, so to make this code 218 * more robust, check that here. 219 * 220 * A proper fix for will be put in place, looking at the function 221 * name right after the '<' token and probably treating this like a 222 * 'call' instruction. 223 */ 224 target = annotated_source__get_line(notes->src, cursor->ops.target.offset); 225 if (target == NULL) { 226 ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n", 227 cursor->ops.target.offset); 228 return; 229 } 230 231 if (annotate_opts.hide_src_code) { 232 from = cursor->al.idx_asm; 233 to = target->idx_asm; 234 } else { 235 from = (u64)cursor->al.idx; 236 to = (u64)target->idx; 237 } 238 239 width = annotation__cycles_width(notes); 240 241 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); 242 __ui_browser__line_arrow(browser, 243 pcnt_width + 2 + notes->src->widths.addr + width + cntr_width, 244 from, to); 245 246 diff = is_fused(ab, cursor); 247 if (diff > 0) { 248 ui_browser__mark_fused(browser, 249 pcnt_width + 3 + notes->src->widths.addr + width + cntr_width, 250 from - diff, diff, to > from); 251 } 252 } 253 254 static unsigned int annotate_browser__refresh(struct ui_browser *browser) 255 { 256 struct annotation *notes = browser__annotation(browser); 257 int ret = ui_browser__list_head_refresh(browser); 258 int pcnt_width = annotation__pcnt_width(notes); 259 260 if (annotate_opts.jump_arrows) 261 annotate_browser__draw_current_jump(browser); 262 263 ui_browser__set_color(browser, HE_COLORSET_NORMAL); 264 __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1); 265 return ret; 266 } 267 268 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b, 269 int percent_type) 270 { 271 int i; 272 273 for (i = 0; i < a->data_nr; i++) { 274 if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type]) 275 continue; 276 return a->data[i].percent[percent_type] - 277 b->data[i].percent[percent_type]; 278 } 279 return 0; 280 } 281 282 static void disasm_rb_tree__insert(struct annotate_browser *browser, 283 struct annotation_line *al) 284 { 285 struct rb_root *root = &browser->entries; 286 struct rb_node **p = &root->rb_node; 287 struct rb_node *parent = NULL; 288 struct annotation_line *l; 289 290 while (*p != NULL) { 291 parent = *p; 292 l = rb_entry(parent, struct annotation_line, rb_node); 293 294 if (disasm__cmp(al, l, annotate_opts.percent_type) < 0) 295 p = &(*p)->rb_left; 296 else 297 p = &(*p)->rb_right; 298 } 299 rb_link_node(&al->rb_node, parent, p); 300 rb_insert_color(&al->rb_node, root); 301 } 302 303 static void annotate_browser__set_top(struct annotate_browser *browser, 304 struct annotation_line *pos, u32 idx) 305 { 306 unsigned back; 307 308 ui_browser__refresh_dimensions(&browser->b); 309 back = browser->b.height / 2; 310 browser->b.top_idx = browser->b.index = idx; 311 312 while (browser->b.top_idx != 0 && back != 0) { 313 pos = list_entry(pos->node.prev, struct annotation_line, node); 314 315 if (annotation_line__filter(pos)) 316 continue; 317 318 --browser->b.top_idx; 319 --back; 320 } 321 322 browser->b.top = pos; 323 browser->b.navkeypressed = true; 324 } 325 326 static void annotate_browser__set_rb_top(struct annotate_browser *browser, 327 struct rb_node *nd) 328 { 329 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); 330 u32 idx = pos->idx; 331 332 if (annotate_opts.hide_src_code) 333 idx = pos->idx_asm; 334 annotate_browser__set_top(browser, pos, idx); 335 browser->curr_hot = nd; 336 } 337 338 static void annotate_browser__calc_percent(struct annotate_browser *browser, 339 struct evsel *evsel) 340 { 341 struct map_symbol *ms = browser->b.priv; 342 struct symbol *sym = ms->sym; 343 struct annotation *notes = symbol__annotation(sym); 344 struct disasm_line *pos; 345 346 browser->entries = RB_ROOT; 347 348 annotation__lock(notes); 349 350 symbol__calc_percent(sym, evsel); 351 352 list_for_each_entry(pos, ¬es->src->source, al.node) { 353 double max_percent = 0.0; 354 int i; 355 356 if (pos->al.offset == -1) { 357 RB_CLEAR_NODE(&pos->al.rb_node); 358 continue; 359 } 360 361 for (i = 0; i < pos->al.data_nr; i++) { 362 double percent; 363 364 percent = annotation_data__percent(&pos->al.data[i], 365 annotate_opts.percent_type); 366 367 if (max_percent < percent) 368 max_percent = percent; 369 } 370 371 if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) { 372 RB_CLEAR_NODE(&pos->al.rb_node); 373 continue; 374 } 375 disasm_rb_tree__insert(browser, &pos->al); 376 } 377 annotation__unlock(notes); 378 379 browser->curr_hot = rb_last(&browser->entries); 380 } 381 382 static struct annotation_line *annotate_browser__find_new_asm_line( 383 struct annotate_browser *browser, 384 int idx_asm) 385 { 386 struct annotation_line *al; 387 struct list_head *head = browser->b.entries; 388 389 /* find an annotation line in the new list with the same idx_asm */ 390 list_for_each_entry(al, head, node) { 391 if (al->idx_asm == idx_asm) 392 return al; 393 } 394 395 /* There are no asm lines */ 396 return NULL; 397 } 398 399 static struct annotation_line *annotate_browser__find_next_asm_line( 400 struct annotate_browser *browser, 401 struct annotation_line *al) 402 { 403 struct annotation_line *it = al; 404 405 /* find next asm line */ 406 list_for_each_entry_continue(it, browser->b.entries, node) { 407 if (it->idx_asm >= 0) 408 return it; 409 } 410 411 /* no asm line found forwards, try backwards */ 412 it = al; 413 list_for_each_entry_continue_reverse(it, browser->b.entries, node) { 414 if (it->idx_asm >= 0) 415 return it; 416 } 417 418 /* There are no asm lines */ 419 return NULL; 420 } 421 422 static bool annotation__has_source(struct annotation *notes) 423 { 424 struct annotation_line *al; 425 bool found_asm = false; 426 427 /* Let's skip the first non-asm lines which present regardless of source. */ 428 list_for_each_entry(al, ¬es->src->source, node) { 429 if (al->offset >= 0) { 430 found_asm = true; 431 break; 432 } 433 } 434 435 if (found_asm) { 436 /* After assembly lines, any line without offset means source. */ 437 list_for_each_entry_continue(al, ¬es->src->source, node) { 438 if (al->offset == -1) 439 return true; 440 } 441 } 442 return false; 443 } 444 445 static bool annotate_browser__toggle_source(struct annotate_browser *browser, 446 struct evsel *evsel) 447 { 448 struct annotation *notes = browser__annotation(&browser->b); 449 struct annotation_line *al; 450 off_t offset = browser->b.index - browser->b.top_idx; 451 452 browser->b.seek(&browser->b, offset, SEEK_CUR); 453 al = list_entry(browser->b.top, struct annotation_line, node); 454 455 if (!annotate_opts.annotate_src) 456 annotate_opts.annotate_src = true; 457 458 /* 459 * It's about to get source code annotation for the first time. 460 * Drop the existing annotation_lines and get the new one with source. 461 * And then move to the original line at the same asm index. 462 */ 463 if (annotate_opts.hide_src_code && !notes->src->tried_source) { 464 struct map_symbol *ms = browser->b.priv; 465 int orig_idx_asm = al->idx_asm; 466 467 /* annotate again with source code info */ 468 annotate_opts.hide_src_code = false; 469 annotated_source__purge(notes->src); 470 symbol__annotate2(ms, evsel, &browser->arch); 471 annotate_opts.hide_src_code = true; 472 473 /* should be after annotated_source__purge() */ 474 notes->src->tried_source = true; 475 476 if (!annotation__has_source(notes)) 477 ui__warning("Annotation has no source code."); 478 479 browser->b.entries = ¬es->src->source; 480 al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); 481 if (unlikely(al == NULL)) { 482 al = list_first_entry(¬es->src->source, 483 struct annotation_line, node); 484 } 485 browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); 486 } 487 488 if (annotate_opts.hide_src_code) { 489 if (al->idx_asm < offset) 490 offset = al->idx; 491 492 browser->b.nr_entries = notes->src->nr_entries; 493 annotate_opts.hide_src_code = false; 494 browser->b.seek(&browser->b, -offset, SEEK_CUR); 495 browser->b.top_idx = al->idx - offset; 496 browser->b.index = al->idx; 497 } else { 498 if (al->idx_asm < 0) { 499 /* move cursor to next asm line */ 500 al = annotate_browser__find_next_asm_line(browser, al); 501 if (!al) { 502 browser->b.seek(&browser->b, -offset, SEEK_CUR); 503 return false; 504 } 505 } 506 507 if (al->idx_asm < offset) 508 offset = al->idx_asm; 509 510 browser->b.nr_entries = notes->src->nr_asm_entries; 511 annotate_opts.hide_src_code = true; 512 browser->b.seek(&browser->b, -offset, SEEK_CUR); 513 browser->b.top_idx = al->idx_asm - offset; 514 browser->b.index = al->idx_asm; 515 } 516 517 if (annotate_opts.hide_src_code_on_title) 518 annotate_opts.hide_src_code_on_title = false; 519 520 return true; 521 } 522 523 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) 524 525 static void annotate_browser__show_full_location(struct ui_browser *browser) 526 { 527 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 528 struct disasm_line *cursor = disasm_line(ab->selection); 529 struct annotation_line *al = &cursor->al; 530 531 if (al->offset != -1) 532 ui_helpline__puts("Only available for source code lines."); 533 else if (al->fileloc == NULL) 534 ui_helpline__puts("No source file location."); 535 else { 536 char help_line[SYM_TITLE_MAX_SIZE]; 537 sprintf (help_line, "Source file location: %s", al->fileloc); 538 ui_helpline__puts(help_line); 539 } 540 } 541 542 static void ui_browser__init_asm_mode(struct ui_browser *browser) 543 { 544 struct annotation *notes = browser__annotation(browser); 545 ui_browser__reset_index(browser); 546 browser->nr_entries = notes->src->nr_asm_entries; 547 } 548 549 static int sym_title(struct symbol *sym, struct map *map, char *title, 550 size_t sz, int percent_type) 551 { 552 return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name, 553 dso__long_name(map__dso(map)), 554 percent_type_str(percent_type), 555 annotate_opts.code_with_type ? "[Type]" : ""); 556 } 557 558 /* 559 * This can be called from external jumps, i.e. jumps from one function 560 * to another, like from the kernel's entry_SYSCALL_64 function to the 561 * swapgs_restore_regs_and_return_to_usermode() function. 562 * 563 * So all we check here is that dl->ops.target.sym is set, if it is, just 564 * go to that function and when exiting from its disassembly, come back 565 * to the calling function. 566 */ 567 static bool annotate_browser__callq(struct annotate_browser *browser, 568 struct evsel *evsel, 569 struct hist_browser_timer *hbt) 570 { 571 struct map_symbol *ms = browser->b.priv, target_ms; 572 struct disasm_line *dl = disasm_line(browser->selection); 573 struct annotation *notes; 574 char title[SYM_TITLE_MAX_SIZE]; 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 annotation__lock(notes); 583 584 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { 585 annotation__unlock(notes); 586 ui__warning("Not enough memory for annotating '%s' symbol!\n", 587 dl->ops.target.sym->name); 588 return true; 589 } 590 591 target_ms.maps = ms->maps; 592 target_ms.map = ms->map; 593 target_ms.sym = dl->ops.target.sym; 594 annotation__unlock(notes); 595 __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); 596 sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); 597 ui_browser__show_title(&browser->b, title); 598 return true; 599 } 600 601 static 602 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 603 s64 offset, s64 *idx) 604 { 605 struct annotation *notes = browser__annotation(&browser->b); 606 struct disasm_line *pos; 607 608 *idx = 0; 609 list_for_each_entry(pos, ¬es->src->source, al.node) { 610 if (pos->al.offset == offset) 611 return pos; 612 if (!annotation_line__filter(&pos->al)) 613 ++*idx; 614 } 615 616 return NULL; 617 } 618 619 static bool annotate_browser__jump(struct annotate_browser *browser, 620 struct evsel *evsel, 621 struct hist_browser_timer *hbt) 622 { 623 struct disasm_line *dl = disasm_line(browser->selection); 624 u64 offset; 625 s64 idx; 626 627 if (!ins__is_jump(&dl->ins)) 628 return false; 629 630 if (dl->ops.target.outside) { 631 annotate_browser__callq(browser, evsel, hbt); 632 return true; 633 } 634 635 offset = dl->ops.target.offset; 636 dl = annotate_browser__find_offset(browser, offset, &idx); 637 if (dl == NULL) { 638 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 639 return true; 640 } 641 642 annotate_browser__set_top(browser, &dl->al, idx); 643 644 return true; 645 } 646 647 static 648 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, 649 char *s, s64 *idx) 650 { 651 struct annotation *notes = browser__annotation(&browser->b); 652 struct annotation_line *al = browser->selection; 653 654 *idx = browser->b.index; 655 list_for_each_entry_continue(al, ¬es->src->source, node) { 656 if (annotation_line__filter(al)) 657 continue; 658 659 ++*idx; 660 661 if (al->line && strstr(al->line, s) != NULL) 662 return al; 663 } 664 665 return NULL; 666 } 667 668 static bool __annotate_browser__search(struct annotate_browser *browser) 669 { 670 struct annotation_line *al; 671 s64 idx; 672 673 al = annotate_browser__find_string(browser, browser->search_bf, &idx); 674 if (al == NULL) { 675 ui_helpline__puts("String not found!"); 676 return false; 677 } 678 679 annotate_browser__set_top(browser, al, idx); 680 browser->searching_backwards = false; 681 return true; 682 } 683 684 static 685 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 686 char *s, s64 *idx) 687 { 688 struct annotation *notes = browser__annotation(&browser->b); 689 struct annotation_line *al = browser->selection; 690 691 *idx = browser->b.index; 692 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { 693 if (annotation_line__filter(al)) 694 continue; 695 696 --*idx; 697 698 if (al->line && strstr(al->line, s) != NULL) 699 return al; 700 } 701 702 return NULL; 703 } 704 705 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 706 { 707 struct annotation_line *al; 708 s64 idx; 709 710 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 711 if (al == NULL) { 712 ui_helpline__puts("String not found!"); 713 return false; 714 } 715 716 annotate_browser__set_top(browser, al, idx); 717 browser->searching_backwards = true; 718 return true; 719 } 720 721 static bool annotate_browser__search_window(struct annotate_browser *browser, 722 int delay_secs) 723 { 724 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 725 "ENTER: OK, ESC: Cancel", 726 delay_secs * 2) != K_ENTER || 727 !*browser->search_bf) 728 return false; 729 730 return true; 731 } 732 733 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 734 { 735 if (annotate_browser__search_window(browser, delay_secs)) 736 return __annotate_browser__search(browser); 737 738 return false; 739 } 740 741 static bool annotate_browser__continue_search(struct annotate_browser *browser, 742 int delay_secs) 743 { 744 if (!*browser->search_bf) 745 return annotate_browser__search(browser, delay_secs); 746 747 return __annotate_browser__search(browser); 748 } 749 750 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 751 int delay_secs) 752 { 753 if (annotate_browser__search_window(browser, delay_secs)) 754 return __annotate_browser__search_reverse(browser); 755 756 return false; 757 } 758 759 static 760 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 761 int delay_secs) 762 { 763 if (!*browser->search_bf) 764 return annotate_browser__search_reverse(browser, delay_secs); 765 766 return __annotate_browser__search_reverse(browser); 767 } 768 769 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help) 770 { 771 struct map_symbol *ms = browser->priv; 772 struct symbol *sym = ms->sym; 773 char symbol_dso[SYM_TITLE_MAX_SIZE]; 774 775 if (ui_browser__show(browser, title, help) < 0) 776 return -1; 777 778 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), annotate_opts.percent_type); 779 780 ui_browser__gotorc_title(browser, 0, 0); 781 ui_browser__set_color(browser, HE_COLORSET_ROOT); 782 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1); 783 return 0; 784 } 785 786 static void 787 switch_percent_type(struct annotation_options *opts, bool base) 788 { 789 switch (opts->percent_type) { 790 case PERCENT_HITS_LOCAL: 791 if (base) 792 opts->percent_type = PERCENT_PERIOD_LOCAL; 793 else 794 opts->percent_type = PERCENT_HITS_GLOBAL; 795 break; 796 case PERCENT_HITS_GLOBAL: 797 if (base) 798 opts->percent_type = PERCENT_PERIOD_GLOBAL; 799 else 800 opts->percent_type = PERCENT_HITS_LOCAL; 801 break; 802 case PERCENT_PERIOD_LOCAL: 803 if (base) 804 opts->percent_type = PERCENT_HITS_LOCAL; 805 else 806 opts->percent_type = PERCENT_PERIOD_GLOBAL; 807 break; 808 case PERCENT_PERIOD_GLOBAL: 809 if (base) 810 opts->percent_type = PERCENT_HITS_GLOBAL; 811 else 812 opts->percent_type = PERCENT_PERIOD_LOCAL; 813 break; 814 default: 815 WARN_ON(1); 816 } 817 } 818 819 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size) 820 { 821 int printed = hists__scnprintf_title(hists, bf, size); 822 823 if (!annotate_opts.hide_src_code_on_title) { 824 printed += scnprintf(bf + printed, size - printed, " [source: %s]", 825 annotate_opts.hide_src_code ? "OFF" : "On"); 826 } 827 828 return printed; 829 } 830 831 static void annotate_browser__debuginfo_warning(struct annotate_browser *browser) 832 { 833 struct map_symbol *ms = browser->b.priv; 834 struct dso *dso = map__dso(ms->map); 835 836 if (browser->dbg == NULL && annotate_opts.code_with_type && 837 !dso__debuginfo_warned(dso)) { 838 ui__warning("DWARF debuginfo not found.\n\n" 839 "Data-type in this DSO will not be displayed.\n" 840 "Please make sure to have debug information."); 841 dso__set_debuginfo_warned(dso); 842 } 843 } 844 845 static int annotate_browser__run(struct annotate_browser *browser, 846 struct evsel *evsel, 847 struct hist_browser_timer *hbt) 848 { 849 struct rb_node *nd = NULL; 850 struct hists *hists = evsel__hists(evsel); 851 struct map_symbol *ms = browser->b.priv; 852 struct symbol *sym = ms->sym; 853 struct annotation *notes = symbol__annotation(ms->sym); 854 const char *help = "Press 'h' for help on key bindings"; 855 int delay_secs = hbt ? hbt->refresh : 0; 856 char *br_cntr_text = NULL; 857 char title[256]; 858 int key; 859 860 annotate__scnprintf_title(hists, title, sizeof(title)); 861 if (annotate_browser__show(&browser->b, title, help) < 0) 862 return -1; 863 864 annotate_browser__calc_percent(browser, evsel); 865 866 if (browser->curr_hot) { 867 annotate_browser__set_rb_top(browser, browser->curr_hot); 868 browser->b.navkeypressed = false; 869 } 870 871 nd = browser->curr_hot; 872 873 annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); 874 875 annotate_browser__debuginfo_warning(browser); 876 877 while (1) { 878 key = ui_browser__run(&browser->b, delay_secs); 879 880 if (delay_secs != 0) { 881 annotate_browser__calc_percent(browser, evsel); 882 /* 883 * Current line focus got out of the list of most active 884 * lines, NULL it so that if TAB|UNTAB is pressed, we 885 * move to curr_hot (current hottest line). 886 */ 887 if (nd != NULL && RB_EMPTY_NODE(nd)) 888 nd = NULL; 889 } 890 891 switch (key) { 892 case K_TIMER: 893 if (hbt) 894 hbt->timer(hbt->arg); 895 896 if (delay_secs != 0) { 897 symbol__annotate_decay_histogram(sym, evsel); 898 annotate__scnprintf_title(hists, title, sizeof(title)); 899 annotate_browser__show(&browser->b, title, help); 900 } 901 continue; 902 case K_TAB: 903 if (nd != NULL) { 904 nd = rb_prev(nd); 905 if (nd == NULL) 906 nd = rb_last(&browser->entries); 907 } else 908 nd = browser->curr_hot; 909 break; 910 case K_UNTAB: 911 if (nd != NULL) { 912 nd = rb_next(nd); 913 if (nd == NULL) 914 nd = rb_first(&browser->entries); 915 } else 916 nd = browser->curr_hot; 917 break; 918 case K_F1: 919 case 'h': 920 ui_browser__help_window(&browser->b, 921 "UP/DOWN/PGUP\n" 922 "PGDN/SPACE Navigate\n" 923 "</> Move to prev/next symbol\n" 924 "q/ESC/CTRL+C Exit\n\n" 925 "ENTER Go to target\n" 926 "H Go to hottest instruction\n" 927 "TAB/shift+TAB Cycle thru hottest instructions\n" 928 "j Toggle showing jump to target arrows\n" 929 "J Toggle showing number of jump sources on targets\n" 930 "n Search next string\n" 931 "o Toggle disassembler output/simplified view\n" 932 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n" 933 "s Toggle source code view\n" 934 "t Circulate percent, total period, samples view\n" 935 "c Show min/max cycle\n" 936 "/ Search string\n" 937 "k Toggle line numbers\n" 938 "l Show full source file location\n" 939 "P Print to [symbol_name].annotation file.\n" 940 "r Run available scripts\n" 941 "p Toggle percent type [local/global]\n" 942 "b Toggle percent base [period/hits]\n" 943 "B Branch counter abbr list (Optional)\n" 944 "? Search string backwards\n" 945 "f Toggle showing offsets to full address\n" 946 "T Toggle data type display\n"); 947 continue; 948 case 'r': 949 script_browse(NULL, NULL); 950 annotate_browser__show(&browser->b, title, help); 951 continue; 952 case 'k': 953 annotate_opts.show_linenr = !annotate_opts.show_linenr; 954 continue; 955 case 'l': 956 annotate_browser__show_full_location (&browser->b); 957 continue; 958 case 'H': 959 nd = browser->curr_hot; 960 break; 961 case 's': 962 if (annotate_browser__toggle_source(browser, evsel)) 963 ui_helpline__puts(help); 964 annotate__scnprintf_title(hists, title, sizeof(title)); 965 annotate_browser__show(&browser->b, title, help); 966 continue; 967 case 'o': 968 annotate_opts.use_offset = !annotate_opts.use_offset; 969 annotation__update_column_widths(notes); 970 continue; 971 case 'O': 972 if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 973 annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 974 continue; 975 case 'j': 976 annotate_opts.jump_arrows = !annotate_opts.jump_arrows; 977 continue; 978 case 'J': 979 annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps; 980 annotation__update_column_widths(notes); 981 continue; 982 case '/': 983 if (annotate_browser__search(browser, delay_secs)) { 984 show_help: 985 ui_helpline__puts(help); 986 } 987 continue; 988 case 'n': 989 if (browser->searching_backwards ? 990 annotate_browser__continue_search_reverse(browser, delay_secs) : 991 annotate_browser__continue_search(browser, delay_secs)) 992 goto show_help; 993 continue; 994 case '?': 995 if (annotate_browser__search_reverse(browser, delay_secs)) 996 goto show_help; 997 continue; 998 case 'D': { 999 static int seq; 1000 ui_helpline__pop(); 1001 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 1002 seq++, browser->b.nr_entries, 1003 browser->b.height, 1004 browser->b.index, 1005 browser->b.top_idx, 1006 notes->src->nr_asm_entries); 1007 } 1008 continue; 1009 case K_ENTER: 1010 case K_RIGHT: 1011 { 1012 struct disasm_line *dl = disasm_line(browser->selection); 1013 1014 if (browser->selection == NULL) 1015 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 1016 else if (browser->selection->offset == -1) 1017 ui_helpline__puts("Actions are only available for assembly lines."); 1018 else if (!dl->ins.ops) 1019 goto show_sup_ins; 1020 else if (ins__is_ret(&dl->ins)) 1021 goto out; 1022 else if (!(annotate_browser__jump(browser, evsel, hbt) || 1023 annotate_browser__callq(browser, evsel, hbt))) { 1024 show_sup_ins: 1025 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 1026 } 1027 continue; 1028 } 1029 case 'P': 1030 map_symbol__annotation_dump(ms, evsel, browser->he); 1031 continue; 1032 case 't': 1033 if (symbol_conf.show_total_period) { 1034 symbol_conf.show_total_period = false; 1035 symbol_conf.show_nr_samples = true; 1036 } else if (symbol_conf.show_nr_samples) 1037 symbol_conf.show_nr_samples = false; 1038 else 1039 symbol_conf.show_total_period = true; 1040 annotation__update_column_widths(notes); 1041 continue; 1042 case 'c': 1043 if (annotate_opts.show_minmax_cycle) 1044 annotate_opts.show_minmax_cycle = false; 1045 else 1046 annotate_opts.show_minmax_cycle = true; 1047 annotation__update_column_widths(notes); 1048 continue; 1049 case 'p': 1050 case 'b': 1051 switch_percent_type(&annotate_opts, key == 'b'); 1052 annotate__scnprintf_title(hists, title, sizeof(title)); 1053 annotate_browser__show(&browser->b, title, help); 1054 continue; 1055 case 'B': 1056 if (br_cntr_text) 1057 ui_browser__help_window(&browser->b, br_cntr_text); 1058 else { 1059 ui_browser__help_window(&browser->b, 1060 "\n The branch counter is not available.\n"); 1061 } 1062 continue; 1063 case 'f': 1064 annotation__toggle_full_addr(notes, ms); 1065 continue; 1066 case 'T': 1067 annotate_opts.code_with_type ^= 1; 1068 if (browser->dbg == NULL) 1069 browser->dbg = dso__debuginfo(map__dso(ms->map)); 1070 if (browser->type_hash == NULL) { 1071 browser->type_hash = hashmap__new(type_hash, type_equal, 1072 /*ctx=*/NULL); 1073 } 1074 annotate_browser__show(&browser->b, title, help); 1075 annotate_browser__debuginfo_warning(browser); 1076 continue; 1077 case K_LEFT: 1078 case '<': 1079 case '>': 1080 case K_ESC: 1081 case 'q': 1082 case CTRL('c'): 1083 goto out; 1084 default: 1085 ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); 1086 continue; 1087 } 1088 1089 if (nd != NULL) 1090 annotate_browser__set_rb_top(browser, nd); 1091 } 1092 out: 1093 ui_browser__hide(&browser->b); 1094 free(br_cntr_text); 1095 return key; 1096 } 1097 1098 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, 1099 struct hist_browser_timer *hbt) 1100 { 1101 /* reset abort key so that it can get Ctrl-C as a key */ 1102 SLang_reset_tty(); 1103 SLang_init_tty(0, 0, 0); 1104 SLtty_set_suspend_state(true); 1105 1106 return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); 1107 } 1108 1109 int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, 1110 struct evsel *evsel, 1111 struct hist_browser_timer *hbt) 1112 { 1113 struct symbol *sym = ms->sym; 1114 struct annotation *notes = symbol__annotation(sym); 1115 struct annotate_browser browser = { 1116 .b = { 1117 .refresh = annotate_browser__refresh, 1118 .seek = ui_browser__list_head_seek, 1119 .write = annotate_browser__write, 1120 .filter = disasm_line__filter, 1121 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 1122 .priv = ms, 1123 .use_navkeypressed = true, 1124 }, 1125 .he = he, 1126 .evsel = evsel, 1127 }; 1128 struct dso *dso; 1129 int ret = -1, err; 1130 int not_annotated = list_empty(¬es->src->source); 1131 1132 if (sym == NULL) 1133 return -1; 1134 1135 dso = map__dso(ms->map); 1136 if (dso__annotate_warned(dso)) 1137 return -1; 1138 1139 if (not_annotated || !sym->annotate2) { 1140 err = symbol__annotate2(ms, evsel, &browser.arch); 1141 if (err) { 1142 char msg[BUFSIZ]; 1143 dso__set_annotate_warned(dso); 1144 symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 1145 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 1146 return -1; 1147 } 1148 1149 if (!annotate_opts.hide_src_code) { 1150 notes->src->tried_source = true; 1151 if (!annotation__has_source(notes)) 1152 ui__warning("Annotation has no source code."); 1153 } 1154 } 1155 1156 /* Copy necessary information when it's called from perf top */ 1157 if (hbt != NULL && he != &annotate_he) { 1158 annotate_he.hists = he->hists; 1159 annotate_he.thread = thread__get(he->thread); 1160 annotate_he.cpumode = he->cpumode; 1161 map_symbol__copy(&annotate_he.ms, ms); 1162 1163 browser.he = &annotate_he; 1164 } 1165 1166 ui_helpline__push("Press ESC to exit"); 1167 1168 if (annotate_opts.code_with_type) { 1169 browser.dbg = dso__debuginfo(dso); 1170 browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL); 1171 } 1172 1173 browser.b.width = notes->src->widths.max_line_len; 1174 browser.b.nr_entries = notes->src->nr_entries; 1175 browser.b.entries = ¬es->src->source; 1176 browser.b.width += 18; /* Percentage */ 1177 1178 if (annotate_opts.hide_src_code) 1179 ui_browser__init_asm_mode(&browser.b); 1180 1181 ret = annotate_browser__run(&browser, evsel, hbt); 1182 1183 debuginfo__delete(browser.dbg); 1184 1185 if (!IS_ERR_OR_NULL(browser.type_hash)) { 1186 struct hashmap_entry *cur; 1187 size_t bkt; 1188 1189 hashmap__for_each_entry(browser.type_hash, cur, bkt) 1190 zfree(&cur->pvalue); 1191 hashmap__free(browser.type_hash); 1192 } 1193 1194 if (not_annotated && !notes->src->tried_source) 1195 annotated_source__purge(notes->src); 1196 1197 if (hbt != NULL && he != &annotate_he) { 1198 thread__zput(annotate_he.thread); 1199 map_symbol__exit(&annotate_he.ms); 1200 } 1201 1202 return ret; 1203 } 1204