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