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