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