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