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 const 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 if (browser->b.nr_entries == 0) 453 return false; 454 455 browser->b.seek(&browser->b, offset, SEEK_CUR); 456 al = list_entry(browser->b.top, struct annotation_line, node); 457 458 if (!annotate_opts.annotate_src) 459 annotate_opts.annotate_src = true; 460 461 /* 462 * It's about to get source code annotation for the first time. 463 * Drop the existing annotation_lines and get the new one with source. 464 * And then move to the original line at the same asm index. 465 */ 466 if (annotate_opts.hide_src_code && !notes->src->tried_source) { 467 struct map_symbol *ms = browser->b.priv; 468 int orig_idx_asm = al->idx_asm; 469 470 /* annotate again with source code info */ 471 annotate_opts.hide_src_code = false; 472 annotated_source__purge(notes->src); 473 symbol__annotate2(ms, evsel, &browser->arch); 474 annotate_opts.hide_src_code = true; 475 476 /* should be after annotated_source__purge() */ 477 notes->src->tried_source = true; 478 479 if (!annotation__has_source(notes)) 480 ui__warning("Annotation has no source code."); 481 482 browser->b.entries = ¬es->src->source; 483 al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); 484 if (unlikely(al == NULL)) { 485 al = list_first_entry(¬es->src->source, 486 struct annotation_line, node); 487 } 488 browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); 489 } 490 491 if (annotate_opts.hide_src_code) { 492 if (al->idx_asm < offset) 493 offset = al->idx; 494 495 browser->b.nr_entries = notes->src->nr_entries; 496 annotate_opts.hide_src_code = false; 497 browser->b.seek(&browser->b, -offset, SEEK_CUR); 498 browser->b.top_idx = al->idx - offset; 499 browser->b.index = al->idx; 500 } else { 501 if (al->idx_asm < 0) { 502 /* move cursor to next asm line */ 503 al = annotate_browser__find_next_asm_line(browser, al); 504 if (!al) { 505 browser->b.seek(&browser->b, -offset, SEEK_CUR); 506 return false; 507 } 508 } 509 510 if (al->idx_asm < offset) 511 offset = al->idx_asm; 512 513 browser->b.nr_entries = notes->src->nr_asm_entries; 514 annotate_opts.hide_src_code = true; 515 browser->b.seek(&browser->b, -offset, SEEK_CUR); 516 browser->b.top_idx = al->idx_asm - offset; 517 browser->b.index = al->idx_asm; 518 } 519 520 if (annotate_opts.hide_src_code_on_title) 521 annotate_opts.hide_src_code_on_title = false; 522 523 return true; 524 } 525 526 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) 527 528 static void annotate_browser__show_full_location(struct ui_browser *browser) 529 { 530 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 531 struct disasm_line *cursor = disasm_line(ab->selection); 532 struct annotation_line *al = &cursor->al; 533 534 if (al->offset != -1) 535 ui_helpline__puts("Only available for source code lines."); 536 else if (al->fileloc == NULL) 537 ui_helpline__puts("No source file location."); 538 else { 539 char help_line[SYM_TITLE_MAX_SIZE]; 540 sprintf (help_line, "Source file location: %s", al->fileloc); 541 ui_helpline__puts(help_line); 542 } 543 } 544 545 static void ui_browser__init_asm_mode(struct ui_browser *browser) 546 { 547 struct annotation *notes = browser__annotation(browser); 548 browser->nr_entries = notes->src->nr_asm_entries; 549 ui_browser__reset_index(browser); 550 } 551 552 static int sym_title(struct symbol *sym, struct map *map, char *title, 553 size_t sz, int percent_type) 554 { 555 return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name, 556 dso__long_name(map__dso(map)), 557 percent_type_str(percent_type), 558 annotate_opts.code_with_type ? "[Type]" : ""); 559 } 560 561 static void annotate_browser__show_function_title(struct annotate_browser *browser) 562 { 563 struct ui_browser *b = &browser->b; 564 struct map_symbol *ms = b->priv; 565 struct symbol *sym = ms->sym; 566 char title[SYM_TITLE_MAX_SIZE]; 567 568 sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type); 569 570 ui_browser__gotorc_title(b, 0, 0); 571 ui_browser__set_color(b, HE_COLORSET_ROOT); 572 ui_browser__write_nstring(b, title, b->width + 1); 573 } 574 575 /* 576 * This can be called from external jumps, i.e. jumps from one function 577 * to another, like from the kernel's entry_SYSCALL_64 function to the 578 * swapgs_restore_regs_and_return_to_usermode() function. 579 * 580 * So all we check here is that dl->ops.target.sym is set, if it is, just 581 * go to that function and when exiting from its disassembly, come back 582 * to the calling function. 583 */ 584 static bool annotate_browser__callq(struct annotate_browser *browser, 585 struct evsel *evsel, 586 struct hist_browser_timer *hbt) 587 { 588 struct map_symbol *ms = browser->b.priv, target_ms; 589 struct disasm_line *dl = disasm_line(browser->selection); 590 struct annotation *notes; 591 592 if (!dl->ops.target.sym) { 593 ui_helpline__puts("The called function was not found."); 594 return true; 595 } 596 597 notes = symbol__annotation(dl->ops.target.sym); 598 annotation__lock(notes); 599 600 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { 601 annotation__unlock(notes); 602 ui__warning("Not enough memory for annotating '%s' symbol!\n", 603 dl->ops.target.sym->name); 604 return true; 605 } 606 607 target_ms.thread = ms->thread; 608 target_ms.map = ms->map; 609 target_ms.sym = dl->ops.target.sym; 610 annotation__unlock(notes); 611 __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR); 612 613 /* 614 * The annotate_browser above changed the title with the target function 615 * and now it's back to the original function. Refresh the header line 616 * for the original function again. 617 */ 618 annotate_browser__show_function_title(browser); 619 return true; 620 } 621 622 static 623 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 624 s64 offset, s64 *idx) 625 { 626 struct annotation *notes = browser__annotation(&browser->b); 627 struct disasm_line *pos; 628 629 *idx = 0; 630 list_for_each_entry(pos, ¬es->src->source, al.node) { 631 if (pos->al.offset == offset) 632 return pos; 633 if (!annotation_line__filter(&pos->al)) 634 ++*idx; 635 } 636 637 return NULL; 638 } 639 640 static bool annotate_browser__jump(struct annotate_browser *browser, 641 struct evsel *evsel, 642 struct hist_browser_timer *hbt) 643 { 644 struct disasm_line *dl = disasm_line(browser->selection); 645 u64 offset; 646 s64 idx; 647 648 if (!ins__is_jump(&dl->ins)) 649 return false; 650 651 if (dl->ops.target.outside) { 652 annotate_browser__callq(browser, evsel, hbt); 653 return true; 654 } 655 656 offset = dl->ops.target.offset; 657 dl = annotate_browser__find_offset(browser, offset, &idx); 658 if (dl == NULL) { 659 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 660 return true; 661 } 662 663 annotate_browser__set_top(browser, &dl->al, idx); 664 665 return true; 666 } 667 668 static 669 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, 670 char *s, s64 *idx) 671 { 672 struct annotation *notes = browser__annotation(&browser->b); 673 struct annotation_line *al = browser->selection; 674 675 *idx = browser->b.index; 676 list_for_each_entry_continue(al, ¬es->src->source, node) { 677 if (annotation_line__filter(al)) 678 continue; 679 680 ++*idx; 681 682 if (al->line && strstr(al->line, s) != NULL) 683 return al; 684 } 685 686 return NULL; 687 } 688 689 static bool __annotate_browser__search(struct annotate_browser *browser) 690 { 691 struct annotation_line *al; 692 s64 idx; 693 694 al = annotate_browser__find_string(browser, browser->search_bf, &idx); 695 if (al == NULL) { 696 ui_helpline__puts("String not found!"); 697 return false; 698 } 699 700 annotate_browser__set_top(browser, al, idx); 701 browser->searching_backwards = false; 702 return true; 703 } 704 705 static 706 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 707 char *s, s64 *idx) 708 { 709 struct annotation *notes = browser__annotation(&browser->b); 710 struct annotation_line *al = browser->selection; 711 712 *idx = browser->b.index; 713 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { 714 if (annotation_line__filter(al)) 715 continue; 716 717 --*idx; 718 719 if (al->line && strstr(al->line, s) != NULL) 720 return al; 721 } 722 723 return NULL; 724 } 725 726 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 727 { 728 struct annotation_line *al; 729 s64 idx; 730 731 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 732 if (al == NULL) { 733 ui_helpline__puts("String not found!"); 734 return false; 735 } 736 737 annotate_browser__set_top(browser, al, idx); 738 browser->searching_backwards = true; 739 return true; 740 } 741 742 static bool annotate_browser__search_window(struct annotate_browser *browser, 743 int delay_secs) 744 { 745 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 746 "ENTER: OK, ESC: Cancel", 747 delay_secs * 2) != K_ENTER || 748 !*browser->search_bf) 749 return false; 750 751 return true; 752 } 753 754 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 755 { 756 if (annotate_browser__search_window(browser, delay_secs)) 757 return __annotate_browser__search(browser); 758 759 return false; 760 } 761 762 static bool annotate_browser__continue_search(struct annotate_browser *browser, 763 int delay_secs) 764 { 765 if (!*browser->search_bf) 766 return annotate_browser__search(browser, delay_secs); 767 768 return __annotate_browser__search(browser); 769 } 770 771 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 772 int delay_secs) 773 { 774 if (annotate_browser__search_window(browser, delay_secs)) 775 return __annotate_browser__search_reverse(browser); 776 777 return false; 778 } 779 780 static 781 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 782 int delay_secs) 783 { 784 if (!*browser->search_bf) 785 return annotate_browser__search_reverse(browser, delay_secs); 786 787 return __annotate_browser__search_reverse(browser); 788 } 789 790 static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help) 791 { 792 if (ui_browser__show(&browser->b, title, help) < 0) 793 return -1; 794 795 annotate_browser__show_function_title(browser); 796 return 0; 797 } 798 799 static void 800 switch_percent_type(struct annotation_options *opts, bool base) 801 { 802 switch (opts->percent_type) { 803 case PERCENT_HITS_LOCAL: 804 if (base) 805 opts->percent_type = PERCENT_PERIOD_LOCAL; 806 else 807 opts->percent_type = PERCENT_HITS_GLOBAL; 808 break; 809 case PERCENT_HITS_GLOBAL: 810 if (base) 811 opts->percent_type = PERCENT_PERIOD_GLOBAL; 812 else 813 opts->percent_type = PERCENT_HITS_LOCAL; 814 break; 815 case PERCENT_PERIOD_LOCAL: 816 if (base) 817 opts->percent_type = PERCENT_HITS_LOCAL; 818 else 819 opts->percent_type = PERCENT_PERIOD_GLOBAL; 820 break; 821 case PERCENT_PERIOD_GLOBAL: 822 if (base) 823 opts->percent_type = PERCENT_HITS_GLOBAL; 824 else 825 opts->percent_type = PERCENT_PERIOD_LOCAL; 826 break; 827 default: 828 WARN_ON(1); 829 } 830 } 831 832 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size) 833 { 834 int printed = hists__scnprintf_title(hists, bf, size); 835 836 if (!annotate_opts.hide_src_code_on_title) { 837 printed += scnprintf(bf + printed, size - printed, " [source: %s]", 838 annotate_opts.hide_src_code ? "OFF" : "On"); 839 } 840 841 return printed; 842 } 843 844 static void annotate_browser__debuginfo_warning(struct annotate_browser *browser) 845 { 846 struct map_symbol *ms = browser->b.priv; 847 struct dso *dso = map__dso(ms->map); 848 849 if (browser->dbg == NULL && annotate_opts.code_with_type && 850 !dso__debuginfo_warned(dso)) { 851 ui__warning("DWARF debuginfo not found.\n\n" 852 "Data-type in this DSO will not be displayed.\n" 853 "Please make sure to have debug information."); 854 dso__set_debuginfo_warned(dso); 855 } 856 } 857 858 static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser) 859 { 860 struct annotation_line *al = NULL; 861 862 if (browser->curr_hot) 863 al = rb_entry(browser->curr_hot, struct annotation_line, rb_node); 864 865 return al ? al->offset : 0; 866 } 867 868 static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err) 869 { 870 struct map_symbol *ms = browser->b.priv; 871 struct symbol *sym = ms->sym; 872 struct dso *dso = map__dso(ms->map); 873 char msg[BUFSIZ]; 874 875 dso__set_annotate_warned(dso); 876 symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 877 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 878 } 879 880 static int annotate_browser__run(struct annotate_browser *browser, 881 struct evsel *evsel, 882 struct hist_browser_timer *hbt) 883 { 884 struct rb_node *nd = NULL; 885 struct hists *hists = evsel__hists(evsel); 886 struct map_symbol *ms = browser->b.priv; 887 struct symbol *sym = ms->sym; 888 struct annotation *notes = symbol__annotation(ms->sym); 889 const char *help = "Press 'h' for help on key bindings"; 890 int delay_secs = hbt ? hbt->refresh : 0; 891 char *br_cntr_text = NULL; 892 char title[256]; 893 int key; 894 895 annotate__scnprintf_title(hists, title, sizeof(title)); 896 if (annotate_browser__show(browser, title, help) < 0) 897 return -1; 898 899 annotate_browser__calc_percent(browser, evsel); 900 901 if (browser->selection != NULL) { 902 browser->curr_hot = &browser->selection->rb_node; 903 browser->b.use_navkeypressed = false; 904 } 905 906 if (browser->curr_hot) { 907 annotate_browser__set_rb_top(browser, browser->curr_hot); 908 browser->b.navkeypressed = false; 909 } 910 911 nd = browser->curr_hot; 912 913 annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); 914 915 annotate_browser__debuginfo_warning(browser); 916 917 while (1) { 918 key = ui_browser__run(&browser->b, delay_secs); 919 920 if (delay_secs != 0) { 921 annotate_browser__calc_percent(browser, evsel); 922 /* 923 * Current line focus got out of the list of most active 924 * lines, NULL it so that if TAB|UNTAB is pressed, we 925 * move to curr_hot (current hottest line). 926 */ 927 if (nd != NULL && RB_EMPTY_NODE(nd)) 928 nd = NULL; 929 } 930 931 switch (key) { 932 case K_TIMER: 933 if (hbt) 934 hbt->timer(hbt->arg); 935 936 if (delay_secs != 0) { 937 symbol__annotate_decay_histogram(sym, evsel); 938 annotate__scnprintf_title(hists, title, sizeof(title)); 939 annotate_browser__show(browser, title, help); 940 } 941 continue; 942 case K_TAB: 943 if (nd != NULL) { 944 nd = rb_prev(nd); 945 if (nd == NULL) 946 nd = rb_last(&browser->entries); 947 } else 948 nd = browser->curr_hot; 949 break; 950 case K_UNTAB: 951 if (nd != NULL) { 952 nd = rb_next(nd); 953 if (nd == NULL) 954 nd = rb_first(&browser->entries); 955 } else 956 nd = browser->curr_hot; 957 break; 958 case K_F1: 959 case 'h': 960 ui_browser__help_window(&browser->b, 961 "UP/DOWN/PGUP\n" 962 "PGDN/SPACE Navigate\n" 963 "</> Move to prev/next symbol\n" 964 "q/ESC/CTRL+C Exit\n\n" 965 "ENTER Go to target\n" 966 "H Go to hottest instruction\n" 967 "TAB/shift+TAB Cycle thru hottest instructions\n" 968 "j Toggle showing jump to target arrows\n" 969 "J Toggle showing number of jump sources on targets\n" 970 "n Search next string\n" 971 "o Toggle disassembler output/simplified view\n" 972 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n" 973 "s Toggle source code view\n" 974 "t Circulate percent, total period, samples view\n" 975 "c Show min/max cycle\n" 976 "/ Search string\n" 977 "k Toggle line numbers\n" 978 "l Show full source file location\n" 979 "P Print to [symbol_name].annotation file.\n" 980 "r Run available scripts\n" 981 "p Toggle percent type [local/global]\n" 982 "b Toggle percent base [period/hits]\n" 983 "B Branch counter abbr list (Optional)\n" 984 "? Search string backwards\n" 985 "f Toggle showing offsets to full address\n" 986 "T Toggle data type display\n"); 987 continue; 988 case 'r': 989 script_browse(NULL, NULL); 990 annotate_browser__show(browser, title, help); 991 continue; 992 case 'k': 993 annotate_opts.show_linenr = !annotate_opts.show_linenr; 994 continue; 995 case 'l': 996 annotate_browser__show_full_location (&browser->b); 997 continue; 998 case 'H': 999 nd = browser->curr_hot; 1000 break; 1001 case 's': { 1002 struct annotation_line *al = NULL; 1003 s64 offset = annotate_browser__curr_hot_offset(browser); 1004 1005 if (annotate_browser__toggle_source(browser, evsel)) 1006 ui_helpline__puts(help); 1007 1008 /* Update the annotation browser's rb_tree, and reset the nd */ 1009 annotate_browser__calc_percent(browser, evsel); 1010 /* Try to find the same asm line as before */ 1011 al = annotated_source__get_line(notes->src, offset); 1012 browser->curr_hot = al ? &al->rb_node : NULL; 1013 nd = browser->curr_hot; 1014 1015 annotate__scnprintf_title(hists, title, sizeof(title)); 1016 annotate_browser__show(browser, title, help); 1017 continue; 1018 } 1019 case 'o': 1020 annotate_opts.use_offset = !annotate_opts.use_offset; 1021 annotation__update_column_widths(notes); 1022 continue; 1023 case 'O': 1024 if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 1025 annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 1026 continue; 1027 case 'j': 1028 annotate_opts.jump_arrows = !annotate_opts.jump_arrows; 1029 continue; 1030 case 'J': 1031 annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps; 1032 annotation__update_column_widths(notes); 1033 continue; 1034 case '/': 1035 if (annotate_browser__search(browser, delay_secs)) { 1036 show_help: 1037 ui_helpline__puts(help); 1038 } 1039 continue; 1040 case 'n': 1041 if (browser->searching_backwards ? 1042 annotate_browser__continue_search_reverse(browser, delay_secs) : 1043 annotate_browser__continue_search(browser, delay_secs)) 1044 goto show_help; 1045 continue; 1046 case '?': 1047 if (annotate_browser__search_reverse(browser, delay_secs)) 1048 goto show_help; 1049 continue; 1050 case 'D': { 1051 static int seq; 1052 ui_helpline__pop(); 1053 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 1054 seq++, browser->b.nr_entries, 1055 browser->b.height, 1056 browser->b.index, 1057 browser->b.top_idx, 1058 notes->src->nr_asm_entries); 1059 } 1060 continue; 1061 case K_ENTER: 1062 case K_RIGHT: 1063 { 1064 struct disasm_line *dl = disasm_line(browser->selection); 1065 1066 if (browser->selection == NULL) 1067 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 1068 else if (browser->selection->offset == -1) 1069 ui_helpline__puts("Actions are only available for assembly lines."); 1070 else if (!dl->ins.ops) 1071 goto show_sup_ins; 1072 else if (ins__is_ret(&dl->ins)) 1073 goto out; 1074 else if (!(annotate_browser__jump(browser, evsel, hbt) || 1075 annotate_browser__callq(browser, evsel, hbt))) { 1076 show_sup_ins: 1077 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 1078 } 1079 continue; 1080 } 1081 case 'P': 1082 map_symbol__annotation_dump(ms, evsel, browser->he); 1083 continue; 1084 case 't': 1085 if (symbol_conf.show_total_period) { 1086 symbol_conf.show_total_period = false; 1087 symbol_conf.show_nr_samples = true; 1088 } else if (symbol_conf.show_nr_samples) 1089 symbol_conf.show_nr_samples = false; 1090 else 1091 symbol_conf.show_total_period = true; 1092 annotation__update_column_widths(notes); 1093 continue; 1094 case 'c': 1095 if (annotate_opts.show_minmax_cycle) 1096 annotate_opts.show_minmax_cycle = false; 1097 else 1098 annotate_opts.show_minmax_cycle = true; 1099 annotation__update_column_widths(notes); 1100 continue; 1101 case 'p': 1102 case 'b': 1103 switch_percent_type(&annotate_opts, key == 'b'); 1104 annotate__scnprintf_title(hists, title, sizeof(title)); 1105 annotate_browser__show(browser, title, help); 1106 continue; 1107 case 'B': 1108 if (br_cntr_text) 1109 ui_browser__help_window(&browser->b, br_cntr_text); 1110 else { 1111 ui_browser__help_window(&browser->b, 1112 "\n The branch counter is not available.\n"); 1113 } 1114 continue; 1115 case 'f': 1116 annotation__toggle_full_addr(notes, ms); 1117 continue; 1118 case 'T': 1119 annotate_opts.code_with_type ^= 1; 1120 if (browser->dbg == NULL) 1121 browser->dbg = dso__debuginfo(map__dso(ms->map)); 1122 if (browser->type_hash == NULL) { 1123 browser->type_hash = hashmap__new(type_hash, type_equal, 1124 /*ctx=*/NULL); 1125 } 1126 annotate_browser__show(browser, title, help); 1127 annotate_browser__debuginfo_warning(browser); 1128 continue; 1129 case K_LEFT: 1130 case '<': 1131 case '>': 1132 case K_ESC: 1133 case 'q': 1134 case CTRL('c'): 1135 goto out; 1136 default: 1137 ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); 1138 continue; 1139 } 1140 1141 if (nd != NULL) 1142 annotate_browser__set_rb_top(browser, nd); 1143 } 1144 out: 1145 ui_browser__hide(&browser->b); 1146 free(br_cntr_text); 1147 return key; 1148 } 1149 1150 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, 1151 struct hist_browser_timer *hbt, u64 al_addr) 1152 { 1153 /* reset abort key so that it can get Ctrl-C as a key */ 1154 SLang_reset_tty(); 1155 SLang_init_tty(0, 0, 0); 1156 SLtty_set_suspend_state(true); 1157 1158 return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr); 1159 } 1160 1161 int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, 1162 struct evsel *evsel, 1163 struct hist_browser_timer *hbt, u64 al_addr) 1164 { 1165 struct symbol *sym = ms->sym; 1166 struct annotation *notes = symbol__annotation(sym); 1167 struct annotate_browser browser = { 1168 .b = { 1169 .refresh = annotate_browser__refresh, 1170 .seek = ui_browser__list_head_seek, 1171 .write = annotate_browser__write, 1172 .filter = disasm_line__filter, 1173 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 1174 .priv = ms, 1175 .use_navkeypressed = true, 1176 }, 1177 .he = he, 1178 .evsel = evsel, 1179 }; 1180 struct dso *dso; 1181 int ret = -1, err; 1182 int not_annotated = list_empty(¬es->src->source); 1183 1184 if (sym == NULL) 1185 return -1; 1186 1187 dso = map__dso(ms->map); 1188 if (dso__annotate_warned(dso)) 1189 return -1; 1190 1191 if (not_annotated || !symbol__is_annotate2(sym)) { 1192 err = symbol__annotate2(ms, evsel, &browser.arch); 1193 if (err) { 1194 annotate_browser__symbol_annotate_error(&browser, err); 1195 return -1; 1196 } 1197 1198 if (!annotate_opts.hide_src_code) { 1199 notes->src->tried_source = true; 1200 if (!annotation__has_source(notes)) 1201 ui__warning("Annotation has no source code."); 1202 } 1203 } else { 1204 err = thread__get_arch(ms->thread, &browser.arch); 1205 if (err) { 1206 annotate_browser__symbol_annotate_error(&browser, err); 1207 return -1; 1208 } 1209 } 1210 1211 /* Copy necessary information when it's called from perf top */ 1212 if (hbt != NULL && he != &annotate_he) { 1213 annotate_he.hists = he->hists; 1214 annotate_he.thread = thread__get(he->thread); 1215 annotate_he.cpumode = he->cpumode; 1216 map_symbol__copy(&annotate_he.ms, ms); 1217 1218 browser.he = &annotate_he; 1219 } 1220 1221 ui_helpline__push("Press ESC to exit"); 1222 1223 if (annotate_opts.code_with_type) { 1224 browser.dbg = dso__debuginfo(dso); 1225 browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL); 1226 } 1227 1228 browser.b.width = notes->src->widths.max_line_len; 1229 browser.b.nr_entries = notes->src->nr_entries; 1230 browser.b.entries = ¬es->src->source; 1231 browser.b.width += 18; /* Percentage */ 1232 1233 if (annotate_opts.hide_src_code) 1234 ui_browser__init_asm_mode(&browser.b); 1235 1236 /* 1237 * If al_addr is set, it means that there should be a line 1238 * intentionally selected, not based on the percentages 1239 * which caculated by the event sampling. In this case, we 1240 * convey this information into the browser selection, where 1241 * the selection in other cases should be empty. 1242 */ 1243 if (al_addr != NO_ADDR) { 1244 struct annotation_line *al = annotated_source__get_line(notes->src, 1245 al_addr - sym->start); 1246 1247 browser.selection = al; 1248 } 1249 1250 ret = annotate_browser__run(&browser, evsel, hbt); 1251 1252 debuginfo__delete(browser.dbg); 1253 1254 if (!IS_ERR_OR_NULL(browser.type_hash)) { 1255 struct hashmap_entry *cur; 1256 size_t bkt; 1257 1258 hashmap__for_each_entry(browser.type_hash, cur, bkt) 1259 zfree(&cur->pvalue); 1260 hashmap__free(browser.type_hash); 1261 } 1262 1263 if (not_annotated && !notes->src->tried_source) 1264 annotated_source__purge(notes->src); 1265 1266 if (hbt != NULL && he != &annotate_he) { 1267 thread__zput(annotate_he.thread); 1268 map_symbol__exit(&annotate_he.ms); 1269 } 1270 1271 return ret; 1272 } 1273