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