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