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 double disasm__cmp(struct annotation_line *a, struct annotation_line *b, 228 int percent_type) 229 { 230 int i; 231 232 for (i = 0; i < a->data_nr; i++) { 233 if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type]) 234 continue; 235 return a->data[i].percent[percent_type] - 236 b->data[i].percent[percent_type]; 237 } 238 return 0; 239 } 240 241 static void disasm_rb_tree__insert(struct annotate_browser *browser, 242 struct annotation_line *al) 243 { 244 struct rb_root *root = &browser->entries; 245 struct rb_node **p = &root->rb_node; 246 struct rb_node *parent = NULL; 247 struct annotation_line *l; 248 249 while (*p != NULL) { 250 parent = *p; 251 l = rb_entry(parent, struct annotation_line, rb_node); 252 253 if (disasm__cmp(al, l, browser->opts->percent_type) < 0) 254 p = &(*p)->rb_left; 255 else 256 p = &(*p)->rb_right; 257 } 258 rb_link_node(&al->rb_node, parent, p); 259 rb_insert_color(&al->rb_node, root); 260 } 261 262 static void annotate_browser__set_top(struct annotate_browser *browser, 263 struct annotation_line *pos, u32 idx) 264 { 265 struct annotation *notes = browser__annotation(&browser->b); 266 unsigned back; 267 268 ui_browser__refresh_dimensions(&browser->b); 269 back = browser->b.height / 2; 270 browser->b.top_idx = browser->b.index = idx; 271 272 while (browser->b.top_idx != 0 && back != 0) { 273 pos = list_entry(pos->node.prev, struct annotation_line, node); 274 275 if (annotation_line__filter(pos, notes)) 276 continue; 277 278 --browser->b.top_idx; 279 --back; 280 } 281 282 browser->b.top = pos; 283 browser->b.navkeypressed = true; 284 } 285 286 static void annotate_browser__set_rb_top(struct annotate_browser *browser, 287 struct rb_node *nd) 288 { 289 struct annotation *notes = browser__annotation(&browser->b); 290 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); 291 u32 idx = pos->idx; 292 293 if (notes->options->hide_src_code) 294 idx = pos->idx_asm; 295 annotate_browser__set_top(browser, pos, idx); 296 browser->curr_hot = nd; 297 } 298 299 static void annotate_browser__calc_percent(struct annotate_browser *browser, 300 struct perf_evsel *evsel) 301 { 302 struct map_symbol *ms = browser->b.priv; 303 struct symbol *sym = ms->sym; 304 struct annotation *notes = symbol__annotation(sym); 305 struct disasm_line *pos; 306 307 browser->entries = RB_ROOT; 308 309 pthread_mutex_lock(¬es->lock); 310 311 symbol__calc_percent(sym, evsel); 312 313 list_for_each_entry(pos, ¬es->src->source, al.node) { 314 double max_percent = 0.0; 315 int i; 316 317 if (pos->al.offset == -1) { 318 RB_CLEAR_NODE(&pos->al.rb_node); 319 continue; 320 } 321 322 for (i = 0; i < pos->al.data_nr; i++) { 323 double percent; 324 325 percent = annotation_data__percent(&pos->al.data[i], 326 browser->opts->percent_type); 327 328 if (max_percent < percent) 329 max_percent = percent; 330 } 331 332 if (max_percent < 0.01 && pos->al.ipc == 0) { 333 RB_CLEAR_NODE(&pos->al.rb_node); 334 continue; 335 } 336 disasm_rb_tree__insert(browser, &pos->al); 337 } 338 pthread_mutex_unlock(¬es->lock); 339 340 browser->curr_hot = rb_last(&browser->entries); 341 } 342 343 static bool annotate_browser__toggle_source(struct annotate_browser *browser) 344 { 345 struct annotation *notes = browser__annotation(&browser->b); 346 struct annotation_line *al; 347 off_t offset = browser->b.index - browser->b.top_idx; 348 349 browser->b.seek(&browser->b, offset, SEEK_CUR); 350 al = list_entry(browser->b.top, struct annotation_line, node); 351 352 if (notes->options->hide_src_code) { 353 if (al->idx_asm < offset) 354 offset = al->idx; 355 356 browser->b.nr_entries = notes->nr_entries; 357 notes->options->hide_src_code = false; 358 browser->b.seek(&browser->b, -offset, SEEK_CUR); 359 browser->b.top_idx = al->idx - offset; 360 browser->b.index = al->idx; 361 } else { 362 if (al->idx_asm < 0) { 363 ui_helpline__puts("Only available for assembly lines."); 364 browser->b.seek(&browser->b, -offset, SEEK_CUR); 365 return false; 366 } 367 368 if (al->idx_asm < offset) 369 offset = al->idx_asm; 370 371 browser->b.nr_entries = notes->nr_asm_entries; 372 notes->options->hide_src_code = true; 373 browser->b.seek(&browser->b, -offset, SEEK_CUR); 374 browser->b.top_idx = al->idx_asm - offset; 375 browser->b.index = al->idx_asm; 376 } 377 378 return true; 379 } 380 381 static void ui_browser__init_asm_mode(struct ui_browser *browser) 382 { 383 struct annotation *notes = browser__annotation(browser); 384 ui_browser__reset_index(browser); 385 browser->nr_entries = notes->nr_asm_entries; 386 } 387 388 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) 389 390 static int sym_title(struct symbol *sym, struct map *map, char *title, 391 size_t sz, int percent_type) 392 { 393 return snprintf(title, sz, "%s %s [Percent: %s]", sym->name, map->dso->long_name, 394 percent_type_str(percent_type)); 395 } 396 397 /* 398 * This can be called from external jumps, i.e. jumps from one functon 399 * to another, like from the kernel's entry_SYSCALL_64 function to the 400 * swapgs_restore_regs_and_return_to_usermode() function. 401 * 402 * So all we check here is that dl->ops.target.sym is set, if it is, just 403 * go to that function and when exiting from its disassembly, come back 404 * to the calling function. 405 */ 406 static bool annotate_browser__callq(struct annotate_browser *browser, 407 struct perf_evsel *evsel, 408 struct hist_browser_timer *hbt) 409 { 410 struct map_symbol *ms = browser->b.priv; 411 struct disasm_line *dl = disasm_line(browser->selection); 412 struct annotation *notes; 413 char title[SYM_TITLE_MAX_SIZE]; 414 415 if (!dl->ops.target.sym) { 416 ui_helpline__puts("The called function was not found."); 417 return true; 418 } 419 420 notes = symbol__annotation(dl->ops.target.sym); 421 pthread_mutex_lock(¬es->lock); 422 423 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) { 424 pthread_mutex_unlock(¬es->lock); 425 ui__warning("Not enough memory for annotating '%s' symbol!\n", 426 dl->ops.target.sym->name); 427 return true; 428 } 429 430 pthread_mutex_unlock(¬es->lock); 431 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts); 432 sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type); 433 ui_browser__show_title(&browser->b, title); 434 return true; 435 } 436 437 static 438 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 439 s64 offset, s64 *idx) 440 { 441 struct annotation *notes = browser__annotation(&browser->b); 442 struct disasm_line *pos; 443 444 *idx = 0; 445 list_for_each_entry(pos, ¬es->src->source, al.node) { 446 if (pos->al.offset == offset) 447 return pos; 448 if (!annotation_line__filter(&pos->al, notes)) 449 ++*idx; 450 } 451 452 return NULL; 453 } 454 455 static bool annotate_browser__jump(struct annotate_browser *browser, 456 struct perf_evsel *evsel, 457 struct hist_browser_timer *hbt) 458 { 459 struct disasm_line *dl = disasm_line(browser->selection); 460 u64 offset; 461 s64 idx; 462 463 if (!ins__is_jump(&dl->ins)) 464 return false; 465 466 if (dl->ops.target.outside) { 467 annotate_browser__callq(browser, evsel, hbt); 468 return true; 469 } 470 471 offset = dl->ops.target.offset; 472 dl = annotate_browser__find_offset(browser, offset, &idx); 473 if (dl == NULL) { 474 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 475 return true; 476 } 477 478 annotate_browser__set_top(browser, &dl->al, idx); 479 480 return true; 481 } 482 483 static 484 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, 485 char *s, s64 *idx) 486 { 487 struct annotation *notes = browser__annotation(&browser->b); 488 struct annotation_line *al = browser->selection; 489 490 *idx = browser->b.index; 491 list_for_each_entry_continue(al, ¬es->src->source, node) { 492 if (annotation_line__filter(al, notes)) 493 continue; 494 495 ++*idx; 496 497 if (al->line && strstr(al->line, s) != NULL) 498 return al; 499 } 500 501 return NULL; 502 } 503 504 static bool __annotate_browser__search(struct annotate_browser *browser) 505 { 506 struct annotation_line *al; 507 s64 idx; 508 509 al = annotate_browser__find_string(browser, browser->search_bf, &idx); 510 if (al == NULL) { 511 ui_helpline__puts("String not found!"); 512 return false; 513 } 514 515 annotate_browser__set_top(browser, al, idx); 516 browser->searching_backwards = false; 517 return true; 518 } 519 520 static 521 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 522 char *s, s64 *idx) 523 { 524 struct annotation *notes = browser__annotation(&browser->b); 525 struct annotation_line *al = browser->selection; 526 527 *idx = browser->b.index; 528 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { 529 if (annotation_line__filter(al, notes)) 530 continue; 531 532 --*idx; 533 534 if (al->line && strstr(al->line, s) != NULL) 535 return al; 536 } 537 538 return NULL; 539 } 540 541 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 542 { 543 struct annotation_line *al; 544 s64 idx; 545 546 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 547 if (al == NULL) { 548 ui_helpline__puts("String not found!"); 549 return false; 550 } 551 552 annotate_browser__set_top(browser, al, idx); 553 browser->searching_backwards = true; 554 return true; 555 } 556 557 static bool annotate_browser__search_window(struct annotate_browser *browser, 558 int delay_secs) 559 { 560 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 561 "ENTER: OK, ESC: Cancel", 562 delay_secs * 2) != K_ENTER || 563 !*browser->search_bf) 564 return false; 565 566 return true; 567 } 568 569 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 570 { 571 if (annotate_browser__search_window(browser, delay_secs)) 572 return __annotate_browser__search(browser); 573 574 return false; 575 } 576 577 static bool annotate_browser__continue_search(struct annotate_browser *browser, 578 int delay_secs) 579 { 580 if (!*browser->search_bf) 581 return annotate_browser__search(browser, delay_secs); 582 583 return __annotate_browser__search(browser); 584 } 585 586 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 587 int delay_secs) 588 { 589 if (annotate_browser__search_window(browser, delay_secs)) 590 return __annotate_browser__search_reverse(browser); 591 592 return false; 593 } 594 595 static 596 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 597 int delay_secs) 598 { 599 if (!*browser->search_bf) 600 return annotate_browser__search_reverse(browser, delay_secs); 601 602 return __annotate_browser__search_reverse(browser); 603 } 604 605 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help) 606 { 607 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 608 struct map_symbol *ms = browser->priv; 609 struct symbol *sym = ms->sym; 610 char symbol_dso[SYM_TITLE_MAX_SIZE]; 611 612 if (ui_browser__show(browser, title, help) < 0) 613 return -1; 614 615 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type); 616 617 ui_browser__gotorc_title(browser, 0, 0); 618 ui_browser__set_color(browser, HE_COLORSET_ROOT); 619 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1); 620 return 0; 621 } 622 623 static void 624 switch_percent_type(struct annotation_options *opts, bool base) 625 { 626 switch (opts->percent_type) { 627 case PERCENT_HITS_LOCAL: 628 if (base) 629 opts->percent_type = PERCENT_PERIOD_LOCAL; 630 else 631 opts->percent_type = PERCENT_HITS_GLOBAL; 632 break; 633 case PERCENT_HITS_GLOBAL: 634 if (base) 635 opts->percent_type = PERCENT_PERIOD_GLOBAL; 636 else 637 opts->percent_type = PERCENT_HITS_LOCAL; 638 break; 639 case PERCENT_PERIOD_LOCAL: 640 if (base) 641 opts->percent_type = PERCENT_HITS_LOCAL; 642 else 643 opts->percent_type = PERCENT_PERIOD_GLOBAL; 644 break; 645 case PERCENT_PERIOD_GLOBAL: 646 if (base) 647 opts->percent_type = PERCENT_HITS_GLOBAL; 648 else 649 opts->percent_type = PERCENT_PERIOD_LOCAL; 650 break; 651 default: 652 WARN_ON(1); 653 } 654 } 655 656 static int annotate_browser__run(struct annotate_browser *browser, 657 struct perf_evsel *evsel, 658 struct hist_browser_timer *hbt) 659 { 660 struct rb_node *nd = NULL; 661 struct hists *hists = evsel__hists(evsel); 662 struct map_symbol *ms = browser->b.priv; 663 struct symbol *sym = ms->sym; 664 struct annotation *notes = symbol__annotation(ms->sym); 665 const char *help = "Press 'h' for help on key bindings"; 666 int delay_secs = hbt ? hbt->refresh : 0; 667 char title[256]; 668 int key; 669 670 hists__scnprintf_title(hists, title, sizeof(title)); 671 if (annotate_browser__show(&browser->b, title, help) < 0) 672 return -1; 673 674 annotate_browser__calc_percent(browser, evsel); 675 676 if (browser->curr_hot) { 677 annotate_browser__set_rb_top(browser, browser->curr_hot); 678 browser->b.navkeypressed = false; 679 } 680 681 nd = browser->curr_hot; 682 683 while (1) { 684 key = ui_browser__run(&browser->b, delay_secs); 685 686 if (delay_secs != 0) { 687 annotate_browser__calc_percent(browser, evsel); 688 /* 689 * Current line focus got out of the list of most active 690 * lines, NULL it so that if TAB|UNTAB is pressed, we 691 * move to curr_hot (current hottest line). 692 */ 693 if (nd != NULL && RB_EMPTY_NODE(nd)) 694 nd = NULL; 695 } 696 697 switch (key) { 698 case K_TIMER: 699 if (hbt) 700 hbt->timer(hbt->arg); 701 702 if (delay_secs != 0) { 703 symbol__annotate_decay_histogram(sym, evsel->idx); 704 hists__scnprintf_title(hists, title, sizeof(title)); 705 annotate_browser__show(&browser->b, title, help); 706 } 707 continue; 708 case K_TAB: 709 if (nd != NULL) { 710 nd = rb_prev(nd); 711 if (nd == NULL) 712 nd = rb_last(&browser->entries); 713 } else 714 nd = browser->curr_hot; 715 break; 716 case K_UNTAB: 717 if (nd != NULL) { 718 nd = rb_next(nd); 719 if (nd == NULL) 720 nd = rb_first(&browser->entries); 721 } else 722 nd = browser->curr_hot; 723 break; 724 case K_F1: 725 case 'h': 726 ui_browser__help_window(&browser->b, 727 "UP/DOWN/PGUP\n" 728 "PGDN/SPACE Navigate\n" 729 "q/ESC/CTRL+C Exit\n\n" 730 "ENTER Go to target\n" 731 "ESC Exit\n" 732 "H Go to hottest instruction\n" 733 "TAB/shift+TAB Cycle thru hottest instructions\n" 734 "j Toggle showing jump to target arrows\n" 735 "J Toggle showing number of jump sources on targets\n" 736 "n Search next string\n" 737 "o Toggle disassembler output/simplified view\n" 738 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n" 739 "s Toggle source code view\n" 740 "t Circulate percent, total period, samples view\n" 741 "c Show min/max cycle\n" 742 "/ Search string\n" 743 "k Toggle line numbers\n" 744 "P Print to [symbol_name].annotation file.\n" 745 "r Run available scripts\n" 746 "p Toggle percent type [local/global]\n" 747 "b Toggle percent base [period/hits]\n" 748 "? Search string backwards\n"); 749 continue; 750 case 'r': 751 { 752 script_browse(NULL); 753 continue; 754 } 755 case 'k': 756 notes->options->show_linenr = !notes->options->show_linenr; 757 break; 758 case 'H': 759 nd = browser->curr_hot; 760 break; 761 case 's': 762 if (annotate_browser__toggle_source(browser)) 763 ui_helpline__puts(help); 764 continue; 765 case 'o': 766 notes->options->use_offset = !notes->options->use_offset; 767 annotation__update_column_widths(notes); 768 continue; 769 case 'O': 770 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 771 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 772 continue; 773 case 'j': 774 notes->options->jump_arrows = !notes->options->jump_arrows; 775 continue; 776 case 'J': 777 notes->options->show_nr_jumps = !notes->options->show_nr_jumps; 778 annotation__update_column_widths(notes); 779 continue; 780 case '/': 781 if (annotate_browser__search(browser, delay_secs)) { 782 show_help: 783 ui_helpline__puts(help); 784 } 785 continue; 786 case 'n': 787 if (browser->searching_backwards ? 788 annotate_browser__continue_search_reverse(browser, delay_secs) : 789 annotate_browser__continue_search(browser, delay_secs)) 790 goto show_help; 791 continue; 792 case '?': 793 if (annotate_browser__search_reverse(browser, delay_secs)) 794 goto show_help; 795 continue; 796 case 'D': { 797 static int seq; 798 ui_helpline__pop(); 799 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 800 seq++, browser->b.nr_entries, 801 browser->b.height, 802 browser->b.index, 803 browser->b.top_idx, 804 notes->nr_asm_entries); 805 } 806 continue; 807 case K_ENTER: 808 case K_RIGHT: 809 { 810 struct disasm_line *dl = disasm_line(browser->selection); 811 812 if (browser->selection == NULL) 813 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 814 else if (browser->selection->offset == -1) 815 ui_helpline__puts("Actions are only available for assembly lines."); 816 else if (!dl->ins.ops) 817 goto show_sup_ins; 818 else if (ins__is_ret(&dl->ins)) 819 goto out; 820 else if (!(annotate_browser__jump(browser, evsel, hbt) || 821 annotate_browser__callq(browser, evsel, hbt))) { 822 show_sup_ins: 823 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 824 } 825 continue; 826 } 827 case 'P': 828 map_symbol__annotation_dump(ms, evsel, browser->opts); 829 continue; 830 case 't': 831 if (notes->options->show_total_period) { 832 notes->options->show_total_period = false; 833 notes->options->show_nr_samples = true; 834 } else if (notes->options->show_nr_samples) 835 notes->options->show_nr_samples = false; 836 else 837 notes->options->show_total_period = true; 838 annotation__update_column_widths(notes); 839 continue; 840 case 'c': 841 if (notes->options->show_minmax_cycle) 842 notes->options->show_minmax_cycle = false; 843 else 844 notes->options->show_minmax_cycle = true; 845 annotation__update_column_widths(notes); 846 continue; 847 case 'p': 848 case 'b': 849 switch_percent_type(browser->opts, key == 'b'); 850 hists__scnprintf_title(hists, title, sizeof(title)); 851 annotate_browser__show(&browser->b, title, help); 852 continue; 853 case K_LEFT: 854 case K_ESC: 855 case 'q': 856 case CTRL('c'): 857 goto out; 858 default: 859 continue; 860 } 861 862 if (nd != NULL) 863 annotate_browser__set_rb_top(browser, nd); 864 } 865 out: 866 ui_browser__hide(&browser->b); 867 return key; 868 } 869 870 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, 871 struct hist_browser_timer *hbt, 872 struct annotation_options *opts) 873 { 874 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts); 875 } 876 877 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, 878 struct hist_browser_timer *hbt, 879 struct annotation_options *opts) 880 { 881 /* reset abort key so that it can get Ctrl-C as a key */ 882 SLang_reset_tty(); 883 SLang_init_tty(0, 0, 0); 884 885 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts); 886 } 887 888 int symbol__tui_annotate(struct symbol *sym, struct map *map, 889 struct perf_evsel *evsel, 890 struct hist_browser_timer *hbt, 891 struct annotation_options *opts) 892 { 893 struct annotation *notes = symbol__annotation(sym); 894 struct map_symbol ms = { 895 .map = map, 896 .sym = sym, 897 }; 898 struct annotate_browser browser = { 899 .b = { 900 .refresh = annotate_browser__refresh, 901 .seek = ui_browser__list_head_seek, 902 .write = annotate_browser__write, 903 .filter = disasm_line__filter, 904 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 905 .priv = &ms, 906 .use_navkeypressed = true, 907 }, 908 .opts = opts, 909 }; 910 int ret = -1, err; 911 912 if (sym == NULL) 913 return -1; 914 915 if (map->dso->annotate_warned) 916 return -1; 917 918 err = symbol__annotate2(sym, map, evsel, opts, &browser.arch); 919 if (err) { 920 char msg[BUFSIZ]; 921 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); 922 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 923 goto out_free_offsets; 924 } 925 926 ui_helpline__push("Press ESC to exit"); 927 928 browser.b.width = notes->max_line_len; 929 browser.b.nr_entries = notes->nr_entries; 930 browser.b.entries = ¬es->src->source, 931 browser.b.width += 18; /* Percentage */ 932 933 if (notes->options->hide_src_code) 934 ui_browser__init_asm_mode(&browser.b); 935 936 ret = annotate_browser__run(&browser, evsel, hbt); 937 938 annotated_source__purge(notes->src); 939 940 out_free_offsets: 941 zfree(¬es->offsets); 942 return ret; 943 } 944