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