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