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