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.maps = ms->maps; 434 target_ms.map = ms->map; 435 target_ms.sym = dl->ops.target.sym; 436 pthread_mutex_unlock(¬es->lock); 437 symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts); 438 sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type); 439 ui_browser__show_title(&browser->b, title); 440 return true; 441 } 442 443 static 444 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 445 s64 offset, s64 *idx) 446 { 447 struct annotation *notes = browser__annotation(&browser->b); 448 struct disasm_line *pos; 449 450 *idx = 0; 451 list_for_each_entry(pos, ¬es->src->source, al.node) { 452 if (pos->al.offset == offset) 453 return pos; 454 if (!annotation_line__filter(&pos->al, notes)) 455 ++*idx; 456 } 457 458 return NULL; 459 } 460 461 static bool annotate_browser__jump(struct annotate_browser *browser, 462 struct evsel *evsel, 463 struct hist_browser_timer *hbt) 464 { 465 struct disasm_line *dl = disasm_line(browser->selection); 466 u64 offset; 467 s64 idx; 468 469 if (!ins__is_jump(&dl->ins)) 470 return false; 471 472 if (dl->ops.target.outside) { 473 annotate_browser__callq(browser, evsel, hbt); 474 return true; 475 } 476 477 offset = dl->ops.target.offset; 478 dl = annotate_browser__find_offset(browser, offset, &idx); 479 if (dl == NULL) { 480 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 481 return true; 482 } 483 484 annotate_browser__set_top(browser, &dl->al, idx); 485 486 return true; 487 } 488 489 static 490 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, 491 char *s, s64 *idx) 492 { 493 struct annotation *notes = browser__annotation(&browser->b); 494 struct annotation_line *al = browser->selection; 495 496 *idx = browser->b.index; 497 list_for_each_entry_continue(al, ¬es->src->source, node) { 498 if (annotation_line__filter(al, notes)) 499 continue; 500 501 ++*idx; 502 503 if (al->line && strstr(al->line, s) != NULL) 504 return al; 505 } 506 507 return NULL; 508 } 509 510 static bool __annotate_browser__search(struct annotate_browser *browser) 511 { 512 struct annotation_line *al; 513 s64 idx; 514 515 al = annotate_browser__find_string(browser, browser->search_bf, &idx); 516 if (al == NULL) { 517 ui_helpline__puts("String not found!"); 518 return false; 519 } 520 521 annotate_browser__set_top(browser, al, idx); 522 browser->searching_backwards = false; 523 return true; 524 } 525 526 static 527 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 528 char *s, s64 *idx) 529 { 530 struct annotation *notes = browser__annotation(&browser->b); 531 struct annotation_line *al = browser->selection; 532 533 *idx = browser->b.index; 534 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { 535 if (annotation_line__filter(al, notes)) 536 continue; 537 538 --*idx; 539 540 if (al->line && strstr(al->line, s) != NULL) 541 return al; 542 } 543 544 return NULL; 545 } 546 547 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 548 { 549 struct annotation_line *al; 550 s64 idx; 551 552 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 553 if (al == NULL) { 554 ui_helpline__puts("String not found!"); 555 return false; 556 } 557 558 annotate_browser__set_top(browser, al, idx); 559 browser->searching_backwards = true; 560 return true; 561 } 562 563 static bool annotate_browser__search_window(struct annotate_browser *browser, 564 int delay_secs) 565 { 566 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 567 "ENTER: OK, ESC: Cancel", 568 delay_secs * 2) != K_ENTER || 569 !*browser->search_bf) 570 return false; 571 572 return true; 573 } 574 575 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 576 { 577 if (annotate_browser__search_window(browser, delay_secs)) 578 return __annotate_browser__search(browser); 579 580 return false; 581 } 582 583 static bool annotate_browser__continue_search(struct annotate_browser *browser, 584 int delay_secs) 585 { 586 if (!*browser->search_bf) 587 return annotate_browser__search(browser, delay_secs); 588 589 return __annotate_browser__search(browser); 590 } 591 592 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 593 int delay_secs) 594 { 595 if (annotate_browser__search_window(browser, delay_secs)) 596 return __annotate_browser__search_reverse(browser); 597 598 return false; 599 } 600 601 static 602 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 603 int delay_secs) 604 { 605 if (!*browser->search_bf) 606 return annotate_browser__search_reverse(browser, delay_secs); 607 608 return __annotate_browser__search_reverse(browser); 609 } 610 611 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help) 612 { 613 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 614 struct map_symbol *ms = browser->priv; 615 struct symbol *sym = ms->sym; 616 char symbol_dso[SYM_TITLE_MAX_SIZE]; 617 618 if (ui_browser__show(browser, title, help) < 0) 619 return -1; 620 621 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type); 622 623 ui_browser__gotorc_title(browser, 0, 0); 624 ui_browser__set_color(browser, HE_COLORSET_ROOT); 625 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1); 626 return 0; 627 } 628 629 static void 630 switch_percent_type(struct annotation_options *opts, bool base) 631 { 632 switch (opts->percent_type) { 633 case PERCENT_HITS_LOCAL: 634 if (base) 635 opts->percent_type = PERCENT_PERIOD_LOCAL; 636 else 637 opts->percent_type = PERCENT_HITS_GLOBAL; 638 break; 639 case PERCENT_HITS_GLOBAL: 640 if (base) 641 opts->percent_type = PERCENT_PERIOD_GLOBAL; 642 else 643 opts->percent_type = PERCENT_HITS_LOCAL; 644 break; 645 case PERCENT_PERIOD_LOCAL: 646 if (base) 647 opts->percent_type = PERCENT_HITS_LOCAL; 648 else 649 opts->percent_type = PERCENT_PERIOD_GLOBAL; 650 break; 651 case PERCENT_PERIOD_GLOBAL: 652 if (base) 653 opts->percent_type = PERCENT_HITS_GLOBAL; 654 else 655 opts->percent_type = PERCENT_PERIOD_LOCAL; 656 break; 657 default: 658 WARN_ON(1); 659 } 660 } 661 662 static int annotate_browser__run(struct annotate_browser *browser, 663 struct evsel *evsel, 664 struct hist_browser_timer *hbt) 665 { 666 struct rb_node *nd = NULL; 667 struct hists *hists = evsel__hists(evsel); 668 struct map_symbol *ms = browser->b.priv; 669 struct symbol *sym = ms->sym; 670 struct annotation *notes = symbol__annotation(ms->sym); 671 const char *help = "Press 'h' for help on key bindings"; 672 int delay_secs = hbt ? hbt->refresh : 0; 673 char title[256]; 674 int key; 675 676 hists__scnprintf_title(hists, title, sizeof(title)); 677 if (annotate_browser__show(&browser->b, title, help) < 0) 678 return -1; 679 680 annotate_browser__calc_percent(browser, evsel); 681 682 if (browser->curr_hot) { 683 annotate_browser__set_rb_top(browser, browser->curr_hot); 684 browser->b.navkeypressed = false; 685 } 686 687 nd = browser->curr_hot; 688 689 while (1) { 690 key = ui_browser__run(&browser->b, delay_secs); 691 692 if (delay_secs != 0) { 693 annotate_browser__calc_percent(browser, evsel); 694 /* 695 * Current line focus got out of the list of most active 696 * lines, NULL it so that if TAB|UNTAB is pressed, we 697 * move to curr_hot (current hottest line). 698 */ 699 if (nd != NULL && RB_EMPTY_NODE(nd)) 700 nd = NULL; 701 } 702 703 switch (key) { 704 case K_TIMER: 705 if (hbt) 706 hbt->timer(hbt->arg); 707 708 if (delay_secs != 0) { 709 symbol__annotate_decay_histogram(sym, evsel->idx); 710 hists__scnprintf_title(hists, title, sizeof(title)); 711 annotate_browser__show(&browser->b, title, help); 712 } 713 continue; 714 case K_TAB: 715 if (nd != NULL) { 716 nd = rb_prev(nd); 717 if (nd == NULL) 718 nd = rb_last(&browser->entries); 719 } else 720 nd = browser->curr_hot; 721 break; 722 case K_UNTAB: 723 if (nd != NULL) { 724 nd = rb_next(nd); 725 if (nd == NULL) 726 nd = rb_first(&browser->entries); 727 } else 728 nd = browser->curr_hot; 729 break; 730 case K_F1: 731 case 'h': 732 ui_browser__help_window(&browser->b, 733 "UP/DOWN/PGUP\n" 734 "PGDN/SPACE Navigate\n" 735 "q/ESC/CTRL+C Exit\n\n" 736 "ENTER Go to target\n" 737 "ESC Exit\n" 738 "H Go to hottest instruction\n" 739 "TAB/shift+TAB Cycle thru hottest instructions\n" 740 "j Toggle showing jump to target arrows\n" 741 "J Toggle showing number of jump sources on targets\n" 742 "n Search next string\n" 743 "o Toggle disassembler output/simplified view\n" 744 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n" 745 "s Toggle source code view\n" 746 "t Circulate percent, total period, samples view\n" 747 "c Show min/max cycle\n" 748 "/ Search string\n" 749 "k Toggle line numbers\n" 750 "P Print to [symbol_name].annotation file.\n" 751 "r Run available scripts\n" 752 "p Toggle percent type [local/global]\n" 753 "b Toggle percent base [period/hits]\n" 754 "? Search string backwards\n"); 755 continue; 756 case 'r': 757 { 758 script_browse(NULL, NULL); 759 continue; 760 } 761 case 'k': 762 notes->options->show_linenr = !notes->options->show_linenr; 763 break; 764 case 'H': 765 nd = browser->curr_hot; 766 break; 767 case 's': 768 if (annotate_browser__toggle_source(browser)) 769 ui_helpline__puts(help); 770 continue; 771 case 'o': 772 notes->options->use_offset = !notes->options->use_offset; 773 annotation__update_column_widths(notes); 774 continue; 775 case 'O': 776 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 777 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 778 continue; 779 case 'j': 780 notes->options->jump_arrows = !notes->options->jump_arrows; 781 continue; 782 case 'J': 783 notes->options->show_nr_jumps = !notes->options->show_nr_jumps; 784 annotation__update_column_widths(notes); 785 continue; 786 case '/': 787 if (annotate_browser__search(browser, delay_secs)) { 788 show_help: 789 ui_helpline__puts(help); 790 } 791 continue; 792 case 'n': 793 if (browser->searching_backwards ? 794 annotate_browser__continue_search_reverse(browser, delay_secs) : 795 annotate_browser__continue_search(browser, delay_secs)) 796 goto show_help; 797 continue; 798 case '?': 799 if (annotate_browser__search_reverse(browser, delay_secs)) 800 goto show_help; 801 continue; 802 case 'D': { 803 static int seq; 804 ui_helpline__pop(); 805 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 806 seq++, browser->b.nr_entries, 807 browser->b.height, 808 browser->b.index, 809 browser->b.top_idx, 810 notes->nr_asm_entries); 811 } 812 continue; 813 case K_ENTER: 814 case K_RIGHT: 815 { 816 struct disasm_line *dl = disasm_line(browser->selection); 817 818 if (browser->selection == NULL) 819 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 820 else if (browser->selection->offset == -1) 821 ui_helpline__puts("Actions are only available for assembly lines."); 822 else if (!dl->ins.ops) 823 goto show_sup_ins; 824 else if (ins__is_ret(&dl->ins)) 825 goto out; 826 else if (!(annotate_browser__jump(browser, evsel, hbt) || 827 annotate_browser__callq(browser, evsel, hbt))) { 828 show_sup_ins: 829 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 830 } 831 continue; 832 } 833 case 'P': 834 map_symbol__annotation_dump(ms, evsel, browser->opts); 835 continue; 836 case 't': 837 if (notes->options->show_total_period) { 838 notes->options->show_total_period = false; 839 notes->options->show_nr_samples = true; 840 } else if (notes->options->show_nr_samples) 841 notes->options->show_nr_samples = false; 842 else 843 notes->options->show_total_period = true; 844 annotation__update_column_widths(notes); 845 continue; 846 case 'c': 847 if (notes->options->show_minmax_cycle) 848 notes->options->show_minmax_cycle = false; 849 else 850 notes->options->show_minmax_cycle = true; 851 annotation__update_column_widths(notes); 852 continue; 853 case 'p': 854 case 'b': 855 switch_percent_type(browser->opts, key == 'b'); 856 hists__scnprintf_title(hists, title, sizeof(title)); 857 annotate_browser__show(&browser->b, title, help); 858 continue; 859 case K_LEFT: 860 case K_ESC: 861 case 'q': 862 case CTRL('c'): 863 goto out; 864 default: 865 continue; 866 } 867 868 if (nd != NULL) 869 annotate_browser__set_rb_top(browser, nd); 870 } 871 out: 872 ui_browser__hide(&browser->b); 873 return key; 874 } 875 876 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, 877 struct hist_browser_timer *hbt, 878 struct annotation_options *opts) 879 { 880 return symbol__tui_annotate(ms, evsel, hbt, opts); 881 } 882 883 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, 884 struct hist_browser_timer *hbt, 885 struct annotation_options *opts) 886 { 887 /* reset abort key so that it can get Ctrl-C as a key */ 888 SLang_reset_tty(); 889 SLang_init_tty(0, 0, 0); 890 891 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts); 892 } 893 894 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, 895 struct hist_browser_timer *hbt, 896 struct annotation_options *opts) 897 { 898 struct symbol *sym = ms->sym; 899 struct annotation *notes = symbol__annotation(sym); 900 struct annotate_browser browser = { 901 .b = { 902 .refresh = annotate_browser__refresh, 903 .seek = ui_browser__list_head_seek, 904 .write = annotate_browser__write, 905 .filter = disasm_line__filter, 906 .extra_title_lines = 1, /* for hists__scnprintf_title() */ 907 .priv = ms, 908 .use_navkeypressed = true, 909 }, 910 .opts = opts, 911 }; 912 int ret = -1, err; 913 914 if (sym == NULL) 915 return -1; 916 917 if (ms->map->dso->annotate_warned) 918 return -1; 919 920 err = symbol__annotate2(ms, evsel, opts, &browser.arch); 921 if (err) { 922 char msg[BUFSIZ]; 923 symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 924 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 925 goto out_free_offsets; 926 } 927 928 ui_helpline__push("Press ESC to exit"); 929 930 browser.b.width = notes->max_line_len; 931 browser.b.nr_entries = notes->nr_entries; 932 browser.b.entries = ¬es->src->source, 933 browser.b.width += 18; /* Percentage */ 934 935 if (notes->options->hide_src_code) 936 ui_browser__init_asm_mode(&browser.b); 937 938 ret = annotate_browser__run(&browser, evsel, hbt); 939 940 annotated_source__purge(notes->src); 941 942 out_free_offsets: 943 zfree(¬es->offsets); 944 return ret; 945 } 946