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