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