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