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