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 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 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 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 mutex_lock(¬es->lock); 478 479 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { 480 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 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 "f Toggle showing offsets to full address\n"); 810 continue; 811 case 'r': 812 script_browse(NULL, NULL); 813 annotate_browser__show(&browser->b, title, help); 814 continue; 815 case 'k': 816 notes->options->show_linenr = !notes->options->show_linenr; 817 continue; 818 case 'l': 819 annotate_browser__show_full_location (&browser->b); 820 continue; 821 case 'H': 822 nd = browser->curr_hot; 823 break; 824 case 's': 825 if (annotate_browser__toggle_source(browser)) 826 ui_helpline__puts(help); 827 continue; 828 case 'o': 829 notes->options->use_offset = !notes->options->use_offset; 830 annotation__update_column_widths(notes); 831 continue; 832 case 'O': 833 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 834 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 835 continue; 836 case 'j': 837 notes->options->jump_arrows = !notes->options->jump_arrows; 838 continue; 839 case 'J': 840 notes->options->show_nr_jumps = !notes->options->show_nr_jumps; 841 annotation__update_column_widths(notes); 842 continue; 843 case '/': 844 if (annotate_browser__search(browser, delay_secs)) { 845 show_help: 846 ui_helpline__puts(help); 847 } 848 continue; 849 case 'n': 850 if (browser->searching_backwards ? 851 annotate_browser__continue_search_reverse(browser, delay_secs) : 852 annotate_browser__continue_search(browser, delay_secs)) 853 goto show_help; 854 continue; 855 case '?': 856 if (annotate_browser__search_reverse(browser, delay_secs)) 857 goto show_help; 858 continue; 859 case 'D': { 860 static int seq; 861 ui_helpline__pop(); 862 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 863 seq++, browser->b.nr_entries, 864 browser->b.height, 865 browser->b.index, 866 browser->b.top_idx, 867 notes->nr_asm_entries); 868 } 869 continue; 870 case K_ENTER: 871 case K_RIGHT: 872 { 873 struct disasm_line *dl = disasm_line(browser->selection); 874 875 if (browser->selection == NULL) 876 ui_helpline__puts("Huh? No selection. Report to [email protected]"); 877 else if (browser->selection->offset == -1) 878 ui_helpline__puts("Actions are only available for assembly lines."); 879 else if (!dl->ins.ops) 880 goto show_sup_ins; 881 else if (ins__is_ret(&dl->ins)) 882 goto out; 883 else if (!(annotate_browser__jump(browser, evsel, hbt) || 884 annotate_browser__callq(browser, evsel, hbt))) { 885 show_sup_ins: 886 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 887 } 888 continue; 889 } 890 case 'P': 891 map_symbol__annotation_dump(ms, evsel, browser->opts); 892 continue; 893 case 't': 894 if (symbol_conf.show_total_period) { 895 symbol_conf.show_total_period = false; 896 symbol_conf.show_nr_samples = true; 897 } else if (symbol_conf.show_nr_samples) 898 symbol_conf.show_nr_samples = false; 899 else 900 symbol_conf.show_total_period = true; 901 annotation__update_column_widths(notes); 902 continue; 903 case 'c': 904 if (notes->options->show_minmax_cycle) 905 notes->options->show_minmax_cycle = false; 906 else 907 notes->options->show_minmax_cycle = true; 908 annotation__update_column_widths(notes); 909 continue; 910 case 'p': 911 case 'b': 912 switch_percent_type(browser->opts, key == 'b'); 913 hists__scnprintf_title(hists, title, sizeof(title)); 914 annotate_browser__show(&browser->b, title, help); 915 continue; 916 case 'f': 917 annotation__toggle_full_addr(notes, ms); 918 continue; 919 case K_LEFT: 920 case K_ESC: 921 case 'q': 922 case CTRL('c'): 923 goto out; 924 default: 925 continue; 926 } 927 928 if (nd != NULL) 929 annotate_browser__set_rb_top(browser, nd); 930 } 931 out: 932 ui_browser__hide(&browser->b); 933 return key; 934 } 935 936 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, 937 struct hist_browser_timer *hbt, 938 struct annotation_options *opts) 939 { 940 return symbol__tui_annotate(ms, evsel, hbt, opts); 941 } 942 943 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, 944 struct hist_browser_timer *hbt, 945 struct annotation_options *opts) 946 { 947 /* reset abort key so that it can get Ctrl-C as a key */ 948 SLang_reset_tty(); 949 SLang_init_tty(0, 0, 0); 950 951 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts); 952 } 953 954 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, 955 struct hist_browser_timer *hbt, 956 struct annotation_options *opts) 957 { 958 struct symbol *sym = ms->sym; 959 struct annotation *notes = symbol__annotation(sym); 960 struct annotate_browser browser = { 961 .b = { 962 .refresh = annotate_browser__refresh, 963 .seek = ui_browser__list_head_seek, 964 .write = annotate_browser__write, 965 .filter = disasm_line__filter, 966 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 967 .priv = ms, 968 .use_navkeypressed = true, 969 }, 970 .opts = opts, 971 }; 972 int ret = -1, err; 973 int not_annotated = list_empty(¬es->src->source); 974 975 if (sym == NULL) 976 return -1; 977 978 if (ms->map->dso->annotate_warned) 979 return -1; 980 981 if (not_annotated) { 982 err = symbol__annotate2(ms, evsel, opts, &browser.arch); 983 if (err) { 984 char msg[BUFSIZ]; 985 ms->map->dso->annotate_warned = true; 986 symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 987 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 988 goto out_free_offsets; 989 } 990 } 991 992 ui_helpline__push("Press ESC to exit"); 993 994 browser.b.width = notes->max_line_len; 995 browser.b.nr_entries = notes->nr_entries; 996 browser.b.entries = ¬es->src->source, 997 browser.b.width += 18; /* Percentage */ 998 999 if (notes->options->hide_src_code) 1000 ui_browser__init_asm_mode(&browser.b); 1001 1002 ret = annotate_browser__run(&browser, evsel, hbt); 1003 1004 if(not_annotated) 1005 annotated_source__purge(notes->src); 1006 1007 out_free_offsets: 1008 if(not_annotated) 1009 zfree(¬es->offsets); 1010 return ret; 1011 } 1012