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