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