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