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