1 #include <stdio.h> 2 #include "../libslang.h" 3 #include <stdlib.h> 4 #include <string.h> 5 #include <linux/rbtree.h> 6 7 #include "../../util/evsel.h" 8 #include "../../util/evlist.h" 9 #include "../../util/hist.h" 10 #include "../../util/pstack.h" 11 #include "../../util/sort.h" 12 #include "../../util/util.h" 13 #include "../../util/top.h" 14 #include "../../arch/common.h" 15 16 #include "../browser.h" 17 #include "../helpline.h" 18 #include "../util.h" 19 #include "../ui.h" 20 #include "map.h" 21 #include "annotate.h" 22 23 struct hist_browser { 24 struct ui_browser b; 25 struct hists *hists; 26 struct hist_entry *he_selection; 27 struct map_symbol *selection; 28 int print_seq; 29 bool show_dso; 30 bool show_headers; 31 float min_pcnt; 32 u64 nr_non_filtered_entries; 33 u64 nr_callchain_rows; 34 }; 35 36 extern void hist_browser__init_hpp(void); 37 38 static int hists__browser_title(struct hists *hists, 39 struct hist_browser_timer *hbt, 40 char *bf, size_t size); 41 static void hist_browser__update_nr_entries(struct hist_browser *hb); 42 43 static struct rb_node *hists__filter_entries(struct rb_node *nd, 44 float min_pcnt); 45 46 static bool hist_browser__has_filter(struct hist_browser *hb) 47 { 48 return hists__has_filter(hb->hists) || hb->min_pcnt; 49 } 50 51 static u32 hist_browser__nr_entries(struct hist_browser *hb) 52 { 53 u32 nr_entries; 54 55 if (hist_browser__has_filter(hb)) 56 nr_entries = hb->nr_non_filtered_entries; 57 else 58 nr_entries = hb->hists->nr_entries; 59 60 return nr_entries + hb->nr_callchain_rows; 61 } 62 63 static void hist_browser__update_rows(struct hist_browser *hb) 64 { 65 struct ui_browser *browser = &hb->b; 66 u16 header_offset = hb->show_headers ? 1 : 0, index_row; 67 68 browser->rows = browser->height - header_offset; 69 /* 70 * Verify if we were at the last line and that line isn't 71 * visibe because we now show the header line(s). 72 */ 73 index_row = browser->index - browser->top_idx; 74 if (index_row >= browser->rows) 75 browser->index -= index_row - browser->rows + 1; 76 } 77 78 static void hist_browser__refresh_dimensions(struct ui_browser *browser) 79 { 80 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 81 82 /* 3 == +/- toggle symbol before actual hist_entry rendering */ 83 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]")); 84 /* 85 * FIXME: Just keeping existing behaviour, but this really should be 86 * before updating browser->width, as it will invalidate the 87 * calculation above. Fix this and the fallout in another 88 * changeset. 89 */ 90 ui_browser__refresh_dimensions(browser); 91 hist_browser__update_rows(hb); 92 } 93 94 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column) 95 { 96 u16 header_offset = browser->show_headers ? 1 : 0; 97 98 ui_browser__gotorc(&browser->b, row + header_offset, column); 99 } 100 101 static void hist_browser__reset(struct hist_browser *browser) 102 { 103 /* 104 * The hists__remove_entry_filter() already folds non-filtered 105 * entries so we can assume it has 0 callchain rows. 106 */ 107 browser->nr_callchain_rows = 0; 108 109 hist_browser__update_nr_entries(browser); 110 browser->b.nr_entries = hist_browser__nr_entries(browser); 111 hist_browser__refresh_dimensions(&browser->b); 112 ui_browser__reset_index(&browser->b); 113 } 114 115 static char tree__folded_sign(bool unfolded) 116 { 117 return unfolded ? '-' : '+'; 118 } 119 120 static char map_symbol__folded(const struct map_symbol *ms) 121 { 122 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' '; 123 } 124 125 static char hist_entry__folded(const struct hist_entry *he) 126 { 127 return map_symbol__folded(&he->ms); 128 } 129 130 static char callchain_list__folded(const struct callchain_list *cl) 131 { 132 return map_symbol__folded(&cl->ms); 133 } 134 135 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold) 136 { 137 ms->unfolded = unfold ? ms->has_children : false; 138 } 139 140 static int callchain_node__count_rows_rb_tree(struct callchain_node *node) 141 { 142 int n = 0; 143 struct rb_node *nd; 144 145 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 146 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 147 struct callchain_list *chain; 148 char folded_sign = ' '; /* No children */ 149 150 list_for_each_entry(chain, &child->val, list) { 151 ++n; 152 /* We need this because we may not have children */ 153 folded_sign = callchain_list__folded(chain); 154 if (folded_sign == '+') 155 break; 156 } 157 158 if (folded_sign == '-') /* Have children and they're unfolded */ 159 n += callchain_node__count_rows_rb_tree(child); 160 } 161 162 return n; 163 } 164 165 static int callchain_node__count_rows(struct callchain_node *node) 166 { 167 struct callchain_list *chain; 168 bool unfolded = false; 169 int n = 0; 170 171 list_for_each_entry(chain, &node->val, list) { 172 ++n; 173 unfolded = chain->ms.unfolded; 174 } 175 176 if (unfolded) 177 n += callchain_node__count_rows_rb_tree(node); 178 179 return n; 180 } 181 182 static int callchain__count_rows(struct rb_root *chain) 183 { 184 struct rb_node *nd; 185 int n = 0; 186 187 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 188 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 189 n += callchain_node__count_rows(node); 190 } 191 192 return n; 193 } 194 195 static bool map_symbol__toggle_fold(struct map_symbol *ms) 196 { 197 if (!ms) 198 return false; 199 200 if (!ms->has_children) 201 return false; 202 203 ms->unfolded = !ms->unfolded; 204 return true; 205 } 206 207 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node) 208 { 209 struct rb_node *nd = rb_first(&node->rb_root); 210 211 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 212 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 213 struct callchain_list *chain; 214 bool first = true; 215 216 list_for_each_entry(chain, &child->val, list) { 217 if (first) { 218 first = false; 219 chain->ms.has_children = chain->list.next != &child->val || 220 !RB_EMPTY_ROOT(&child->rb_root); 221 } else 222 chain->ms.has_children = chain->list.next == &child->val && 223 !RB_EMPTY_ROOT(&child->rb_root); 224 } 225 226 callchain_node__init_have_children_rb_tree(child); 227 } 228 } 229 230 static void callchain_node__init_have_children(struct callchain_node *node) 231 { 232 struct callchain_list *chain; 233 234 if (!list_empty(&node->val)) { 235 chain = list_entry(node->val.prev, struct callchain_list, list); 236 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); 237 } 238 239 callchain_node__init_have_children_rb_tree(node); 240 } 241 242 static void callchain__init_have_children(struct rb_root *root) 243 { 244 struct rb_node *nd; 245 246 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 247 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 248 callchain_node__init_have_children(node); 249 } 250 } 251 252 static void hist_entry__init_have_children(struct hist_entry *he) 253 { 254 if (!he->init_have_children) { 255 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 256 callchain__init_have_children(&he->sorted_chain); 257 he->init_have_children = true; 258 } 259 } 260 261 static bool hist_browser__toggle_fold(struct hist_browser *browser) 262 { 263 if (map_symbol__toggle_fold(browser->selection)) { 264 struct hist_entry *he = browser->he_selection; 265 266 hist_entry__init_have_children(he); 267 browser->b.nr_entries -= he->nr_rows; 268 browser->nr_callchain_rows -= he->nr_rows; 269 270 if (he->ms.unfolded) 271 he->nr_rows = callchain__count_rows(&he->sorted_chain); 272 else 273 he->nr_rows = 0; 274 275 browser->b.nr_entries += he->nr_rows; 276 browser->nr_callchain_rows += he->nr_rows; 277 278 return true; 279 } 280 281 /* If it doesn't have children, no toggling performed */ 282 return false; 283 } 284 285 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold) 286 { 287 int n = 0; 288 struct rb_node *nd; 289 290 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 291 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 292 struct callchain_list *chain; 293 bool has_children = false; 294 295 list_for_each_entry(chain, &child->val, list) { 296 ++n; 297 map_symbol__set_folding(&chain->ms, unfold); 298 has_children = chain->ms.has_children; 299 } 300 301 if (has_children) 302 n += callchain_node__set_folding_rb_tree(child, unfold); 303 } 304 305 return n; 306 } 307 308 static int callchain_node__set_folding(struct callchain_node *node, bool unfold) 309 { 310 struct callchain_list *chain; 311 bool has_children = false; 312 int n = 0; 313 314 list_for_each_entry(chain, &node->val, list) { 315 ++n; 316 map_symbol__set_folding(&chain->ms, unfold); 317 has_children = chain->ms.has_children; 318 } 319 320 if (has_children) 321 n += callchain_node__set_folding_rb_tree(node, unfold); 322 323 return n; 324 } 325 326 static int callchain__set_folding(struct rb_root *chain, bool unfold) 327 { 328 struct rb_node *nd; 329 int n = 0; 330 331 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 332 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 333 n += callchain_node__set_folding(node, unfold); 334 } 335 336 return n; 337 } 338 339 static void hist_entry__set_folding(struct hist_entry *he, bool unfold) 340 { 341 hist_entry__init_have_children(he); 342 map_symbol__set_folding(&he->ms, unfold); 343 344 if (he->ms.has_children) { 345 int n = callchain__set_folding(&he->sorted_chain, unfold); 346 he->nr_rows = unfold ? n : 0; 347 } else 348 he->nr_rows = 0; 349 } 350 351 static void 352 __hist_browser__set_folding(struct hist_browser *browser, bool unfold) 353 { 354 struct rb_node *nd; 355 struct hists *hists = browser->hists; 356 357 for (nd = rb_first(&hists->entries); 358 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 359 nd = rb_next(nd)) { 360 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 361 hist_entry__set_folding(he, unfold); 362 browser->nr_callchain_rows += he->nr_rows; 363 } 364 } 365 366 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 367 { 368 browser->nr_callchain_rows = 0; 369 __hist_browser__set_folding(browser, unfold); 370 371 browser->b.nr_entries = hist_browser__nr_entries(browser); 372 /* Go to the start, we may be way after valid entries after a collapse */ 373 ui_browser__reset_index(&browser->b); 374 } 375 376 static void ui_browser__warn_lost_events(struct ui_browser *browser) 377 { 378 ui_browser__warning(browser, 4, 379 "Events are being lost, check IO/CPU overload!\n\n" 380 "You may want to run 'perf' using a RT scheduler policy:\n\n" 381 " perf top -r 80\n\n" 382 "Or reduce the sampling frequency."); 383 } 384 385 static int hist_browser__run(struct hist_browser *browser, 386 struct hist_browser_timer *hbt) 387 { 388 int key; 389 char title[160]; 390 int delay_secs = hbt ? hbt->refresh : 0; 391 392 browser->b.entries = &browser->hists->entries; 393 browser->b.nr_entries = hist_browser__nr_entries(browser); 394 395 hists__browser_title(browser->hists, hbt, title, sizeof(title)); 396 397 if (ui_browser__show(&browser->b, title, 398 "Press '?' for help on key bindings") < 0) 399 return -1; 400 401 while (1) { 402 key = ui_browser__run(&browser->b, delay_secs); 403 404 switch (key) { 405 case K_TIMER: { 406 u64 nr_entries; 407 hbt->timer(hbt->arg); 408 409 if (hist_browser__has_filter(browser)) 410 hist_browser__update_nr_entries(browser); 411 412 nr_entries = hist_browser__nr_entries(browser); 413 ui_browser__update_nr_entries(&browser->b, nr_entries); 414 415 if (browser->hists->stats.nr_lost_warned != 416 browser->hists->stats.nr_events[PERF_RECORD_LOST]) { 417 browser->hists->stats.nr_lost_warned = 418 browser->hists->stats.nr_events[PERF_RECORD_LOST]; 419 ui_browser__warn_lost_events(&browser->b); 420 } 421 422 hists__browser_title(browser->hists, 423 hbt, title, sizeof(title)); 424 ui_browser__show_title(&browser->b, title); 425 continue; 426 } 427 case 'D': { /* Debug */ 428 static int seq; 429 struct hist_entry *h = rb_entry(browser->b.top, 430 struct hist_entry, rb_node); 431 ui_helpline__pop(); 432 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 433 seq++, browser->b.nr_entries, 434 browser->hists->nr_entries, 435 browser->b.rows, 436 browser->b.index, 437 browser->b.top_idx, 438 h->row_offset, h->nr_rows); 439 } 440 break; 441 case 'C': 442 /* Collapse the whole world. */ 443 hist_browser__set_folding(browser, false); 444 break; 445 case 'E': 446 /* Expand the whole world. */ 447 hist_browser__set_folding(browser, true); 448 break; 449 case 'H': 450 browser->show_headers = !browser->show_headers; 451 hist_browser__update_rows(browser); 452 break; 453 case K_ENTER: 454 if (hist_browser__toggle_fold(browser)) 455 break; 456 /* fall thru */ 457 default: 458 goto out; 459 } 460 } 461 out: 462 ui_browser__hide(&browser->b); 463 return key; 464 } 465 466 static char *callchain_list__sym_name(struct callchain_list *cl, 467 char *bf, size_t bfsize, bool show_dso) 468 { 469 int printed; 470 471 if (cl->ms.sym) 472 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); 473 else 474 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); 475 476 if (show_dso) 477 scnprintf(bf + printed, bfsize - printed, " %s", 478 cl->ms.map ? cl->ms.map->dso->short_name : "unknown"); 479 480 return bf; 481 } 482 483 struct callchain_print_arg { 484 /* for hists browser */ 485 off_t row_offset; 486 bool is_current_entry; 487 488 /* for file dump */ 489 FILE *fp; 490 int printed; 491 }; 492 493 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, 494 struct callchain_list *chain, 495 const char *str, int offset, 496 unsigned short row, 497 struct callchain_print_arg *arg); 498 499 static void hist_browser__show_callchain_entry(struct hist_browser *browser, 500 struct callchain_list *chain, 501 const char *str, int offset, 502 unsigned short row, 503 struct callchain_print_arg *arg) 504 { 505 int color, width; 506 char folded_sign = callchain_list__folded(chain); 507 508 color = HE_COLORSET_NORMAL; 509 width = browser->b.width - (offset + 2); 510 if (ui_browser__is_current_entry(&browser->b, row)) { 511 browser->selection = &chain->ms; 512 color = HE_COLORSET_SELECTED; 513 arg->is_current_entry = true; 514 } 515 516 ui_browser__set_color(&browser->b, color); 517 hist_browser__gotorc(browser, row, 0); 518 slsmg_write_nstring(" ", offset); 519 slsmg_printf("%c ", folded_sign); 520 slsmg_write_nstring(str, width); 521 } 522 523 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, 524 struct callchain_list *chain, 525 const char *str, int offset, 526 unsigned short row __maybe_unused, 527 struct callchain_print_arg *arg) 528 { 529 char folded_sign = callchain_list__folded(chain); 530 531 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", 532 folded_sign, str); 533 } 534 535 typedef bool (*check_output_full_fn)(struct hist_browser *browser, 536 unsigned short row); 537 538 static bool hist_browser__check_output_full(struct hist_browser *browser, 539 unsigned short row) 540 { 541 return browser->b.rows == row; 542 } 543 544 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, 545 unsigned short row __maybe_unused) 546 { 547 return false; 548 } 549 550 #define LEVEL_OFFSET_STEP 3 551 552 static int hist_browser__show_callchain(struct hist_browser *browser, 553 struct rb_root *root, int level, 554 unsigned short row, u64 total, 555 print_callchain_entry_fn print, 556 struct callchain_print_arg *arg, 557 check_output_full_fn is_output_full) 558 { 559 struct rb_node *node; 560 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 561 u64 new_total; 562 563 node = rb_first(root); 564 while (node) { 565 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 566 struct rb_node *next = rb_next(node); 567 u64 cumul = callchain_cumul_hits(child); 568 struct callchain_list *chain; 569 char folded_sign = ' '; 570 int first = true; 571 int extra_offset = 0; 572 573 list_for_each_entry(chain, &child->val, list) { 574 char bf[1024], *alloc_str; 575 const char *str; 576 bool was_first = first; 577 578 if (first) 579 first = false; 580 else if (level > 1) 581 extra_offset = LEVEL_OFFSET_STEP; 582 583 folded_sign = callchain_list__folded(chain); 584 if (arg->row_offset != 0) { 585 arg->row_offset--; 586 goto do_next; 587 } 588 589 alloc_str = NULL; 590 str = callchain_list__sym_name(chain, bf, sizeof(bf), 591 browser->show_dso); 592 593 if (was_first && level > 1) { 594 double percent = cumul * 100.0 / total; 595 596 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) 597 str = "Not enough memory!"; 598 else 599 str = alloc_str; 600 } 601 602 print(browser, chain, str, offset + extra_offset, row, arg); 603 604 free(alloc_str); 605 606 if (is_output_full(browser, ++row)) 607 goto out; 608 do_next: 609 if (folded_sign == '+') 610 break; 611 } 612 613 if (folded_sign == '-') { 614 const int new_level = level + (extra_offset ? 2 : 1); 615 616 if (callchain_param.mode == CHAIN_GRAPH_REL) 617 new_total = child->children_hit; 618 else 619 new_total = total; 620 621 row += hist_browser__show_callchain(browser, &child->rb_root, 622 new_level, row, new_total, 623 print, arg, is_output_full); 624 } 625 if (is_output_full(browser, row)) 626 break; 627 node = next; 628 } 629 out: 630 return row - first_row; 631 } 632 633 struct hpp_arg { 634 struct ui_browser *b; 635 char folded_sign; 636 bool current_entry; 637 }; 638 639 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 640 { 641 struct hpp_arg *arg = hpp->ptr; 642 int ret, len; 643 va_list args; 644 double percent; 645 646 va_start(args, fmt); 647 len = va_arg(args, int); 648 percent = va_arg(args, double); 649 va_end(args); 650 651 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 652 653 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 654 slsmg_printf("%s", hpp->buf); 655 656 advance_hpp(hpp, ret); 657 return ret; 658 } 659 660 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 661 static u64 __hpp_get_##_field(struct hist_entry *he) \ 662 { \ 663 return he->stat._field; \ 664 } \ 665 \ 666 static int \ 667 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 668 struct perf_hpp *hpp, \ 669 struct hist_entry *he) \ 670 { \ 671 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 672 __hpp__slsmg_color_printf, true); \ 673 } 674 675 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 676 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 677 { \ 678 return he->stat_acc->_field; \ 679 } \ 680 \ 681 static int \ 682 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 683 struct perf_hpp *hpp, \ 684 struct hist_entry *he) \ 685 { \ 686 if (!symbol_conf.cumulate_callchain) { \ 687 int len = fmt->user_len ?: fmt->len; \ 688 int ret = scnprintf(hpp->buf, hpp->size, \ 689 "%*s", len, "N/A"); \ 690 slsmg_printf("%s", hpp->buf); \ 691 \ 692 return ret; \ 693 } \ 694 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ 695 " %*.2f%%", __hpp__slsmg_color_printf, true); \ 696 } 697 698 __HPP_COLOR_PERCENT_FN(overhead, period) 699 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 700 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 701 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 702 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 703 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 704 705 #undef __HPP_COLOR_PERCENT_FN 706 #undef __HPP_COLOR_ACC_PERCENT_FN 707 708 void hist_browser__init_hpp(void) 709 { 710 perf_hpp__format[PERF_HPP__OVERHEAD].color = 711 hist_browser__hpp_color_overhead; 712 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 713 hist_browser__hpp_color_overhead_sys; 714 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 715 hist_browser__hpp_color_overhead_us; 716 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 717 hist_browser__hpp_color_overhead_guest_sys; 718 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 719 hist_browser__hpp_color_overhead_guest_us; 720 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 721 hist_browser__hpp_color_overhead_acc; 722 } 723 724 static int hist_browser__show_entry(struct hist_browser *browser, 725 struct hist_entry *entry, 726 unsigned short row) 727 { 728 char s[256]; 729 int printed = 0; 730 int width = browser->b.width; 731 char folded_sign = ' '; 732 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 733 off_t row_offset = entry->row_offset; 734 bool first = true; 735 struct perf_hpp_fmt *fmt; 736 737 if (current_entry) { 738 browser->he_selection = entry; 739 browser->selection = &entry->ms; 740 } 741 742 if (symbol_conf.use_callchain) { 743 hist_entry__init_have_children(entry); 744 folded_sign = hist_entry__folded(entry); 745 } 746 747 if (row_offset == 0) { 748 struct hpp_arg arg = { 749 .b = &browser->b, 750 .folded_sign = folded_sign, 751 .current_entry = current_entry, 752 }; 753 struct perf_hpp hpp = { 754 .buf = s, 755 .size = sizeof(s), 756 .ptr = &arg, 757 }; 758 759 hist_browser__gotorc(browser, row, 0); 760 761 perf_hpp__for_each_format(fmt) { 762 if (perf_hpp__should_skip(fmt)) 763 continue; 764 765 if (current_entry && browser->b.navkeypressed) { 766 ui_browser__set_color(&browser->b, 767 HE_COLORSET_SELECTED); 768 } else { 769 ui_browser__set_color(&browser->b, 770 HE_COLORSET_NORMAL); 771 } 772 773 if (first) { 774 if (symbol_conf.use_callchain) { 775 slsmg_printf("%c ", folded_sign); 776 width -= 2; 777 } 778 first = false; 779 } else { 780 slsmg_printf(" "); 781 width -= 2; 782 } 783 784 if (fmt->color) { 785 width -= fmt->color(fmt, &hpp, entry); 786 } else { 787 width -= fmt->entry(fmt, &hpp, entry); 788 slsmg_printf("%s", s); 789 } 790 } 791 792 /* The scroll bar isn't being used */ 793 if (!browser->b.navkeypressed) 794 width += 1; 795 796 slsmg_write_nstring("", width); 797 798 ++row; 799 ++printed; 800 } else 801 --row_offset; 802 803 if (folded_sign == '-' && row != browser->b.rows) { 804 u64 total = hists__total_period(entry->hists); 805 struct callchain_print_arg arg = { 806 .row_offset = row_offset, 807 .is_current_entry = current_entry, 808 }; 809 810 printed += hist_browser__show_callchain(browser, 811 &entry->sorted_chain, 1, row, total, 812 hist_browser__show_callchain_entry, &arg, 813 hist_browser__check_output_full); 814 815 if (arg.is_current_entry) 816 browser->he_selection = entry; 817 } 818 819 return printed; 820 } 821 822 static int advance_hpp_check(struct perf_hpp *hpp, int inc) 823 { 824 advance_hpp(hpp, inc); 825 return hpp->size <= 0; 826 } 827 828 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) 829 { 830 struct perf_hpp dummy_hpp = { 831 .buf = buf, 832 .size = size, 833 }; 834 struct perf_hpp_fmt *fmt; 835 size_t ret = 0; 836 837 if (symbol_conf.use_callchain) { 838 ret = scnprintf(buf, size, " "); 839 if (advance_hpp_check(&dummy_hpp, ret)) 840 return ret; 841 } 842 843 perf_hpp__for_each_format(fmt) { 844 if (perf_hpp__should_skip(fmt)) 845 continue; 846 847 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); 848 if (advance_hpp_check(&dummy_hpp, ret)) 849 break; 850 851 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 852 if (advance_hpp_check(&dummy_hpp, ret)) 853 break; 854 } 855 856 return ret; 857 } 858 859 static void hist_browser__show_headers(struct hist_browser *browser) 860 { 861 char headers[1024]; 862 863 hists__scnprintf_headers(headers, sizeof(headers), browser->hists); 864 ui_browser__gotorc(&browser->b, 0, 0); 865 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 866 slsmg_write_nstring(headers, browser->b.width + 1); 867 } 868 869 static void ui_browser__hists_init_top(struct ui_browser *browser) 870 { 871 if (browser->top == NULL) { 872 struct hist_browser *hb; 873 874 hb = container_of(browser, struct hist_browser, b); 875 browser->top = rb_first(&hb->hists->entries); 876 } 877 } 878 879 static unsigned int hist_browser__refresh(struct ui_browser *browser) 880 { 881 unsigned row = 0; 882 u16 header_offset = 0; 883 struct rb_node *nd; 884 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 885 886 if (hb->show_headers) { 887 hist_browser__show_headers(hb); 888 header_offset = 1; 889 } 890 891 ui_browser__hists_init_top(browser); 892 893 for (nd = browser->top; nd; nd = rb_next(nd)) { 894 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 895 float percent; 896 897 if (h->filtered) 898 continue; 899 900 percent = hist_entry__get_percent_limit(h); 901 if (percent < hb->min_pcnt) 902 continue; 903 904 row += hist_browser__show_entry(hb, h, row); 905 if (row == browser->rows) 906 break; 907 } 908 909 return row + header_offset; 910 } 911 912 static struct rb_node *hists__filter_entries(struct rb_node *nd, 913 float min_pcnt) 914 { 915 while (nd != NULL) { 916 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 917 float percent = hist_entry__get_percent_limit(h); 918 919 if (!h->filtered && percent >= min_pcnt) 920 return nd; 921 922 nd = rb_next(nd); 923 } 924 925 return NULL; 926 } 927 928 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 929 float min_pcnt) 930 { 931 while (nd != NULL) { 932 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 933 float percent = hist_entry__get_percent_limit(h); 934 935 if (!h->filtered && percent >= min_pcnt) 936 return nd; 937 938 nd = rb_prev(nd); 939 } 940 941 return NULL; 942 } 943 944 static void ui_browser__hists_seek(struct ui_browser *browser, 945 off_t offset, int whence) 946 { 947 struct hist_entry *h; 948 struct rb_node *nd; 949 bool first = true; 950 struct hist_browser *hb; 951 952 hb = container_of(browser, struct hist_browser, b); 953 954 if (browser->nr_entries == 0) 955 return; 956 957 ui_browser__hists_init_top(browser); 958 959 switch (whence) { 960 case SEEK_SET: 961 nd = hists__filter_entries(rb_first(browser->entries), 962 hb->min_pcnt); 963 break; 964 case SEEK_CUR: 965 nd = browser->top; 966 goto do_offset; 967 case SEEK_END: 968 nd = hists__filter_prev_entries(rb_last(browser->entries), 969 hb->min_pcnt); 970 first = false; 971 break; 972 default: 973 return; 974 } 975 976 /* 977 * Moves not relative to the first visible entry invalidates its 978 * row_offset: 979 */ 980 h = rb_entry(browser->top, struct hist_entry, rb_node); 981 h->row_offset = 0; 982 983 /* 984 * Here we have to check if nd is expanded (+), if it is we can't go 985 * the next top level hist_entry, instead we must compute an offset of 986 * what _not_ to show and not change the first visible entry. 987 * 988 * This offset increments when we are going from top to bottom and 989 * decreases when we're going from bottom to top. 990 * 991 * As we don't have backpointers to the top level in the callchains 992 * structure, we need to always print the whole hist_entry callchain, 993 * skipping the first ones that are before the first visible entry 994 * and stop when we printed enough lines to fill the screen. 995 */ 996 do_offset: 997 if (offset > 0) { 998 do { 999 h = rb_entry(nd, struct hist_entry, rb_node); 1000 if (h->ms.unfolded) { 1001 u16 remaining = h->nr_rows - h->row_offset; 1002 if (offset > remaining) { 1003 offset -= remaining; 1004 h->row_offset = 0; 1005 } else { 1006 h->row_offset += offset; 1007 offset = 0; 1008 browser->top = nd; 1009 break; 1010 } 1011 } 1012 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt); 1013 if (nd == NULL) 1014 break; 1015 --offset; 1016 browser->top = nd; 1017 } while (offset != 0); 1018 } else if (offset < 0) { 1019 while (1) { 1020 h = rb_entry(nd, struct hist_entry, rb_node); 1021 if (h->ms.unfolded) { 1022 if (first) { 1023 if (-offset > h->row_offset) { 1024 offset += h->row_offset; 1025 h->row_offset = 0; 1026 } else { 1027 h->row_offset += offset; 1028 offset = 0; 1029 browser->top = nd; 1030 break; 1031 } 1032 } else { 1033 if (-offset > h->nr_rows) { 1034 offset += h->nr_rows; 1035 h->row_offset = 0; 1036 } else { 1037 h->row_offset = h->nr_rows + offset; 1038 offset = 0; 1039 browser->top = nd; 1040 break; 1041 } 1042 } 1043 } 1044 1045 nd = hists__filter_prev_entries(rb_prev(nd), 1046 hb->min_pcnt); 1047 if (nd == NULL) 1048 break; 1049 ++offset; 1050 browser->top = nd; 1051 if (offset == 0) { 1052 /* 1053 * Last unfiltered hist_entry, check if it is 1054 * unfolded, if it is then we should have 1055 * row_offset at its last entry. 1056 */ 1057 h = rb_entry(nd, struct hist_entry, rb_node); 1058 if (h->ms.unfolded) 1059 h->row_offset = h->nr_rows; 1060 break; 1061 } 1062 first = false; 1063 } 1064 } else { 1065 browser->top = nd; 1066 h = rb_entry(nd, struct hist_entry, rb_node); 1067 h->row_offset = 0; 1068 } 1069 } 1070 1071 static int hist_browser__fprintf_callchain(struct hist_browser *browser, 1072 struct hist_entry *he, FILE *fp) 1073 { 1074 u64 total = hists__total_period(he->hists); 1075 struct callchain_print_arg arg = { 1076 .fp = fp, 1077 }; 1078 1079 if (symbol_conf.cumulate_callchain) 1080 total = he->stat_acc->period; 1081 1082 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, 1083 hist_browser__fprintf_callchain_entry, &arg, 1084 hist_browser__check_dump_full); 1085 return arg.printed; 1086 } 1087 1088 static int hist_browser__fprintf_entry(struct hist_browser *browser, 1089 struct hist_entry *he, FILE *fp) 1090 { 1091 char s[8192]; 1092 int printed = 0; 1093 char folded_sign = ' '; 1094 struct perf_hpp hpp = { 1095 .buf = s, 1096 .size = sizeof(s), 1097 }; 1098 struct perf_hpp_fmt *fmt; 1099 bool first = true; 1100 int ret; 1101 1102 if (symbol_conf.use_callchain) 1103 folded_sign = hist_entry__folded(he); 1104 1105 if (symbol_conf.use_callchain) 1106 printed += fprintf(fp, "%c ", folded_sign); 1107 1108 perf_hpp__for_each_format(fmt) { 1109 if (perf_hpp__should_skip(fmt)) 1110 continue; 1111 1112 if (!first) { 1113 ret = scnprintf(hpp.buf, hpp.size, " "); 1114 advance_hpp(&hpp, ret); 1115 } else 1116 first = false; 1117 1118 ret = fmt->entry(fmt, &hpp, he); 1119 advance_hpp(&hpp, ret); 1120 } 1121 printed += fprintf(fp, "%s\n", rtrim(s)); 1122 1123 if (folded_sign == '-') 1124 printed += hist_browser__fprintf_callchain(browser, he, fp); 1125 1126 return printed; 1127 } 1128 1129 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 1130 { 1131 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 1132 browser->min_pcnt); 1133 int printed = 0; 1134 1135 while (nd) { 1136 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1137 1138 printed += hist_browser__fprintf_entry(browser, h, fp); 1139 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); 1140 } 1141 1142 return printed; 1143 } 1144 1145 static int hist_browser__dump(struct hist_browser *browser) 1146 { 1147 char filename[64]; 1148 FILE *fp; 1149 1150 while (1) { 1151 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 1152 if (access(filename, F_OK)) 1153 break; 1154 /* 1155 * XXX: Just an arbitrary lazy upper limit 1156 */ 1157 if (++browser->print_seq == 8192) { 1158 ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 1159 return -1; 1160 } 1161 } 1162 1163 fp = fopen(filename, "w"); 1164 if (fp == NULL) { 1165 char bf[64]; 1166 const char *err = strerror_r(errno, bf, sizeof(bf)); 1167 ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 1168 return -1; 1169 } 1170 1171 ++browser->print_seq; 1172 hist_browser__fprintf(browser, fp); 1173 fclose(fp); 1174 ui_helpline__fpush("%s written!", filename); 1175 1176 return 0; 1177 } 1178 1179 static struct hist_browser *hist_browser__new(struct hists *hists) 1180 { 1181 struct hist_browser *browser = zalloc(sizeof(*browser)); 1182 1183 if (browser) { 1184 browser->hists = hists; 1185 browser->b.refresh = hist_browser__refresh; 1186 browser->b.refresh_dimensions = hist_browser__refresh_dimensions; 1187 browser->b.seek = ui_browser__hists_seek; 1188 browser->b.use_navkeypressed = true; 1189 browser->show_headers = symbol_conf.show_hist_headers; 1190 } 1191 1192 return browser; 1193 } 1194 1195 static void hist_browser__delete(struct hist_browser *browser) 1196 { 1197 free(browser); 1198 } 1199 1200 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 1201 { 1202 return browser->he_selection; 1203 } 1204 1205 static struct thread *hist_browser__selected_thread(struct hist_browser *browser) 1206 { 1207 return browser->he_selection->thread; 1208 } 1209 1210 /* Check whether the browser is for 'top' or 'report' */ 1211 static inline bool is_report_browser(void *timer) 1212 { 1213 return timer == NULL; 1214 } 1215 1216 static int hists__browser_title(struct hists *hists, 1217 struct hist_browser_timer *hbt, 1218 char *bf, size_t size) 1219 { 1220 char unit; 1221 int printed; 1222 const struct dso *dso = hists->dso_filter; 1223 const struct thread *thread = hists->thread_filter; 1224 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1225 u64 nr_events = hists->stats.total_period; 1226 struct perf_evsel *evsel = hists_to_evsel(hists); 1227 const char *ev_name = perf_evsel__name(evsel); 1228 char buf[512]; 1229 size_t buflen = sizeof(buf); 1230 1231 if (symbol_conf.filter_relative) { 1232 nr_samples = hists->stats.nr_non_filtered_samples; 1233 nr_events = hists->stats.total_non_filtered_period; 1234 } 1235 1236 if (perf_evsel__is_group_event(evsel)) { 1237 struct perf_evsel *pos; 1238 1239 perf_evsel__group_desc(evsel, buf, buflen); 1240 ev_name = buf; 1241 1242 for_each_group_member(pos, evsel) { 1243 struct hists *pos_hists = evsel__hists(pos); 1244 1245 if (symbol_conf.filter_relative) { 1246 nr_samples += pos_hists->stats.nr_non_filtered_samples; 1247 nr_events += pos_hists->stats.total_non_filtered_period; 1248 } else { 1249 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1250 nr_events += pos_hists->stats.total_period; 1251 } 1252 } 1253 } 1254 1255 nr_samples = convert_unit(nr_samples, &unit); 1256 printed = scnprintf(bf, size, 1257 "Samples: %lu%c of event '%s', Event count (approx.): %lu", 1258 nr_samples, unit, ev_name, nr_events); 1259 1260 1261 if (hists->uid_filter_str) 1262 printed += snprintf(bf + printed, size - printed, 1263 ", UID: %s", hists->uid_filter_str); 1264 if (thread) 1265 printed += scnprintf(bf + printed, size - printed, 1266 ", Thread: %s(%d)", 1267 (thread->comm_set ? thread__comm_str(thread) : ""), 1268 thread->tid); 1269 if (dso) 1270 printed += scnprintf(bf + printed, size - printed, 1271 ", DSO: %s", dso->short_name); 1272 if (!is_report_browser(hbt)) { 1273 struct perf_top *top = hbt->arg; 1274 1275 if (top->zero) 1276 printed += scnprintf(bf + printed, size - printed, " [z]"); 1277 } 1278 1279 return printed; 1280 } 1281 1282 static inline void free_popup_options(char **options, int n) 1283 { 1284 int i; 1285 1286 for (i = 0; i < n; ++i) 1287 zfree(&options[i]); 1288 } 1289 1290 /* 1291 * Only runtime switching of perf data file will make "input_name" point 1292 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 1293 * whether we need to call free() for current "input_name" during the switch. 1294 */ 1295 static bool is_input_name_malloced = false; 1296 1297 static int switch_data_file(void) 1298 { 1299 char *pwd, *options[32], *abs_path[32], *tmp; 1300 DIR *pwd_dir; 1301 int nr_options = 0, choice = -1, ret = -1; 1302 struct dirent *dent; 1303 1304 pwd = getenv("PWD"); 1305 if (!pwd) 1306 return ret; 1307 1308 pwd_dir = opendir(pwd); 1309 if (!pwd_dir) 1310 return ret; 1311 1312 memset(options, 0, sizeof(options)); 1313 memset(options, 0, sizeof(abs_path)); 1314 1315 while ((dent = readdir(pwd_dir))) { 1316 char path[PATH_MAX]; 1317 u64 magic; 1318 char *name = dent->d_name; 1319 FILE *file; 1320 1321 if (!(dent->d_type == DT_REG)) 1322 continue; 1323 1324 snprintf(path, sizeof(path), "%s/%s", pwd, name); 1325 1326 file = fopen(path, "r"); 1327 if (!file) 1328 continue; 1329 1330 if (fread(&magic, 1, 8, file) < 8) 1331 goto close_file_and_continue; 1332 1333 if (is_perf_magic(magic)) { 1334 options[nr_options] = strdup(name); 1335 if (!options[nr_options]) 1336 goto close_file_and_continue; 1337 1338 abs_path[nr_options] = strdup(path); 1339 if (!abs_path[nr_options]) { 1340 zfree(&options[nr_options]); 1341 ui__warning("Can't search all data files due to memory shortage.\n"); 1342 fclose(file); 1343 break; 1344 } 1345 1346 nr_options++; 1347 } 1348 1349 close_file_and_continue: 1350 fclose(file); 1351 if (nr_options >= 32) { 1352 ui__warning("Too many perf data files in PWD!\n" 1353 "Only the first 32 files will be listed.\n"); 1354 break; 1355 } 1356 } 1357 closedir(pwd_dir); 1358 1359 if (nr_options) { 1360 choice = ui__popup_menu(nr_options, options); 1361 if (choice < nr_options && choice >= 0) { 1362 tmp = strdup(abs_path[choice]); 1363 if (tmp) { 1364 if (is_input_name_malloced) 1365 free((void *)input_name); 1366 input_name = tmp; 1367 is_input_name_malloced = true; 1368 ret = 0; 1369 } else 1370 ui__warning("Data switch failed due to memory shortage!\n"); 1371 } 1372 } 1373 1374 free_popup_options(options, nr_options); 1375 free_popup_options(abs_path, nr_options); 1376 return ret; 1377 } 1378 1379 static void hist_browser__update_nr_entries(struct hist_browser *hb) 1380 { 1381 u64 nr_entries = 0; 1382 struct rb_node *nd = rb_first(&hb->hists->entries); 1383 1384 if (hb->min_pcnt == 0) { 1385 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 1386 return; 1387 } 1388 1389 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 1390 nr_entries++; 1391 nd = rb_next(nd); 1392 } 1393 1394 hb->nr_non_filtered_entries = nr_entries; 1395 } 1396 1397 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1398 const char *helpline, 1399 bool left_exits, 1400 struct hist_browser_timer *hbt, 1401 float min_pcnt, 1402 struct perf_session_env *env) 1403 { 1404 struct hists *hists = evsel__hists(evsel); 1405 struct hist_browser *browser = hist_browser__new(hists); 1406 struct branch_info *bi; 1407 struct pstack *fstack; 1408 char *options[16]; 1409 int nr_options = 0; 1410 int key = -1; 1411 char buf[64]; 1412 char script_opt[64]; 1413 int delay_secs = hbt ? hbt->refresh : 0; 1414 struct perf_hpp_fmt *fmt; 1415 1416 #define HIST_BROWSER_HELP_COMMON \ 1417 "h/?/F1 Show this window\n" \ 1418 "UP/DOWN/PGUP\n" \ 1419 "PGDN/SPACE Navigate\n" \ 1420 "q/ESC/CTRL+C Exit browser\n\n" \ 1421 "For multiple event sessions:\n\n" \ 1422 "TAB/UNTAB Switch events\n\n" \ 1423 "For symbolic views (--sort has sym):\n\n" \ 1424 "-> Zoom into DSO/Threads & Annotate current symbol\n" \ 1425 "<- Zoom out\n" \ 1426 "a Annotate current symbol\n" \ 1427 "C Collapse all callchains\n" \ 1428 "d Zoom into current DSO\n" \ 1429 "E Expand all callchains\n" \ 1430 "F Toggle percentage of filtered entries\n" \ 1431 "H Display column headers\n" \ 1432 1433 /* help messages are sorted by lexical order of the hotkey */ 1434 const char report_help[] = HIST_BROWSER_HELP_COMMON 1435 "i Show header information\n" 1436 "P Print histograms to perf.hist.N\n" 1437 "r Run available scripts\n" 1438 "s Switch to another data file in PWD\n" 1439 "t Zoom into current Thread\n" 1440 "V Verbose (DSO names in callchains, etc)\n" 1441 "/ Filter symbol by name"; 1442 const char top_help[] = HIST_BROWSER_HELP_COMMON 1443 "P Print histograms to perf.hist.N\n" 1444 "t Zoom into current Thread\n" 1445 "V Verbose (DSO names in callchains, etc)\n" 1446 "z Toggle zeroing of samples\n" 1447 "/ Filter symbol by name"; 1448 1449 if (browser == NULL) 1450 return -1; 1451 1452 if (min_pcnt) { 1453 browser->min_pcnt = min_pcnt; 1454 hist_browser__update_nr_entries(browser); 1455 } 1456 1457 fstack = pstack__new(2); 1458 if (fstack == NULL) 1459 goto out; 1460 1461 ui_helpline__push(helpline); 1462 1463 memset(options, 0, sizeof(options)); 1464 1465 perf_hpp__for_each_format(fmt) 1466 perf_hpp__reset_width(fmt, hists); 1467 1468 if (symbol_conf.col_width_list_str) 1469 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 1470 1471 while (1) { 1472 const struct thread *thread = NULL; 1473 const struct dso *dso = NULL; 1474 int choice = 0, 1475 annotate = -2, zoom_dso = -2, zoom_thread = -2, 1476 annotate_f = -2, annotate_t = -2, browse_map = -2; 1477 int scripts_comm = -2, scripts_symbol = -2, 1478 scripts_all = -2, switch_data = -2; 1479 1480 nr_options = 0; 1481 1482 key = hist_browser__run(browser, hbt); 1483 1484 if (browser->he_selection != NULL) { 1485 thread = hist_browser__selected_thread(browser); 1486 dso = browser->selection->map ? browser->selection->map->dso : NULL; 1487 } 1488 switch (key) { 1489 case K_TAB: 1490 case K_UNTAB: 1491 if (nr_events == 1) 1492 continue; 1493 /* 1494 * Exit the browser, let hists__browser_tree 1495 * go to the next or previous 1496 */ 1497 goto out_free_stack; 1498 case 'a': 1499 if (!sort__has_sym) { 1500 ui_browser__warning(&browser->b, delay_secs * 2, 1501 "Annotation is only available for symbolic views, " 1502 "include \"sym*\" in --sort to use it."); 1503 continue; 1504 } 1505 1506 if (browser->selection == NULL || 1507 browser->selection->sym == NULL || 1508 browser->selection->map->dso->annotate_warned) 1509 continue; 1510 goto do_annotate; 1511 case 'P': 1512 hist_browser__dump(browser); 1513 continue; 1514 case 'd': 1515 goto zoom_dso; 1516 case 'V': 1517 browser->show_dso = !browser->show_dso; 1518 continue; 1519 case 't': 1520 goto zoom_thread; 1521 case '/': 1522 if (ui_browser__input_window("Symbol to show", 1523 "Please enter the name of symbol you want to see", 1524 buf, "ENTER: OK, ESC: Cancel", 1525 delay_secs * 2) == K_ENTER) { 1526 hists->symbol_filter_str = *buf ? buf : NULL; 1527 hists__filter_by_symbol(hists); 1528 hist_browser__reset(browser); 1529 } 1530 continue; 1531 case 'r': 1532 if (is_report_browser(hbt)) 1533 goto do_scripts; 1534 continue; 1535 case 's': 1536 if (is_report_browser(hbt)) 1537 goto do_data_switch; 1538 continue; 1539 case 'i': 1540 /* env->arch is NULL for live-mode (i.e. perf top) */ 1541 if (env->arch) 1542 tui__header_window(env); 1543 continue; 1544 case 'F': 1545 symbol_conf.filter_relative ^= 1; 1546 continue; 1547 case 'z': 1548 if (!is_report_browser(hbt)) { 1549 struct perf_top *top = hbt->arg; 1550 1551 top->zero = !top->zero; 1552 } 1553 continue; 1554 case K_F1: 1555 case 'h': 1556 case '?': 1557 ui_browser__help_window(&browser->b, 1558 is_report_browser(hbt) ? report_help : top_help); 1559 continue; 1560 case K_ENTER: 1561 case K_RIGHT: 1562 /* menu */ 1563 break; 1564 case K_LEFT: { 1565 const void *top; 1566 1567 if (pstack__empty(fstack)) { 1568 /* 1569 * Go back to the perf_evsel_menu__run or other user 1570 */ 1571 if (left_exits) 1572 goto out_free_stack; 1573 continue; 1574 } 1575 top = pstack__pop(fstack); 1576 if (top == &browser->hists->dso_filter) 1577 goto zoom_out_dso; 1578 if (top == &browser->hists->thread_filter) 1579 goto zoom_out_thread; 1580 continue; 1581 } 1582 case K_ESC: 1583 if (!left_exits && 1584 !ui_browser__dialog_yesno(&browser->b, 1585 "Do you really want to exit?")) 1586 continue; 1587 /* Fall thru */ 1588 case 'q': 1589 case CTRL('c'): 1590 goto out_free_stack; 1591 default: 1592 continue; 1593 } 1594 1595 if (!sort__has_sym) 1596 goto add_exit_option; 1597 1598 if (sort__mode == SORT_MODE__BRANCH) { 1599 bi = browser->he_selection->branch_info; 1600 if (browser->selection != NULL && 1601 bi && 1602 bi->from.sym != NULL && 1603 !bi->from.map->dso->annotate_warned && 1604 asprintf(&options[nr_options], "Annotate %s", 1605 bi->from.sym->name) > 0) 1606 annotate_f = nr_options++; 1607 1608 if (browser->selection != NULL && 1609 bi && 1610 bi->to.sym != NULL && 1611 !bi->to.map->dso->annotate_warned && 1612 (bi->to.sym != bi->from.sym || 1613 bi->to.map->dso != bi->from.map->dso) && 1614 asprintf(&options[nr_options], "Annotate %s", 1615 bi->to.sym->name) > 0) 1616 annotate_t = nr_options++; 1617 } else { 1618 if (browser->selection != NULL && 1619 browser->selection->sym != NULL && 1620 !browser->selection->map->dso->annotate_warned) { 1621 struct annotation *notes; 1622 1623 notes = symbol__annotation(browser->selection->sym); 1624 1625 if (notes->src && 1626 asprintf(&options[nr_options], "Annotate %s", 1627 browser->selection->sym->name) > 0) 1628 annotate = nr_options++; 1629 } 1630 } 1631 1632 if (thread != NULL && 1633 asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 1634 (browser->hists->thread_filter ? "out of" : "into"), 1635 (thread->comm_set ? thread__comm_str(thread) : ""), 1636 thread->tid) > 0) 1637 zoom_thread = nr_options++; 1638 1639 if (dso != NULL && 1640 asprintf(&options[nr_options], "Zoom %s %s DSO", 1641 (browser->hists->dso_filter ? "out of" : "into"), 1642 (dso->kernel ? "the Kernel" : dso->short_name)) > 0) 1643 zoom_dso = nr_options++; 1644 1645 if (browser->selection != NULL && 1646 browser->selection->map != NULL && 1647 asprintf(&options[nr_options], "Browse map details") > 0) 1648 browse_map = nr_options++; 1649 1650 /* perf script support */ 1651 if (browser->he_selection) { 1652 struct symbol *sym; 1653 1654 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", 1655 thread__comm_str(browser->he_selection->thread)) > 0) 1656 scripts_comm = nr_options++; 1657 1658 sym = browser->he_selection->ms.sym; 1659 if (sym && sym->namelen && 1660 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", 1661 sym->name) > 0) 1662 scripts_symbol = nr_options++; 1663 } 1664 1665 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) 1666 scripts_all = nr_options++; 1667 1668 if (is_report_browser(hbt) && asprintf(&options[nr_options], 1669 "Switch to another data file in PWD") > 0) 1670 switch_data = nr_options++; 1671 add_exit_option: 1672 options[nr_options++] = (char *)"Exit"; 1673 retry_popup_menu: 1674 choice = ui__popup_menu(nr_options, options); 1675 1676 if (choice == nr_options - 1) 1677 break; 1678 1679 if (choice == -1) { 1680 free_popup_options(options, nr_options - 1); 1681 continue; 1682 } 1683 1684 if (choice == annotate || choice == annotate_t || choice == annotate_f) { 1685 struct hist_entry *he; 1686 struct annotation *notes; 1687 int err; 1688 do_annotate: 1689 if (!objdump_path && perf_session_env__lookup_objdump(env)) 1690 continue; 1691 1692 he = hist_browser__selected_entry(browser); 1693 if (he == NULL) 1694 continue; 1695 1696 /* 1697 * we stash the branch_info symbol + map into the 1698 * the ms so we don't have to rewrite all the annotation 1699 * code to use branch_info. 1700 * in branch mode, the ms struct is not used 1701 */ 1702 if (choice == annotate_f) { 1703 he->ms.sym = he->branch_info->from.sym; 1704 he->ms.map = he->branch_info->from.map; 1705 } else if (choice == annotate_t) { 1706 he->ms.sym = he->branch_info->to.sym; 1707 he->ms.map = he->branch_info->to.map; 1708 } 1709 1710 notes = symbol__annotation(he->ms.sym); 1711 if (!notes->src) 1712 continue; 1713 1714 /* 1715 * Don't let this be freed, say, by hists__decay_entry. 1716 */ 1717 he->used = true; 1718 err = hist_entry__tui_annotate(he, evsel, hbt); 1719 he->used = false; 1720 /* 1721 * offer option to annotate the other branch source or target 1722 * (if they exists) when returning from annotate 1723 */ 1724 if ((err == 'q' || err == CTRL('c')) 1725 && annotate_t != -2 && annotate_f != -2) 1726 goto retry_popup_menu; 1727 1728 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1729 if (err) 1730 ui_browser__handle_resize(&browser->b); 1731 1732 } else if (choice == browse_map) 1733 map__browse(browser->selection->map); 1734 else if (choice == zoom_dso) { 1735 zoom_dso: 1736 if (browser->hists->dso_filter) { 1737 pstack__remove(fstack, &browser->hists->dso_filter); 1738 zoom_out_dso: 1739 ui_helpline__pop(); 1740 browser->hists->dso_filter = NULL; 1741 perf_hpp__set_elide(HISTC_DSO, false); 1742 } else { 1743 if (dso == NULL) 1744 continue; 1745 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1746 dso->kernel ? "the Kernel" : dso->short_name); 1747 browser->hists->dso_filter = dso; 1748 perf_hpp__set_elide(HISTC_DSO, true); 1749 pstack__push(fstack, &browser->hists->dso_filter); 1750 } 1751 hists__filter_by_dso(hists); 1752 hist_browser__reset(browser); 1753 } else if (choice == zoom_thread) { 1754 zoom_thread: 1755 if (browser->hists->thread_filter) { 1756 pstack__remove(fstack, &browser->hists->thread_filter); 1757 zoom_out_thread: 1758 ui_helpline__pop(); 1759 browser->hists->thread_filter = NULL; 1760 perf_hpp__set_elide(HISTC_THREAD, false); 1761 } else { 1762 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1763 thread->comm_set ? thread__comm_str(thread) : "", 1764 thread->tid); 1765 browser->hists->thread_filter = thread; 1766 perf_hpp__set_elide(HISTC_THREAD, false); 1767 pstack__push(fstack, &browser->hists->thread_filter); 1768 } 1769 hists__filter_by_thread(hists); 1770 hist_browser__reset(browser); 1771 } 1772 /* perf scripts support */ 1773 else if (choice == scripts_all || choice == scripts_comm || 1774 choice == scripts_symbol) { 1775 do_scripts: 1776 memset(script_opt, 0, 64); 1777 1778 if (choice == scripts_comm) 1779 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); 1780 1781 if (choice == scripts_symbol) 1782 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); 1783 1784 script_browse(script_opt); 1785 } 1786 /* Switch to another data file */ 1787 else if (choice == switch_data) { 1788 do_data_switch: 1789 if (!switch_data_file()) { 1790 key = K_SWITCH_INPUT_DATA; 1791 break; 1792 } else 1793 ui__warning("Won't switch the data files due to\n" 1794 "no valid data file get selected!\n"); 1795 } 1796 } 1797 out_free_stack: 1798 pstack__delete(fstack); 1799 out: 1800 hist_browser__delete(browser); 1801 free_popup_options(options, nr_options - 1); 1802 return key; 1803 } 1804 1805 struct perf_evsel_menu { 1806 struct ui_browser b; 1807 struct perf_evsel *selection; 1808 bool lost_events, lost_events_warned; 1809 float min_pcnt; 1810 struct perf_session_env *env; 1811 }; 1812 1813 static void perf_evsel_menu__write(struct ui_browser *browser, 1814 void *entry, int row) 1815 { 1816 struct perf_evsel_menu *menu = container_of(browser, 1817 struct perf_evsel_menu, b); 1818 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 1819 struct hists *hists = evsel__hists(evsel); 1820 bool current_entry = ui_browser__is_current_entry(browser, row); 1821 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1822 const char *ev_name = perf_evsel__name(evsel); 1823 char bf[256], unit; 1824 const char *warn = " "; 1825 size_t printed; 1826 1827 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 1828 HE_COLORSET_NORMAL); 1829 1830 if (perf_evsel__is_group_event(evsel)) { 1831 struct perf_evsel *pos; 1832 1833 ev_name = perf_evsel__group_name(evsel); 1834 1835 for_each_group_member(pos, evsel) { 1836 struct hists *pos_hists = evsel__hists(pos); 1837 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1838 } 1839 } 1840 1841 nr_events = convert_unit(nr_events, &unit); 1842 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 1843 unit, unit == ' ' ? "" : " ", ev_name); 1844 slsmg_printf("%s", bf); 1845 1846 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 1847 if (nr_events != 0) { 1848 menu->lost_events = true; 1849 if (!current_entry) 1850 ui_browser__set_color(browser, HE_COLORSET_TOP); 1851 nr_events = convert_unit(nr_events, &unit); 1852 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 1853 nr_events, unit, unit == ' ' ? "" : " "); 1854 warn = bf; 1855 } 1856 1857 slsmg_write_nstring(warn, browser->width - printed); 1858 1859 if (current_entry) 1860 menu->selection = evsel; 1861 } 1862 1863 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 1864 int nr_events, const char *help, 1865 struct hist_browser_timer *hbt) 1866 { 1867 struct perf_evlist *evlist = menu->b.priv; 1868 struct perf_evsel *pos; 1869 const char *title = "Available samples"; 1870 int delay_secs = hbt ? hbt->refresh : 0; 1871 int key; 1872 1873 if (ui_browser__show(&menu->b, title, 1874 "ESC: exit, ENTER|->: Browse histograms") < 0) 1875 return -1; 1876 1877 while (1) { 1878 key = ui_browser__run(&menu->b, delay_secs); 1879 1880 switch (key) { 1881 case K_TIMER: 1882 hbt->timer(hbt->arg); 1883 1884 if (!menu->lost_events_warned && menu->lost_events) { 1885 ui_browser__warn_lost_events(&menu->b); 1886 menu->lost_events_warned = true; 1887 } 1888 continue; 1889 case K_RIGHT: 1890 case K_ENTER: 1891 if (!menu->selection) 1892 continue; 1893 pos = menu->selection; 1894 browse_hists: 1895 perf_evlist__set_selected(evlist, pos); 1896 /* 1897 * Give the calling tool a chance to populate the non 1898 * default evsel resorted hists tree. 1899 */ 1900 if (hbt) 1901 hbt->timer(hbt->arg); 1902 key = perf_evsel__hists_browse(pos, nr_events, help, 1903 true, hbt, 1904 menu->min_pcnt, 1905 menu->env); 1906 ui_browser__show_title(&menu->b, title); 1907 switch (key) { 1908 case K_TAB: 1909 if (pos->node.next == &evlist->entries) 1910 pos = perf_evlist__first(evlist); 1911 else 1912 pos = perf_evsel__next(pos); 1913 goto browse_hists; 1914 case K_UNTAB: 1915 if (pos->node.prev == &evlist->entries) 1916 pos = perf_evlist__last(evlist); 1917 else 1918 pos = perf_evsel__prev(pos); 1919 goto browse_hists; 1920 case K_ESC: 1921 if (!ui_browser__dialog_yesno(&menu->b, 1922 "Do you really want to exit?")) 1923 continue; 1924 /* Fall thru */ 1925 case K_SWITCH_INPUT_DATA: 1926 case 'q': 1927 case CTRL('c'): 1928 goto out; 1929 default: 1930 continue; 1931 } 1932 case K_LEFT: 1933 continue; 1934 case K_ESC: 1935 if (!ui_browser__dialog_yesno(&menu->b, 1936 "Do you really want to exit?")) 1937 continue; 1938 /* Fall thru */ 1939 case 'q': 1940 case CTRL('c'): 1941 goto out; 1942 default: 1943 continue; 1944 } 1945 } 1946 1947 out: 1948 ui_browser__hide(&menu->b); 1949 return key; 1950 } 1951 1952 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 1953 void *entry) 1954 { 1955 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 1956 1957 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 1958 return true; 1959 1960 return false; 1961 } 1962 1963 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1964 int nr_entries, const char *help, 1965 struct hist_browser_timer *hbt, 1966 float min_pcnt, 1967 struct perf_session_env *env) 1968 { 1969 struct perf_evsel *pos; 1970 struct perf_evsel_menu menu = { 1971 .b = { 1972 .entries = &evlist->entries, 1973 .refresh = ui_browser__list_head_refresh, 1974 .seek = ui_browser__list_head_seek, 1975 .write = perf_evsel_menu__write, 1976 .filter = filter_group_entries, 1977 .nr_entries = nr_entries, 1978 .priv = evlist, 1979 }, 1980 .min_pcnt = min_pcnt, 1981 .env = env, 1982 }; 1983 1984 ui_helpline__push("Press ESC to exit"); 1985 1986 evlist__for_each(evlist, pos) { 1987 const char *ev_name = perf_evsel__name(pos); 1988 size_t line_len = strlen(ev_name) + 7; 1989 1990 if (menu.b.width < line_len) 1991 menu.b.width = line_len; 1992 } 1993 1994 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 1995 } 1996 1997 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 1998 struct hist_browser_timer *hbt, 1999 float min_pcnt, 2000 struct perf_session_env *env) 2001 { 2002 int nr_entries = evlist->nr_entries; 2003 2004 single_entry: 2005 if (nr_entries == 1) { 2006 struct perf_evsel *first = perf_evlist__first(evlist); 2007 2008 return perf_evsel__hists_browse(first, nr_entries, help, 2009 false, hbt, min_pcnt, 2010 env); 2011 } 2012 2013 if (symbol_conf.event_group) { 2014 struct perf_evsel *pos; 2015 2016 nr_entries = 0; 2017 evlist__for_each(evlist, pos) { 2018 if (perf_evsel__is_group_leader(pos)) 2019 nr_entries++; 2020 } 2021 2022 if (nr_entries == 1) 2023 goto single_entry; 2024 } 2025 2026 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 2027 hbt, min_pcnt, env); 2028 } 2029