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