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