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