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