1 #include "../../util/util.h" 2 #include "../browser.h" 3 #include "../helpline.h" 4 #include "../libslang.h" 5 #include "../ui.h" 6 #include "../util.h" 7 #include "../../util/annotate.h" 8 #include "../../util/hist.h" 9 #include "../../util/sort.h" 10 #include "../../util/symbol.h" 11 #include <pthread.h> 12 #include <newt.h> 13 14 struct browser_disasm_line { 15 struct rb_node rb_node; 16 double percent; 17 u32 idx; 18 int idx_asm; 19 int jump_sources; 20 }; 21 22 static struct annotate_browser_opt { 23 bool hide_src_code, 24 use_offset, 25 jump_arrows, 26 show_nr_jumps; 27 } annotate_browser__opts = { 28 .use_offset = true, 29 .jump_arrows = true, 30 }; 31 32 struct annotate_browser { 33 struct ui_browser b; 34 struct rb_root entries; 35 struct rb_node *curr_hot; 36 struct disasm_line *selection; 37 struct disasm_line **offsets; 38 u64 start; 39 int nr_asm_entries; 40 int nr_entries; 41 int max_jump_sources; 42 int nr_jumps; 43 bool searching_backwards; 44 u8 addr_width; 45 u8 jumps_width; 46 u8 target_width; 47 u8 min_addr_width; 48 u8 max_addr_width; 49 char search_bf[128]; 50 }; 51 52 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) 53 { 54 return (struct browser_disasm_line *)(dl + 1); 55 } 56 57 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, 58 void *entry) 59 { 60 if (annotate_browser__opts.hide_src_code) { 61 struct disasm_line *dl = list_entry(entry, struct disasm_line, node); 62 return dl->offset == -1; 63 } 64 65 return false; 66 } 67 68 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, 69 int nr, bool current) 70 { 71 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) 72 return HE_COLORSET_SELECTED; 73 if (nr == browser->max_jump_sources) 74 return HE_COLORSET_TOP; 75 if (nr > 1) 76 return HE_COLORSET_MEDIUM; 77 return HE_COLORSET_NORMAL; 78 } 79 80 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, 81 int nr, bool current) 82 { 83 int color = annotate_browser__jumps_percent_color(browser, nr, current); 84 return ui_browser__set_color(&browser->b, color); 85 } 86 87 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) 88 { 89 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 90 struct disasm_line *dl = list_entry(entry, struct disasm_line, node); 91 struct browser_disasm_line *bdl = disasm_line__browser(dl); 92 bool current_entry = ui_browser__is_current_entry(browser, row); 93 bool change_color = (!annotate_browser__opts.hide_src_code && 94 (!current_entry || (browser->use_navkeypressed && 95 !browser->navkeypressed))); 96 int width = browser->width, printed; 97 char bf[256]; 98 99 if (dl->offset != -1 && bdl->percent != 0.0) { 100 ui_browser__set_percent_color(browser, bdl->percent, current_entry); 101 slsmg_printf("%6.2f ", bdl->percent); 102 } else { 103 ui_browser__set_percent_color(browser, 0, current_entry); 104 slsmg_write_nstring(" ", 7); 105 } 106 107 SLsmg_write_char(' '); 108 109 /* The scroll bar isn't being used */ 110 if (!browser->navkeypressed) 111 width += 1; 112 113 if (!*dl->line) 114 slsmg_write_nstring(" ", width - 7); 115 else if (dl->offset == -1) { 116 printed = scnprintf(bf, sizeof(bf), "%*s ", 117 ab->addr_width, " "); 118 slsmg_write_nstring(bf, printed); 119 slsmg_write_nstring(dl->line, width - printed - 6); 120 } else { 121 u64 addr = dl->offset; 122 int color = -1; 123 124 if (!annotate_browser__opts.use_offset) 125 addr += ab->start; 126 127 if (!annotate_browser__opts.use_offset) { 128 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); 129 } else { 130 if (bdl->jump_sources) { 131 if (annotate_browser__opts.show_nr_jumps) { 132 int prev; 133 printed = scnprintf(bf, sizeof(bf), "%*d ", 134 ab->jumps_width, 135 bdl->jump_sources); 136 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources, 137 current_entry); 138 slsmg_write_nstring(bf, printed); 139 ui_browser__set_color(browser, prev); 140 } 141 142 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", 143 ab->target_width, addr); 144 } else { 145 printed = scnprintf(bf, sizeof(bf), "%*s ", 146 ab->addr_width, " "); 147 } 148 } 149 150 if (change_color) 151 color = ui_browser__set_color(browser, HE_COLORSET_ADDR); 152 slsmg_write_nstring(bf, printed); 153 if (change_color) 154 ui_browser__set_color(browser, color); 155 if (dl->ins && dl->ins->ops->scnprintf) { 156 if (ins__is_jump(dl->ins)) { 157 bool fwd = dl->ops.target.offset > (u64)dl->offset; 158 159 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : 160 SLSMG_UARROW_CHAR); 161 SLsmg_write_char(' '); 162 } else if (ins__is_call(dl->ins)) { 163 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); 164 SLsmg_write_char(' '); 165 } else { 166 slsmg_write_nstring(" ", 2); 167 } 168 } else { 169 if (strcmp(dl->name, "retq")) { 170 slsmg_write_nstring(" ", 2); 171 } else { 172 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); 173 SLsmg_write_char(' '); 174 } 175 } 176 177 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); 178 slsmg_write_nstring(bf, width - 10 - printed); 179 } 180 181 if (current_entry) 182 ab->selection = dl; 183 } 184 185 static void annotate_browser__draw_current_jump(struct ui_browser *browser) 186 { 187 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 188 struct disasm_line *cursor = ab->selection, *target; 189 struct browser_disasm_line *btarget, *bcursor; 190 unsigned int from, to; 191 struct map_symbol *ms = ab->b.priv; 192 struct symbol *sym = ms->sym; 193 194 /* PLT symbols contain external offsets */ 195 if (strstr(sym->name, "@plt")) 196 return; 197 198 if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || 199 !disasm_line__has_offset(cursor)) 200 return; 201 202 target = ab->offsets[cursor->ops.target.offset]; 203 if (!target) 204 return; 205 206 bcursor = disasm_line__browser(cursor); 207 btarget = disasm_line__browser(target); 208 209 if (annotate_browser__opts.hide_src_code) { 210 from = bcursor->idx_asm; 211 to = btarget->idx_asm; 212 } else { 213 from = (u64)bcursor->idx; 214 to = (u64)btarget->idx; 215 } 216 217 ui_browser__set_color(browser, HE_COLORSET_CODE); 218 __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); 219 } 220 221 static unsigned int annotate_browser__refresh(struct ui_browser *browser) 222 { 223 int ret = ui_browser__list_head_refresh(browser); 224 225 if (annotate_browser__opts.jump_arrows) 226 annotate_browser__draw_current_jump(browser); 227 228 ui_browser__set_color(browser, HE_COLORSET_NORMAL); 229 __ui_browser__vline(browser, 7, 0, browser->height - 1); 230 return ret; 231 } 232 233 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) 234 { 235 double percent = 0.0; 236 237 if (dl->offset != -1) { 238 int len = sym->end - sym->start; 239 unsigned int hits = 0; 240 struct annotation *notes = symbol__annotation(sym); 241 struct source_line *src_line = notes->src->lines; 242 struct sym_hist *h = annotation__histogram(notes, evidx); 243 s64 offset = dl->offset; 244 struct disasm_line *next; 245 246 next = disasm__get_next_ip_line(¬es->src->source, dl); 247 while (offset < (s64)len && 248 (next == NULL || offset < next->offset)) { 249 if (src_line) { 250 percent += src_line[offset].percent; 251 } else 252 hits += h->addr[offset]; 253 254 ++offset; 255 } 256 /* 257 * If the percentage wasn't already calculated in 258 * symbol__get_source_line, do it now: 259 */ 260 if (src_line == NULL && h->sum) 261 percent = 100.0 * hits / h->sum; 262 } 263 264 return percent; 265 } 266 267 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) 268 { 269 struct rb_node **p = &root->rb_node; 270 struct rb_node *parent = NULL; 271 struct browser_disasm_line *l; 272 273 while (*p != NULL) { 274 parent = *p; 275 l = rb_entry(parent, struct browser_disasm_line, rb_node); 276 if (bdl->percent < l->percent) 277 p = &(*p)->rb_left; 278 else 279 p = &(*p)->rb_right; 280 } 281 rb_link_node(&bdl->rb_node, parent, p); 282 rb_insert_color(&bdl->rb_node, root); 283 } 284 285 static void annotate_browser__set_top(struct annotate_browser *browser, 286 struct disasm_line *pos, u32 idx) 287 { 288 unsigned back; 289 290 ui_browser__refresh_dimensions(&browser->b); 291 back = browser->b.height / 2; 292 browser->b.top_idx = browser->b.index = idx; 293 294 while (browser->b.top_idx != 0 && back != 0) { 295 pos = list_entry(pos->node.prev, struct disasm_line, node); 296 297 if (disasm_line__filter(&browser->b, &pos->node)) 298 continue; 299 300 --browser->b.top_idx; 301 --back; 302 } 303 304 browser->b.top = pos; 305 browser->b.navkeypressed = true; 306 } 307 308 static void annotate_browser__set_rb_top(struct annotate_browser *browser, 309 struct rb_node *nd) 310 { 311 struct browser_disasm_line *bpos; 312 struct disasm_line *pos; 313 u32 idx; 314 315 bpos = rb_entry(nd, struct browser_disasm_line, rb_node); 316 pos = ((struct disasm_line *)bpos) - 1; 317 idx = bpos->idx; 318 if (annotate_browser__opts.hide_src_code) 319 idx = bpos->idx_asm; 320 annotate_browser__set_top(browser, pos, idx); 321 browser->curr_hot = nd; 322 } 323 324 static void annotate_browser__calc_percent(struct annotate_browser *browser, 325 int evidx) 326 { 327 struct map_symbol *ms = browser->b.priv; 328 struct symbol *sym = ms->sym; 329 struct annotation *notes = symbol__annotation(sym); 330 struct disasm_line *pos; 331 332 browser->entries = RB_ROOT; 333 334 pthread_mutex_lock(¬es->lock); 335 336 list_for_each_entry(pos, ¬es->src->source, node) { 337 struct browser_disasm_line *bpos = disasm_line__browser(pos); 338 bpos->percent = disasm_line__calc_percent(pos, sym, evidx); 339 if (bpos->percent < 0.01) { 340 RB_CLEAR_NODE(&bpos->rb_node); 341 continue; 342 } 343 disasm_rb_tree__insert(&browser->entries, bpos); 344 } 345 pthread_mutex_unlock(¬es->lock); 346 347 browser->curr_hot = rb_last(&browser->entries); 348 } 349 350 static bool annotate_browser__toggle_source(struct annotate_browser *browser) 351 { 352 struct disasm_line *dl; 353 struct browser_disasm_line *bdl; 354 off_t offset = browser->b.index - browser->b.top_idx; 355 356 browser->b.seek(&browser->b, offset, SEEK_CUR); 357 dl = list_entry(browser->b.top, struct disasm_line, node); 358 bdl = disasm_line__browser(dl); 359 360 if (annotate_browser__opts.hide_src_code) { 361 if (bdl->idx_asm < offset) 362 offset = bdl->idx; 363 364 browser->b.nr_entries = browser->nr_entries; 365 annotate_browser__opts.hide_src_code = false; 366 browser->b.seek(&browser->b, -offset, SEEK_CUR); 367 browser->b.top_idx = bdl->idx - offset; 368 browser->b.index = bdl->idx; 369 } else { 370 if (bdl->idx_asm < 0) { 371 ui_helpline__puts("Only available for assembly lines."); 372 browser->b.seek(&browser->b, -offset, SEEK_CUR); 373 return false; 374 } 375 376 if (bdl->idx_asm < offset) 377 offset = bdl->idx_asm; 378 379 browser->b.nr_entries = browser->nr_asm_entries; 380 annotate_browser__opts.hide_src_code = true; 381 browser->b.seek(&browser->b, -offset, SEEK_CUR); 382 browser->b.top_idx = bdl->idx_asm - offset; 383 browser->b.index = bdl->idx_asm; 384 } 385 386 return true; 387 } 388 389 static void annotate_browser__init_asm_mode(struct annotate_browser *browser) 390 { 391 ui_browser__reset_index(&browser->b); 392 browser->b.nr_entries = browser->nr_asm_entries; 393 } 394 395 static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, 396 struct hist_browser_timer *hbt) 397 { 398 struct map_symbol *ms = browser->b.priv; 399 struct disasm_line *dl = browser->selection; 400 struct symbol *sym = ms->sym; 401 struct annotation *notes; 402 struct symbol *target; 403 u64 ip; 404 405 if (!ins__is_call(dl->ins)) 406 return false; 407 408 ip = ms->map->map_ip(ms->map, dl->ops.target.addr); 409 target = map__find_symbol(ms->map, ip, NULL); 410 if (target == NULL) { 411 ui_helpline__puts("The called function was not found."); 412 return true; 413 } 414 415 notes = symbol__annotation(target); 416 pthread_mutex_lock(¬es->lock); 417 418 if (notes->src == NULL && symbol__alloc_hist(target) < 0) { 419 pthread_mutex_unlock(¬es->lock); 420 ui__warning("Not enough memory for annotating '%s' symbol!\n", 421 target->name); 422 return true; 423 } 424 425 pthread_mutex_unlock(¬es->lock); 426 symbol__tui_annotate(target, ms->map, evidx, hbt); 427 ui_browser__show_title(&browser->b, sym->name); 428 return true; 429 } 430 431 static 432 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, 433 s64 offset, s64 *idx) 434 { 435 struct map_symbol *ms = browser->b.priv; 436 struct symbol *sym = ms->sym; 437 struct annotation *notes = symbol__annotation(sym); 438 struct disasm_line *pos; 439 440 *idx = 0; 441 list_for_each_entry(pos, ¬es->src->source, node) { 442 if (pos->offset == offset) 443 return pos; 444 if (!disasm_line__filter(&browser->b, &pos->node)) 445 ++*idx; 446 } 447 448 return NULL; 449 } 450 451 static bool annotate_browser__jump(struct annotate_browser *browser) 452 { 453 struct disasm_line *dl = browser->selection; 454 s64 idx; 455 456 if (!ins__is_jump(dl->ins)) 457 return false; 458 459 dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); 460 if (dl == NULL) { 461 ui_helpline__puts("Invallid jump offset"); 462 return true; 463 } 464 465 annotate_browser__set_top(browser, dl, idx); 466 467 return true; 468 } 469 470 static 471 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, 472 char *s, s64 *idx) 473 { 474 struct map_symbol *ms = browser->b.priv; 475 struct symbol *sym = ms->sym; 476 struct annotation *notes = symbol__annotation(sym); 477 struct disasm_line *pos = browser->selection; 478 479 *idx = browser->b.index; 480 list_for_each_entry_continue(pos, ¬es->src->source, node) { 481 if (disasm_line__filter(&browser->b, &pos->node)) 482 continue; 483 484 ++*idx; 485 486 if (pos->line && strstr(pos->line, s) != NULL) 487 return pos; 488 } 489 490 return NULL; 491 } 492 493 static bool __annotate_browser__search(struct annotate_browser *browser) 494 { 495 struct disasm_line *dl; 496 s64 idx; 497 498 dl = annotate_browser__find_string(browser, browser->search_bf, &idx); 499 if (dl == NULL) { 500 ui_helpline__puts("String not found!"); 501 return false; 502 } 503 504 annotate_browser__set_top(browser, dl, idx); 505 browser->searching_backwards = false; 506 return true; 507 } 508 509 static 510 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 511 char *s, s64 *idx) 512 { 513 struct map_symbol *ms = browser->b.priv; 514 struct symbol *sym = ms->sym; 515 struct annotation *notes = symbol__annotation(sym); 516 struct disasm_line *pos = browser->selection; 517 518 *idx = browser->b.index; 519 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { 520 if (disasm_line__filter(&browser->b, &pos->node)) 521 continue; 522 523 --*idx; 524 525 if (pos->line && strstr(pos->line, s) != NULL) 526 return pos; 527 } 528 529 return NULL; 530 } 531 532 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 533 { 534 struct disasm_line *dl; 535 s64 idx; 536 537 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 538 if (dl == NULL) { 539 ui_helpline__puts("String not found!"); 540 return false; 541 } 542 543 annotate_browser__set_top(browser, dl, idx); 544 browser->searching_backwards = true; 545 return true; 546 } 547 548 static bool annotate_browser__search_window(struct annotate_browser *browser, 549 int delay_secs) 550 { 551 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 552 "ENTER: OK, ESC: Cancel", 553 delay_secs * 2) != K_ENTER || 554 !*browser->search_bf) 555 return false; 556 557 return true; 558 } 559 560 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 561 { 562 if (annotate_browser__search_window(browser, delay_secs)) 563 return __annotate_browser__search(browser); 564 565 return false; 566 } 567 568 static bool annotate_browser__continue_search(struct annotate_browser *browser, 569 int delay_secs) 570 { 571 if (!*browser->search_bf) 572 return annotate_browser__search(browser, delay_secs); 573 574 return __annotate_browser__search(browser); 575 } 576 577 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 578 int delay_secs) 579 { 580 if (annotate_browser__search_window(browser, delay_secs)) 581 return __annotate_browser__search_reverse(browser); 582 583 return false; 584 } 585 586 static 587 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 588 int delay_secs) 589 { 590 if (!*browser->search_bf) 591 return annotate_browser__search_reverse(browser, delay_secs); 592 593 return __annotate_browser__search_reverse(browser); 594 } 595 596 static void annotate_browser__update_addr_width(struct annotate_browser *browser) 597 { 598 if (annotate_browser__opts.use_offset) 599 browser->target_width = browser->min_addr_width; 600 else 601 browser->target_width = browser->max_addr_width; 602 603 browser->addr_width = browser->target_width; 604 605 if (annotate_browser__opts.show_nr_jumps) 606 browser->addr_width += browser->jumps_width + 1; 607 } 608 609 static int annotate_browser__run(struct annotate_browser *browser, int evidx, 610 struct hist_browser_timer *hbt) 611 { 612 struct rb_node *nd = NULL; 613 struct map_symbol *ms = browser->b.priv; 614 struct symbol *sym = ms->sym; 615 const char *help = "Press 'h' for help on key bindings"; 616 int delay_secs = hbt ? hbt->refresh : 0; 617 int key; 618 619 if (ui_browser__show(&browser->b, sym->name, help) < 0) 620 return -1; 621 622 annotate_browser__calc_percent(browser, evidx); 623 624 if (browser->curr_hot) { 625 annotate_browser__set_rb_top(browser, browser->curr_hot); 626 browser->b.navkeypressed = false; 627 } 628 629 nd = browser->curr_hot; 630 631 while (1) { 632 key = ui_browser__run(&browser->b, delay_secs); 633 634 if (delay_secs != 0) { 635 annotate_browser__calc_percent(browser, evidx); 636 /* 637 * Current line focus got out of the list of most active 638 * lines, NULL it so that if TAB|UNTAB is pressed, we 639 * move to curr_hot (current hottest line). 640 */ 641 if (nd != NULL && RB_EMPTY_NODE(nd)) 642 nd = NULL; 643 } 644 645 switch (key) { 646 case K_TIMER: 647 if (hbt) 648 hbt->timer(hbt->arg); 649 650 if (delay_secs != 0) 651 symbol__annotate_decay_histogram(sym, evidx); 652 continue; 653 case K_TAB: 654 if (nd != NULL) { 655 nd = rb_prev(nd); 656 if (nd == NULL) 657 nd = rb_last(&browser->entries); 658 } else 659 nd = browser->curr_hot; 660 break; 661 case K_UNTAB: 662 if (nd != NULL) 663 nd = rb_next(nd); 664 if (nd == NULL) 665 nd = rb_first(&browser->entries); 666 else 667 nd = browser->curr_hot; 668 break; 669 case K_F1: 670 case 'h': 671 ui_browser__help_window(&browser->b, 672 "UP/DOWN/PGUP\n" 673 "PGDN/SPACE Navigate\n" 674 "q/ESC/CTRL+C Exit\n\n" 675 "-> Go to target\n" 676 "<- Exit\n" 677 "H Cycle thru hottest instructions\n" 678 "j Toggle showing jump to target arrows\n" 679 "J Toggle showing number of jump sources on targets\n" 680 "n Search next string\n" 681 "o Toggle disassembler output/simplified view\n" 682 "s Toggle source code view\n" 683 "/ Search string\n" 684 "r Run available scripts\n" 685 "? Search previous string\n"); 686 continue; 687 case 'r': 688 { 689 script_browse(NULL); 690 continue; 691 } 692 case 'H': 693 nd = browser->curr_hot; 694 break; 695 case 's': 696 if (annotate_browser__toggle_source(browser)) 697 ui_helpline__puts(help); 698 continue; 699 case 'o': 700 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; 701 annotate_browser__update_addr_width(browser); 702 continue; 703 case 'j': 704 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; 705 continue; 706 case 'J': 707 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; 708 annotate_browser__update_addr_width(browser); 709 continue; 710 case '/': 711 if (annotate_browser__search(browser, delay_secs)) { 712 show_help: 713 ui_helpline__puts(help); 714 } 715 continue; 716 case 'n': 717 if (browser->searching_backwards ? 718 annotate_browser__continue_search_reverse(browser, delay_secs) : 719 annotate_browser__continue_search(browser, delay_secs)) 720 goto show_help; 721 continue; 722 case '?': 723 if (annotate_browser__search_reverse(browser, delay_secs)) 724 goto show_help; 725 continue; 726 case 'D': { 727 static int seq; 728 ui_helpline__pop(); 729 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 730 seq++, browser->b.nr_entries, 731 browser->b.height, 732 browser->b.index, 733 browser->b.top_idx, 734 browser->nr_asm_entries); 735 } 736 continue; 737 case K_ENTER: 738 case K_RIGHT: 739 if (browser->selection == NULL) 740 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 741 else if (browser->selection->offset == -1) 742 ui_helpline__puts("Actions are only available for assembly lines."); 743 else if (!browser->selection->ins) { 744 if (strcmp(browser->selection->name, "retq")) 745 goto show_sup_ins; 746 goto out; 747 } else if (!(annotate_browser__jump(browser) || 748 annotate_browser__callq(browser, evidx, hbt))) { 749 show_sup_ins: 750 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); 751 } 752 continue; 753 case K_LEFT: 754 case K_ESC: 755 case 'q': 756 case CTRL('c'): 757 goto out; 758 default: 759 continue; 760 } 761 762 if (nd != NULL) 763 annotate_browser__set_rb_top(browser, nd); 764 } 765 out: 766 ui_browser__hide(&browser->b); 767 return key; 768 } 769 770 int hist_entry__tui_annotate(struct hist_entry *he, int evidx, 771 struct hist_browser_timer *hbt) 772 { 773 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); 774 } 775 776 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, 777 size_t size) 778 { 779 u64 offset; 780 struct map_symbol *ms = browser->b.priv; 781 struct symbol *sym = ms->sym; 782 783 /* PLT symbols contain external offsets */ 784 if (strstr(sym->name, "@plt")) 785 return; 786 787 for (offset = 0; offset < size; ++offset) { 788 struct disasm_line *dl = browser->offsets[offset], *dlt; 789 struct browser_disasm_line *bdlt; 790 791 if (!dl || !dl->ins || !ins__is_jump(dl->ins) || 792 !disasm_line__has_offset(dl)) 793 continue; 794 795 if (dl->ops.target.offset >= size) { 796 ui__error("jump to after symbol!\n" 797 "size: %zx, jump target: %" PRIx64, 798 size, dl->ops.target.offset); 799 continue; 800 } 801 802 dlt = browser->offsets[dl->ops.target.offset]; 803 /* 804 * FIXME: Oops, no jump target? Buggy disassembler? Or do we 805 * have to adjust to the previous offset? 806 */ 807 if (dlt == NULL) 808 continue; 809 810 bdlt = disasm_line__browser(dlt); 811 if (++bdlt->jump_sources > browser->max_jump_sources) 812 browser->max_jump_sources = bdlt->jump_sources; 813 814 ++browser->nr_jumps; 815 } 816 817 } 818 819 static inline int width_jumps(int n) 820 { 821 if (n >= 100) 822 return 5; 823 if (n / 10) 824 return 2; 825 return 1; 826 } 827 828 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, 829 struct hist_browser_timer *hbt) 830 { 831 struct disasm_line *pos, *n; 832 struct annotation *notes; 833 size_t size; 834 struct map_symbol ms = { 835 .map = map, 836 .sym = sym, 837 }; 838 struct annotate_browser browser = { 839 .b = { 840 .refresh = annotate_browser__refresh, 841 .seek = ui_browser__list_head_seek, 842 .write = annotate_browser__write, 843 .filter = disasm_line__filter, 844 .priv = &ms, 845 .use_navkeypressed = true, 846 }, 847 }; 848 int ret = -1; 849 850 if (sym == NULL) 851 return -1; 852 853 size = symbol__size(sym); 854 855 if (map->dso->annotate_warned) 856 return -1; 857 858 browser.offsets = zalloc(size * sizeof(struct disasm_line *)); 859 if (browser.offsets == NULL) { 860 ui__error("Not enough memory!"); 861 return -1; 862 } 863 864 if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { 865 ui__error("%s", ui_helpline__last_msg); 866 goto out_free_offsets; 867 } 868 869 ui_helpline__push("Press <- or ESC to exit"); 870 871 notes = symbol__annotation(sym); 872 browser.start = map__rip_2objdump(map, sym->start); 873 874 list_for_each_entry(pos, ¬es->src->source, node) { 875 struct browser_disasm_line *bpos; 876 size_t line_len = strlen(pos->line); 877 878 if (browser.b.width < line_len) 879 browser.b.width = line_len; 880 bpos = disasm_line__browser(pos); 881 bpos->idx = browser.nr_entries++; 882 if (pos->offset != -1) { 883 bpos->idx_asm = browser.nr_asm_entries++; 884 /* 885 * FIXME: short term bandaid to cope with assembly 886 * routines that comes with labels in the same column 887 * as the address in objdump, sigh. 888 * 889 * E.g. copy_user_generic_unrolled 890 */ 891 if (pos->offset < (s64)size) 892 browser.offsets[pos->offset] = pos; 893 } else 894 bpos->idx_asm = -1; 895 } 896 897 annotate_browser__mark_jump_targets(&browser, size); 898 899 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); 900 browser.max_addr_width = hex_width(sym->end); 901 browser.jumps_width = width_jumps(browser.max_jump_sources); 902 browser.b.nr_entries = browser.nr_entries; 903 browser.b.entries = ¬es->src->source, 904 browser.b.width += 18; /* Percentage */ 905 906 if (annotate_browser__opts.hide_src_code) 907 annotate_browser__init_asm_mode(&browser); 908 909 annotate_browser__update_addr_width(&browser); 910 911 ret = annotate_browser__run(&browser, evidx, hbt); 912 list_for_each_entry_safe(pos, n, ¬es->src->source, node) { 913 list_del(&pos->node); 914 disasm_line__free(pos); 915 } 916 917 out_free_offsets: 918 free(browser.offsets); 919 return ret; 920 } 921 922 #define ANNOTATE_CFG(n) \ 923 { .name = #n, .value = &annotate_browser__opts.n, } 924 925 /* 926 * Keep the entries sorted, they are bsearch'ed 927 */ 928 static struct annotate__config { 929 const char *name; 930 bool *value; 931 } annotate__configs[] = { 932 ANNOTATE_CFG(hide_src_code), 933 ANNOTATE_CFG(jump_arrows), 934 ANNOTATE_CFG(show_nr_jumps), 935 ANNOTATE_CFG(use_offset), 936 }; 937 938 #undef ANNOTATE_CFG 939 940 static int annotate_config__cmp(const void *name, const void *cfgp) 941 { 942 const struct annotate__config *cfg = cfgp; 943 944 return strcmp(name, cfg->name); 945 } 946 947 static int annotate__config(const char *var, const char *value, 948 void *data __maybe_unused) 949 { 950 struct annotate__config *cfg; 951 const char *name; 952 953 if (prefixcmp(var, "annotate.") != 0) 954 return 0; 955 956 name = var + 9; 957 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), 958 sizeof(struct annotate__config), annotate_config__cmp); 959 960 if (cfg == NULL) 961 return -1; 962 963 *cfg->value = perf_config_bool(name, value); 964 return 0; 965 } 966 967 void annotate_browser__init(void) 968 { 969 perf_config(annotate__config, NULL); 970 } 971