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