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