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 "/ Search string\n" 699 "k Toggle line numbers\n" 700 "P Print to [symbol_name].annotation file.\n" 701 "r Run available scripts\n" 702 "? Search string backwards\n"); 703 continue; 704 case 'r': 705 { 706 script_browse(NULL); 707 continue; 708 } 709 case 'k': 710 notes->options->show_linenr = !notes->options->show_linenr; 711 break; 712 case 'H': 713 nd = browser->curr_hot; 714 break; 715 case 's': 716 if (annotate_browser__toggle_source(browser)) 717 ui_helpline__puts(help); 718 continue; 719 case 'o': 720 notes->options->use_offset = !notes->options->use_offset; 721 annotation__update_column_widths(notes); 722 continue; 723 case 'O': 724 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 725 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 726 continue; 727 case 'j': 728 notes->options->jump_arrows = !notes->options->jump_arrows; 729 continue; 730 case 'J': 731 notes->options->show_nr_jumps = !notes->options->show_nr_jumps; 732 annotation__update_column_widths(notes); 733 continue; 734 case '/': 735 if (annotate_browser__search(browser, delay_secs)) { 736 show_help: 737 ui_helpline__puts(help); 738 } 739 continue; 740 case 'n': 741 if (browser->searching_backwards ? 742 annotate_browser__continue_search_reverse(browser, delay_secs) : 743 annotate_browser__continue_search(browser, delay_secs)) 744 goto show_help; 745 continue; 746 case '?': 747 if (annotate_browser__search_reverse(browser, delay_secs)) 748 goto show_help; 749 continue; 750 case 'D': { 751 static int seq; 752 ui_helpline__pop(); 753 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 754 seq++, browser->b.nr_entries, 755 browser->b.height, 756 browser->b.index, 757 browser->b.top_idx, 758 notes->nr_asm_entries); 759 } 760 continue; 761 case K_ENTER: 762 case K_RIGHT: 763 { 764 struct disasm_line *dl = disasm_line(browser->selection); 765 766 if (browser->selection == NULL) 767 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 768 else if (browser->selection->offset == -1) 769 ui_helpline__puts("Actions are only available for assembly lines."); 770 else if (!dl->ins.ops) 771 goto show_sup_ins; 772 else if (ins__is_ret(&dl->ins)) 773 goto out; 774 else if (!(annotate_browser__jump(browser, evsel, hbt) || 775 annotate_browser__callq(browser, evsel, hbt))) { 776 show_sup_ins: 777 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 778 } 779 continue; 780 } 781 case 'P': 782 map_symbol__annotation_dump(ms, evsel); 783 continue; 784 case 't': 785 if (notes->options->show_total_period) { 786 notes->options->show_total_period = false; 787 notes->options->show_nr_samples = true; 788 } else if (notes->options->show_nr_samples) 789 notes->options->show_nr_samples = false; 790 else 791 notes->options->show_total_period = true; 792 annotation__update_column_widths(notes); 793 continue; 794 case K_LEFT: 795 case K_ESC: 796 case 'q': 797 case CTRL('c'): 798 goto out; 799 default: 800 continue; 801 } 802 803 if (nd != NULL) 804 annotate_browser__set_rb_top(browser, nd); 805 } 806 out: 807 ui_browser__hide(&browser->b); 808 return key; 809 } 810 811 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, 812 struct hist_browser_timer *hbt) 813 { 814 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt); 815 } 816 817 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, 818 struct hist_browser_timer *hbt) 819 { 820 /* reset abort key so that it can get Ctrl-C as a key */ 821 SLang_reset_tty(); 822 SLang_init_tty(0, 0, 0); 823 824 return map_symbol__tui_annotate(&he->ms, evsel, hbt); 825 } 826 827 int symbol__tui_annotate(struct symbol *sym, struct map *map, 828 struct perf_evsel *evsel, 829 struct hist_browser_timer *hbt) 830 { 831 struct annotation *notes = symbol__annotation(sym); 832 struct map_symbol ms = { 833 .map = map, 834 .sym = sym, 835 }; 836 struct annotate_browser browser = { 837 .b = { 838 .refresh = annotate_browser__refresh, 839 .seek = ui_browser__list_head_seek, 840 .write = annotate_browser__write, 841 .filter = disasm_line__filter, 842 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 843 .priv = &ms, 844 .use_navkeypressed = true, 845 }, 846 }; 847 int ret = -1, err; 848 849 if (sym == NULL) 850 return -1; 851 852 if (map->dso->annotate_warned) 853 return -1; 854 855 err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch); 856 if (err) { 857 char msg[BUFSIZ]; 858 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); 859 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 860 goto out_free_offsets; 861 } 862 863 ui_helpline__push("Press ESC to exit"); 864 865 browser.b.width = notes->max_line_len; 866 browser.b.nr_entries = notes->nr_entries; 867 browser.b.entries = ¬es->src->source, 868 browser.b.width += 18; /* Percentage */ 869 870 if (notes->options->hide_src_code) 871 ui_browser__init_asm_mode(&browser.b); 872 873 ret = annotate_browser__run(&browser, evsel, hbt); 874 875 annotated_source__purge(notes->src); 876 877 out_free_offsets: 878 zfree(¬es->offsets); 879 return ret; 880 } 881