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 #include <linux/kernel.h> 14 15 struct disasm_line_samples { 16 double percent; 17 u64 nr; 18 }; 19 20 #define IPC_WIDTH 6 21 #define CYCLES_WIDTH 6 22 23 struct browser_disasm_line { 24 struct rb_node rb_node; 25 u32 idx; 26 int idx_asm; 27 int jump_sources; 28 /* 29 * actual length of this array is saved on the nr_events field 30 * of the struct annotate_browser 31 */ 32 struct disasm_line_samples samples[1]; 33 }; 34 35 static struct annotate_browser_opt { 36 bool hide_src_code, 37 use_offset, 38 jump_arrows, 39 show_linenr, 40 show_nr_jumps, 41 show_total_period; 42 } annotate_browser__opts = { 43 .use_offset = true, 44 .jump_arrows = true, 45 }; 46 47 struct annotate_browser { 48 struct ui_browser b; 49 struct rb_root entries; 50 struct rb_node *curr_hot; 51 struct disasm_line *selection; 52 struct disasm_line **offsets; 53 int nr_events; 54 u64 start; 55 int nr_asm_entries; 56 int nr_entries; 57 int max_jump_sources; 58 int nr_jumps; 59 bool searching_backwards; 60 bool have_cycles; 61 u8 addr_width; 62 u8 jumps_width; 63 u8 target_width; 64 u8 min_addr_width; 65 u8 max_addr_width; 66 char search_bf[128]; 67 }; 68 69 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) 70 { 71 return (struct browser_disasm_line *)(dl + 1); 72 } 73 74 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, 75 void *entry) 76 { 77 if (annotate_browser__opts.hide_src_code) { 78 struct disasm_line *dl = list_entry(entry, struct disasm_line, node); 79 return dl->offset == -1; 80 } 81 82 return false; 83 } 84 85 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, 86 int nr, bool current) 87 { 88 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) 89 return HE_COLORSET_SELECTED; 90 if (nr == browser->max_jump_sources) 91 return HE_COLORSET_TOP; 92 if (nr > 1) 93 return HE_COLORSET_MEDIUM; 94 return HE_COLORSET_NORMAL; 95 } 96 97 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, 98 int nr, bool current) 99 { 100 int color = annotate_browser__jumps_percent_color(browser, nr, current); 101 return ui_browser__set_color(&browser->b, color); 102 } 103 104 static int annotate_browser__pcnt_width(struct annotate_browser *ab) 105 { 106 int w = 7 * ab->nr_events; 107 108 if (ab->have_cycles) 109 w += IPC_WIDTH + CYCLES_WIDTH; 110 return w; 111 } 112 113 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) 114 { 115 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); 116 struct disasm_line *dl = list_entry(entry, struct disasm_line, node); 117 struct browser_disasm_line *bdl = disasm_line__browser(dl); 118 bool current_entry = ui_browser__is_current_entry(browser, row); 119 bool change_color = (!annotate_browser__opts.hide_src_code && 120 (!current_entry || (browser->use_navkeypressed && 121 !browser->navkeypressed))); 122 int width = browser->width, printed; 123 int i, pcnt_width = annotate_browser__pcnt_width(ab); 124 double percent_max = 0.0; 125 char bf[256]; 126 127 for (i = 0; i < ab->nr_events; i++) { 128 if (bdl->samples[i].percent > percent_max) 129 percent_max = bdl->samples[i].percent; 130 } 131 132 if (dl->offset != -1 && percent_max != 0.0) { 133 if (percent_max != 0.0) { 134 for (i = 0; i < ab->nr_events; i++) { 135 ui_browser__set_percent_color(browser, 136 bdl->samples[i].percent, 137 current_entry); 138 if (annotate_browser__opts.show_total_period) { 139 ui_browser__printf(browser, "%6" PRIu64 " ", 140 bdl->samples[i].nr); 141 } else { 142 ui_browser__printf(browser, "%6.2f ", 143 bdl->samples[i].percent); 144 } 145 } 146 } else { 147 ui_browser__write_nstring(browser, " ", 7 * ab->nr_events); 148 } 149 } else { 150 ui_browser__set_percent_color(browser, 0, current_entry); 151 ui_browser__write_nstring(browser, " ", 7 * ab->nr_events); 152 } 153 if (ab->have_cycles) { 154 if (dl->ipc) 155 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc); 156 else 157 ui_browser__write_nstring(browser, " ", IPC_WIDTH); 158 if (dl->cycles) 159 ui_browser__printf(browser, "%*" PRIu64 " ", 160 CYCLES_WIDTH - 1, dl->cycles); 161 else 162 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH); 163 } 164 165 SLsmg_write_char(' '); 166 167 /* The scroll bar isn't being used */ 168 if (!browser->navkeypressed) 169 width += 1; 170 171 if (!*dl->line) 172 ui_browser__write_nstring(browser, " ", width - pcnt_width); 173 else if (dl->offset == -1) { 174 if (dl->line_nr && annotate_browser__opts.show_linenr) 175 printed = scnprintf(bf, sizeof(bf), "%-*d ", 176 ab->addr_width + 1, dl->line_nr); 177 else 178 printed = scnprintf(bf, sizeof(bf), "%*s ", 179 ab->addr_width, " "); 180 ui_browser__write_nstring(browser, bf, printed); 181 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1); 182 } else { 183 u64 addr = dl->offset; 184 int color = -1; 185 186 if (!annotate_browser__opts.use_offset) 187 addr += ab->start; 188 189 if (!annotate_browser__opts.use_offset) { 190 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); 191 } else { 192 if (bdl->jump_sources) { 193 if (annotate_browser__opts.show_nr_jumps) { 194 int prev; 195 printed = scnprintf(bf, sizeof(bf), "%*d ", 196 ab->jumps_width, 197 bdl->jump_sources); 198 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources, 199 current_entry); 200 ui_browser__write_nstring(browser, bf, printed); 201 ui_browser__set_color(browser, prev); 202 } 203 204 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", 205 ab->target_width, addr); 206 } else { 207 printed = scnprintf(bf, sizeof(bf), "%*s ", 208 ab->addr_width, " "); 209 } 210 } 211 212 if (change_color) 213 color = ui_browser__set_color(browser, HE_COLORSET_ADDR); 214 ui_browser__write_nstring(browser, bf, printed); 215 if (change_color) 216 ui_browser__set_color(browser, color); 217 if (dl->ins.ops && dl->ins.ops->scnprintf) { 218 if (ins__is_jump(&dl->ins)) { 219 bool fwd = dl->ops.target.offset > dl->offset; 220 221 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : 222 SLSMG_UARROW_CHAR); 223 SLsmg_write_char(' '); 224 } else if (ins__is_call(&dl->ins)) { 225 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); 226 SLsmg_write_char(' '); 227 } else if (ins__is_ret(&dl->ins)) { 228 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); 229 SLsmg_write_char(' '); 230 } else { 231 ui_browser__write_nstring(browser, " ", 2); 232 } 233 } else { 234 ui_browser__write_nstring(browser, " ", 2); 235 } 236 237 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); 238 ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed); 239 } 240 241 if (current_entry) 242 ab->selection = dl; 243 } 244 245 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) 246 { 247 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) 248 || !disasm_line__has_offset(dl) 249 || dl->ops.target.offset < 0 250 || dl->ops.target.offset >= (s64)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) || 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 u64 offset; 549 s64 idx; 550 551 if (!ins__is_jump(&dl->ins)) 552 return false; 553 554 offset = dl->ops.target.offset; 555 dl = annotate_browser__find_offset(browser, offset, &idx); 556 if (dl == NULL) { 557 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); 558 return true; 559 } 560 561 annotate_browser__set_top(browser, dl, idx); 562 563 return true; 564 } 565 566 static 567 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, 568 char *s, s64 *idx) 569 { 570 struct map_symbol *ms = browser->b.priv; 571 struct symbol *sym = ms->sym; 572 struct annotation *notes = symbol__annotation(sym); 573 struct disasm_line *pos = browser->selection; 574 575 *idx = browser->b.index; 576 list_for_each_entry_continue(pos, ¬es->src->source, node) { 577 if (disasm_line__filter(&browser->b, &pos->node)) 578 continue; 579 580 ++*idx; 581 582 if (pos->line && strstr(pos->line, s) != NULL) 583 return pos; 584 } 585 586 return NULL; 587 } 588 589 static bool __annotate_browser__search(struct annotate_browser *browser) 590 { 591 struct disasm_line *dl; 592 s64 idx; 593 594 dl = annotate_browser__find_string(browser, browser->search_bf, &idx); 595 if (dl == NULL) { 596 ui_helpline__puts("String not found!"); 597 return false; 598 } 599 600 annotate_browser__set_top(browser, dl, idx); 601 browser->searching_backwards = false; 602 return true; 603 } 604 605 static 606 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, 607 char *s, s64 *idx) 608 { 609 struct map_symbol *ms = browser->b.priv; 610 struct symbol *sym = ms->sym; 611 struct annotation *notes = symbol__annotation(sym); 612 struct disasm_line *pos = browser->selection; 613 614 *idx = browser->b.index; 615 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { 616 if (disasm_line__filter(&browser->b, &pos->node)) 617 continue; 618 619 --*idx; 620 621 if (pos->line && strstr(pos->line, s) != NULL) 622 return pos; 623 } 624 625 return NULL; 626 } 627 628 static bool __annotate_browser__search_reverse(struct annotate_browser *browser) 629 { 630 struct disasm_line *dl; 631 s64 idx; 632 633 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); 634 if (dl == NULL) { 635 ui_helpline__puts("String not found!"); 636 return false; 637 } 638 639 annotate_browser__set_top(browser, dl, idx); 640 browser->searching_backwards = true; 641 return true; 642 } 643 644 static bool annotate_browser__search_window(struct annotate_browser *browser, 645 int delay_secs) 646 { 647 if (ui_browser__input_window("Search", "String: ", browser->search_bf, 648 "ENTER: OK, ESC: Cancel", 649 delay_secs * 2) != K_ENTER || 650 !*browser->search_bf) 651 return false; 652 653 return true; 654 } 655 656 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) 657 { 658 if (annotate_browser__search_window(browser, delay_secs)) 659 return __annotate_browser__search(browser); 660 661 return false; 662 } 663 664 static bool annotate_browser__continue_search(struct annotate_browser *browser, 665 int delay_secs) 666 { 667 if (!*browser->search_bf) 668 return annotate_browser__search(browser, delay_secs); 669 670 return __annotate_browser__search(browser); 671 } 672 673 static bool annotate_browser__search_reverse(struct annotate_browser *browser, 674 int delay_secs) 675 { 676 if (annotate_browser__search_window(browser, delay_secs)) 677 return __annotate_browser__search_reverse(browser); 678 679 return false; 680 } 681 682 static 683 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, 684 int delay_secs) 685 { 686 if (!*browser->search_bf) 687 return annotate_browser__search_reverse(browser, delay_secs); 688 689 return __annotate_browser__search_reverse(browser); 690 } 691 692 static void annotate_browser__update_addr_width(struct annotate_browser *browser) 693 { 694 if (annotate_browser__opts.use_offset) 695 browser->target_width = browser->min_addr_width; 696 else 697 browser->target_width = browser->max_addr_width; 698 699 browser->addr_width = browser->target_width; 700 701 if (annotate_browser__opts.show_nr_jumps) 702 browser->addr_width += browser->jumps_width + 1; 703 } 704 705 static int annotate_browser__run(struct annotate_browser *browser, 706 struct perf_evsel *evsel, 707 struct hist_browser_timer *hbt) 708 { 709 struct rb_node *nd = NULL; 710 struct map_symbol *ms = browser->b.priv; 711 struct symbol *sym = ms->sym; 712 const char *help = "Press 'h' for help on key bindings"; 713 int delay_secs = hbt ? hbt->refresh : 0; 714 int key; 715 char title[SYM_TITLE_MAX_SIZE]; 716 717 sym_title(sym, ms->map, title, sizeof(title)); 718 if (ui_browser__show(&browser->b, title, help) < 0) 719 return -1; 720 721 annotate_browser__calc_percent(browser, evsel); 722 723 if (browser->curr_hot) { 724 annotate_browser__set_rb_top(browser, browser->curr_hot); 725 browser->b.navkeypressed = false; 726 } 727 728 nd = browser->curr_hot; 729 730 while (1) { 731 key = ui_browser__run(&browser->b, delay_secs); 732 733 if (delay_secs != 0) { 734 annotate_browser__calc_percent(browser, evsel); 735 /* 736 * Current line focus got out of the list of most active 737 * lines, NULL it so that if TAB|UNTAB is pressed, we 738 * move to curr_hot (current hottest line). 739 */ 740 if (nd != NULL && RB_EMPTY_NODE(nd)) 741 nd = NULL; 742 } 743 744 switch (key) { 745 case K_TIMER: 746 if (hbt) 747 hbt->timer(hbt->arg); 748 749 if (delay_secs != 0) 750 symbol__annotate_decay_histogram(sym, evsel->idx); 751 continue; 752 case K_TAB: 753 if (nd != NULL) { 754 nd = rb_prev(nd); 755 if (nd == NULL) 756 nd = rb_last(&browser->entries); 757 } else 758 nd = browser->curr_hot; 759 break; 760 case K_UNTAB: 761 if (nd != NULL) { 762 nd = rb_next(nd); 763 if (nd == NULL) 764 nd = rb_first(&browser->entries); 765 } else 766 nd = browser->curr_hot; 767 break; 768 case K_F1: 769 case 'h': 770 ui_browser__help_window(&browser->b, 771 "UP/DOWN/PGUP\n" 772 "PGDN/SPACE Navigate\n" 773 "q/ESC/CTRL+C Exit\n\n" 774 "ENTER Go to target\n" 775 "ESC Exit\n" 776 "H Cycle thru hottest instructions\n" 777 "j Toggle showing jump to target arrows\n" 778 "J Toggle showing number of jump sources on targets\n" 779 "n Search next string\n" 780 "o Toggle disassembler output/simplified view\n" 781 "s Toggle source code view\n" 782 "t Toggle total period view\n" 783 "/ Search string\n" 784 "k Toggle line numbers\n" 785 "r Run available scripts\n" 786 "? Search string backwards\n"); 787 continue; 788 case 'r': 789 { 790 script_browse(NULL); 791 continue; 792 } 793 case 'k': 794 annotate_browser__opts.show_linenr = 795 !annotate_browser__opts.show_linenr; 796 break; 797 case 'H': 798 nd = browser->curr_hot; 799 break; 800 case 's': 801 if (annotate_browser__toggle_source(browser)) 802 ui_helpline__puts(help); 803 continue; 804 case 'o': 805 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; 806 annotate_browser__update_addr_width(browser); 807 continue; 808 case 'j': 809 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; 810 continue; 811 case 'J': 812 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; 813 annotate_browser__update_addr_width(browser); 814 continue; 815 case '/': 816 if (annotate_browser__search(browser, delay_secs)) { 817 show_help: 818 ui_helpline__puts(help); 819 } 820 continue; 821 case 'n': 822 if (browser->searching_backwards ? 823 annotate_browser__continue_search_reverse(browser, delay_secs) : 824 annotate_browser__continue_search(browser, delay_secs)) 825 goto show_help; 826 continue; 827 case '?': 828 if (annotate_browser__search_reverse(browser, delay_secs)) 829 goto show_help; 830 continue; 831 case 'D': { 832 static int seq; 833 ui_helpline__pop(); 834 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", 835 seq++, browser->b.nr_entries, 836 browser->b.height, 837 browser->b.index, 838 browser->b.top_idx, 839 browser->nr_asm_entries); 840 } 841 continue; 842 case K_ENTER: 843 case K_RIGHT: 844 if (browser->selection == NULL) 845 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); 846 else if (browser->selection->offset == -1) 847 ui_helpline__puts("Actions are only available for assembly lines."); 848 else if (!browser->selection->ins.ops) 849 goto show_sup_ins; 850 else if (ins__is_ret(&browser->selection->ins)) 851 goto out; 852 else if (!(annotate_browser__jump(browser) || 853 annotate_browser__callq(browser, evsel, hbt))) { 854 show_sup_ins: 855 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); 856 } 857 continue; 858 case 't': 859 annotate_browser__opts.show_total_period = 860 !annotate_browser__opts.show_total_period; 861 annotate_browser__update_addr_width(browser); 862 continue; 863 case K_LEFT: 864 case K_ESC: 865 case 'q': 866 case CTRL('c'): 867 goto out; 868 default: 869 continue; 870 } 871 872 if (nd != NULL) 873 annotate_browser__set_rb_top(browser, nd); 874 } 875 out: 876 ui_browser__hide(&browser->b); 877 return key; 878 } 879 880 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, 881 struct hist_browser_timer *hbt) 882 { 883 /* Set default value for show_total_period. */ 884 annotate_browser__opts.show_total_period = 885 symbol_conf.show_total_period; 886 887 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt); 888 } 889 890 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, 891 struct hist_browser_timer *hbt) 892 { 893 /* reset abort key so that it can get Ctrl-C as a key */ 894 SLang_reset_tty(); 895 SLang_init_tty(0, 0, 0); 896 897 return map_symbol__tui_annotate(&he->ms, evsel, hbt); 898 } 899 900 901 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end) 902 { 903 unsigned n_insn = 0; 904 u64 offset; 905 906 for (offset = start; offset <= end; offset++) { 907 if (browser->offsets[offset]) 908 n_insn++; 909 } 910 return n_insn; 911 } 912 913 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end, 914 struct cyc_hist *ch) 915 { 916 unsigned n_insn; 917 u64 offset; 918 919 n_insn = count_insn(browser, start, end); 920 if (n_insn && ch->num && ch->cycles) { 921 float ipc = n_insn / ((double)ch->cycles / (double)ch->num); 922 923 /* Hide data when there are too many overlaps. */ 924 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2) 925 return; 926 927 for (offset = start; offset <= end; offset++) { 928 struct disasm_line *dl = browser->offsets[offset]; 929 930 if (dl) 931 dl->ipc = ipc; 932 } 933 } 934 } 935 936 /* 937 * This should probably be in util/annotate.c to share with the tty 938 * annotate, but right now we need the per byte offsets arrays, 939 * which are only here. 940 */ 941 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size, 942 struct symbol *sym) 943 { 944 u64 offset; 945 struct annotation *notes = symbol__annotation(sym); 946 947 if (!notes->src || !notes->src->cycles_hist) 948 return; 949 950 pthread_mutex_lock(¬es->lock); 951 for (offset = 0; offset < size; ++offset) { 952 struct cyc_hist *ch; 953 954 ch = ¬es->src->cycles_hist[offset]; 955 if (ch && ch->cycles) { 956 struct disasm_line *dl; 957 958 if (ch->have_start) 959 count_and_fill(browser, ch->start, offset, ch); 960 dl = browser->offsets[offset]; 961 if (dl && ch->num_aggr) 962 dl->cycles = ch->cycles_aggr / ch->num_aggr; 963 browser->have_cycles = true; 964 } 965 } 966 pthread_mutex_unlock(¬es->lock); 967 } 968 969 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, 970 size_t size) 971 { 972 u64 offset; 973 struct map_symbol *ms = browser->b.priv; 974 struct symbol *sym = ms->sym; 975 976 /* PLT symbols contain external offsets */ 977 if (strstr(sym->name, "@plt")) 978 return; 979 980 for (offset = 0; offset < size; ++offset) { 981 struct disasm_line *dl = browser->offsets[offset], *dlt; 982 struct browser_disasm_line *bdlt; 983 984 if (!disasm_line__is_valid_jump(dl, sym)) 985 continue; 986 987 dlt = browser->offsets[dl->ops.target.offset]; 988 /* 989 * FIXME: Oops, no jump target? Buggy disassembler? Or do we 990 * have to adjust to the previous offset? 991 */ 992 if (dlt == NULL) 993 continue; 994 995 bdlt = disasm_line__browser(dlt); 996 if (++bdlt->jump_sources > browser->max_jump_sources) 997 browser->max_jump_sources = bdlt->jump_sources; 998 999 ++browser->nr_jumps; 1000 } 1001 } 1002 1003 static inline int width_jumps(int n) 1004 { 1005 if (n >= 100) 1006 return 5; 1007 if (n / 10) 1008 return 2; 1009 return 1; 1010 } 1011 1012 int symbol__tui_annotate(struct symbol *sym, struct map *map, 1013 struct perf_evsel *evsel, 1014 struct hist_browser_timer *hbt) 1015 { 1016 struct disasm_line *pos, *n; 1017 struct annotation *notes; 1018 size_t size; 1019 struct map_symbol ms = { 1020 .map = map, 1021 .sym = sym, 1022 }; 1023 struct annotate_browser browser = { 1024 .b = { 1025 .refresh = annotate_browser__refresh, 1026 .seek = ui_browser__list_head_seek, 1027 .write = annotate_browser__write, 1028 .filter = disasm_line__filter, 1029 .priv = &ms, 1030 .use_navkeypressed = true, 1031 }, 1032 }; 1033 int ret = -1, err; 1034 int nr_pcnt = 1; 1035 size_t sizeof_bdl = sizeof(struct browser_disasm_line); 1036 1037 if (sym == NULL) 1038 return -1; 1039 1040 size = symbol__size(sym); 1041 1042 if (map->dso->annotate_warned) 1043 return -1; 1044 1045 browser.offsets = zalloc(size * sizeof(struct disasm_line *)); 1046 if (browser.offsets == NULL) { 1047 ui__error("Not enough memory!"); 1048 return -1; 1049 } 1050 1051 if (perf_evsel__is_group_event(evsel)) { 1052 nr_pcnt = evsel->nr_members; 1053 sizeof_bdl += sizeof(struct disasm_line_samples) * 1054 (nr_pcnt - 1); 1055 } 1056 1057 err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel), sizeof_bdl); 1058 if (err) { 1059 char msg[BUFSIZ]; 1060 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); 1061 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 1062 goto out_free_offsets; 1063 } 1064 1065 ui_helpline__push("Press ESC to exit"); 1066 1067 notes = symbol__annotation(sym); 1068 browser.start = map__rip_2objdump(map, sym->start); 1069 1070 list_for_each_entry(pos, ¬es->src->source, node) { 1071 struct browser_disasm_line *bpos; 1072 size_t line_len = strlen(pos->line); 1073 1074 if (browser.b.width < line_len) 1075 browser.b.width = line_len; 1076 bpos = disasm_line__browser(pos); 1077 bpos->idx = browser.nr_entries++; 1078 if (pos->offset != -1) { 1079 bpos->idx_asm = browser.nr_asm_entries++; 1080 /* 1081 * FIXME: short term bandaid to cope with assembly 1082 * routines that comes with labels in the same column 1083 * as the address in objdump, sigh. 1084 * 1085 * E.g. copy_user_generic_unrolled 1086 */ 1087 if (pos->offset < (s64)size) 1088 browser.offsets[pos->offset] = pos; 1089 } else 1090 bpos->idx_asm = -1; 1091 } 1092 1093 annotate_browser__mark_jump_targets(&browser, size); 1094 annotate__compute_ipc(&browser, size, sym); 1095 1096 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); 1097 browser.max_addr_width = hex_width(sym->end); 1098 browser.jumps_width = width_jumps(browser.max_jump_sources); 1099 browser.nr_events = nr_pcnt; 1100 browser.b.nr_entries = browser.nr_entries; 1101 browser.b.entries = ¬es->src->source, 1102 browser.b.width += 18; /* Percentage */ 1103 1104 if (annotate_browser__opts.hide_src_code) 1105 annotate_browser__init_asm_mode(&browser); 1106 1107 annotate_browser__update_addr_width(&browser); 1108 1109 ret = annotate_browser__run(&browser, evsel, hbt); 1110 list_for_each_entry_safe(pos, n, ¬es->src->source, node) { 1111 list_del(&pos->node); 1112 disasm_line__free(pos); 1113 } 1114 1115 out_free_offsets: 1116 free(browser.offsets); 1117 return ret; 1118 } 1119 1120 #define ANNOTATE_CFG(n) \ 1121 { .name = #n, .value = &annotate_browser__opts.n, } 1122 1123 /* 1124 * Keep the entries sorted, they are bsearch'ed 1125 */ 1126 static struct annotate_config { 1127 const char *name; 1128 bool *value; 1129 } annotate__configs[] = { 1130 ANNOTATE_CFG(hide_src_code), 1131 ANNOTATE_CFG(jump_arrows), 1132 ANNOTATE_CFG(show_linenr), 1133 ANNOTATE_CFG(show_nr_jumps), 1134 ANNOTATE_CFG(show_total_period), 1135 ANNOTATE_CFG(use_offset), 1136 }; 1137 1138 #undef ANNOTATE_CFG 1139 1140 static int annotate_config__cmp(const void *name, const void *cfgp) 1141 { 1142 const struct annotate_config *cfg = cfgp; 1143 1144 return strcmp(name, cfg->name); 1145 } 1146 1147 static int annotate__config(const char *var, const char *value, 1148 void *data __maybe_unused) 1149 { 1150 struct annotate_config *cfg; 1151 const char *name; 1152 1153 if (prefixcmp(var, "annotate.") != 0) 1154 return 0; 1155 1156 name = var + 9; 1157 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), 1158 sizeof(struct annotate_config), annotate_config__cmp); 1159 1160 if (cfg == NULL) 1161 ui__warning("%s variable unknown, ignoring...", var); 1162 else 1163 *cfg->value = perf_config_bool(name, value); 1164 return 0; 1165 } 1166 1167 void annotate_browser__init(void) 1168 { 1169 perf_config(annotate__config, NULL); 1170 } 1171