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