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