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