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 22 struct 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 38 extern void hist_browser__init_hpp(void); 39 40 static int hists__browser_title(struct hists *hists, 41 struct hist_browser_timer *hbt, 42 char *bf, size_t size); 43 static void hist_browser__update_nr_entries(struct hist_browser *hb); 44 45 static struct rb_node *hists__filter_entries(struct rb_node *nd, 46 float min_pcnt); 47 48 static 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 53 static 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 71 static 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 84 static 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 99 static 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 115 static 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 122 static 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 136 static char tree__folded_sign(bool unfolded) 137 { 138 return unfolded ? '-' : '+'; 139 } 140 141 static char hist_entry__folded(const struct hist_entry *he) 142 { 143 return he->has_children ? tree__folded_sign(he->unfolded) : ' '; 144 } 145 146 static char callchain_list__folded(const struct callchain_list *cl) 147 { 148 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; 149 } 150 151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) 152 { 153 cl->unfolded = unfold ? cl->has_children : false; 154 } 155 156 static 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 181 static 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 198 static 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 211 static 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 223 static 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 235 static 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 258 static 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 274 static 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 285 static 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 294 static 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 329 static 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 352 static 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 370 static 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 383 static 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 395 static 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 410 static 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 420 static 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 429 static 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 } 504 out: 505 ui_browser__hide(&browser->b); 506 return key; 507 } 508 509 struct 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 519 typedef 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 525 static 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 551 static 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 563 typedef bool (*check_output_full_fn)(struct hist_browser *browser, 564 unsigned short row); 565 566 static bool hist_browser__check_output_full(struct hist_browser *browser, 567 unsigned short row) 568 { 569 return browser->b.rows == row; 570 } 571 572 static 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 580 static 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; 639 do_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 } 660 out: 661 return row - first_row; 662 } 663 664 struct hpp_arg { 665 struct ui_browser *b; 666 char folded_sign; 667 bool current_entry; 668 }; 669 670 static 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) \ 692 static u64 __hpp_get_##_field(struct hist_entry *he) \ 693 { \ 694 return he->stat._field; \ 695 } \ 696 \ 697 static int \ 698 hist_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) \ 707 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 708 { \ 709 return he->stat_acc->_field; \ 710 } \ 711 \ 712 static int \ 713 hist_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 740 void 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 756 static 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 862 static int advance_hpp_check(struct perf_hpp *hpp, int inc) 863 { 864 advance_hpp(hpp, inc); 865 return hpp->size <= 0; 866 } 867 868 static 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 901 static 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 911 static 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 921 static 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 956 static 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 972 static 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 988 static 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 */ 1040 do_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 1118 static 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 1135 static 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 1176 static 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 1192 static 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 1226 static 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 1246 static void hist_browser__delete(struct hist_browser *browser) 1247 { 1248 free(browser); 1249 } 1250 1251 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 1252 { 1253 return browser->he_selection; 1254 } 1255 1256 static 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' */ 1262 static inline bool is_report_browser(void *timer) 1263 { 1264 return timer == NULL; 1265 } 1266 1267 static 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 1342 static 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 */ 1355 static bool is_input_name_malloced = false; 1356 1357 static 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 1409 close_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 1439 struct 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 1447 static int 1448 do_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 1478 static int 1479 add_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 1495 static int 1496 do_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 1519 static int 1520 add_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 1537 static int 1538 do_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 1562 static int 1563 add_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 1579 static int 1580 do_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 1587 static int 1588 add_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 1602 static int 1603 do_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 1621 static int 1622 add_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 1645 static int 1646 do_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 1658 static int 1659 add_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 1672 static int 1673 do_exit_browser(struct hist_browser *browser __maybe_unused, 1674 struct popup_action *act __maybe_unused) 1675 { 1676 return 0; 1677 } 1678 1679 static int 1680 add_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 1690 static int 1691 do_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 1708 static int 1709 add_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 1725 static 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 1743 static 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 } 2035 skip_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 nr_options += add_script_opt(browser, 2063 &actions[nr_options], 2064 &options[nr_options], 2065 NULL, browser->selection->sym); 2066 } 2067 nr_options += add_script_opt(browser, &actions[nr_options], 2068 &options[nr_options], NULL, NULL); 2069 nr_options += add_switch_opt(browser, &actions[nr_options], 2070 &options[nr_options]); 2071 add_exit_option: 2072 nr_options += add_exit_opt(browser, &actions[nr_options], 2073 &options[nr_options]); 2074 2075 do { 2076 struct popup_action *act; 2077 2078 choice = ui__popup_menu(nr_options, options); 2079 if (choice == -1 || choice >= nr_options) 2080 break; 2081 2082 act = &actions[choice]; 2083 key = act->fn(browser, act); 2084 } while (key == 1); 2085 2086 if (key == K_SWITCH_INPUT_DATA) 2087 break; 2088 } 2089 out_free_stack: 2090 pstack__delete(browser->pstack); 2091 out: 2092 hist_browser__delete(browser); 2093 free_popup_options(options, MAX_OPTIONS); 2094 return key; 2095 } 2096 2097 struct perf_evsel_menu { 2098 struct ui_browser b; 2099 struct perf_evsel *selection; 2100 bool lost_events, lost_events_warned; 2101 float min_pcnt; 2102 struct perf_env *env; 2103 }; 2104 2105 static void perf_evsel_menu__write(struct ui_browser *browser, 2106 void *entry, int row) 2107 { 2108 struct perf_evsel_menu *menu = container_of(browser, 2109 struct perf_evsel_menu, b); 2110 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2111 struct hists *hists = evsel__hists(evsel); 2112 bool current_entry = ui_browser__is_current_entry(browser, row); 2113 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2114 const char *ev_name = perf_evsel__name(evsel); 2115 char bf[256], unit; 2116 const char *warn = " "; 2117 size_t printed; 2118 2119 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 2120 HE_COLORSET_NORMAL); 2121 2122 if (perf_evsel__is_group_event(evsel)) { 2123 struct perf_evsel *pos; 2124 2125 ev_name = perf_evsel__group_name(evsel); 2126 2127 for_each_group_member(pos, evsel) { 2128 struct hists *pos_hists = evsel__hists(pos); 2129 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2130 } 2131 } 2132 2133 nr_events = convert_unit(nr_events, &unit); 2134 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 2135 unit, unit == ' ' ? "" : " ", ev_name); 2136 ui_browser__printf(browser, "%s", bf); 2137 2138 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 2139 if (nr_events != 0) { 2140 menu->lost_events = true; 2141 if (!current_entry) 2142 ui_browser__set_color(browser, HE_COLORSET_TOP); 2143 nr_events = convert_unit(nr_events, &unit); 2144 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 2145 nr_events, unit, unit == ' ' ? "" : " "); 2146 warn = bf; 2147 } 2148 2149 ui_browser__write_nstring(browser, warn, browser->width - printed); 2150 2151 if (current_entry) 2152 menu->selection = evsel; 2153 } 2154 2155 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 2156 int nr_events, const char *help, 2157 struct hist_browser_timer *hbt) 2158 { 2159 struct perf_evlist *evlist = menu->b.priv; 2160 struct perf_evsel *pos; 2161 const char *title = "Available samples"; 2162 int delay_secs = hbt ? hbt->refresh : 0; 2163 int key; 2164 2165 if (ui_browser__show(&menu->b, title, 2166 "ESC: exit, ENTER|->: Browse histograms") < 0) 2167 return -1; 2168 2169 while (1) { 2170 key = ui_browser__run(&menu->b, delay_secs); 2171 2172 switch (key) { 2173 case K_TIMER: 2174 hbt->timer(hbt->arg); 2175 2176 if (!menu->lost_events_warned && menu->lost_events) { 2177 ui_browser__warn_lost_events(&menu->b); 2178 menu->lost_events_warned = true; 2179 } 2180 continue; 2181 case K_RIGHT: 2182 case K_ENTER: 2183 if (!menu->selection) 2184 continue; 2185 pos = menu->selection; 2186 browse_hists: 2187 perf_evlist__set_selected(evlist, pos); 2188 /* 2189 * Give the calling tool a chance to populate the non 2190 * default evsel resorted hists tree. 2191 */ 2192 if (hbt) 2193 hbt->timer(hbt->arg); 2194 key = perf_evsel__hists_browse(pos, nr_events, help, 2195 true, hbt, 2196 menu->min_pcnt, 2197 menu->env); 2198 ui_browser__show_title(&menu->b, title); 2199 switch (key) { 2200 case K_TAB: 2201 if (pos->node.next == &evlist->entries) 2202 pos = perf_evlist__first(evlist); 2203 else 2204 pos = perf_evsel__next(pos); 2205 goto browse_hists; 2206 case K_UNTAB: 2207 if (pos->node.prev == &evlist->entries) 2208 pos = perf_evlist__last(evlist); 2209 else 2210 pos = perf_evsel__prev(pos); 2211 goto browse_hists; 2212 case K_SWITCH_INPUT_DATA: 2213 case 'q': 2214 case CTRL('c'): 2215 goto out; 2216 case K_ESC: 2217 default: 2218 continue; 2219 } 2220 case K_LEFT: 2221 continue; 2222 case K_ESC: 2223 if (!ui_browser__dialog_yesno(&menu->b, 2224 "Do you really want to exit?")) 2225 continue; 2226 /* Fall thru */ 2227 case 'q': 2228 case CTRL('c'): 2229 goto out; 2230 default: 2231 continue; 2232 } 2233 } 2234 2235 out: 2236 ui_browser__hide(&menu->b); 2237 return key; 2238 } 2239 2240 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 2241 void *entry) 2242 { 2243 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2244 2245 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 2246 return true; 2247 2248 return false; 2249 } 2250 2251 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 2252 int nr_entries, const char *help, 2253 struct hist_browser_timer *hbt, 2254 float min_pcnt, 2255 struct perf_env *env) 2256 { 2257 struct perf_evsel *pos; 2258 struct perf_evsel_menu menu = { 2259 .b = { 2260 .entries = &evlist->entries, 2261 .refresh = ui_browser__list_head_refresh, 2262 .seek = ui_browser__list_head_seek, 2263 .write = perf_evsel_menu__write, 2264 .filter = filter_group_entries, 2265 .nr_entries = nr_entries, 2266 .priv = evlist, 2267 }, 2268 .min_pcnt = min_pcnt, 2269 .env = env, 2270 }; 2271 2272 ui_helpline__push("Press ESC to exit"); 2273 2274 evlist__for_each(evlist, pos) { 2275 const char *ev_name = perf_evsel__name(pos); 2276 size_t line_len = strlen(ev_name) + 7; 2277 2278 if (menu.b.width < line_len) 2279 menu.b.width = line_len; 2280 } 2281 2282 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 2283 } 2284 2285 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 2286 struct hist_browser_timer *hbt, 2287 float min_pcnt, 2288 struct perf_env *env) 2289 { 2290 int nr_entries = evlist->nr_entries; 2291 2292 single_entry: 2293 if (nr_entries == 1) { 2294 struct perf_evsel *first = perf_evlist__first(evlist); 2295 2296 return perf_evsel__hists_browse(first, nr_entries, help, 2297 false, hbt, min_pcnt, 2298 env); 2299 } 2300 2301 if (symbol_conf.event_group) { 2302 struct perf_evsel *pos; 2303 2304 nr_entries = 0; 2305 evlist__for_each(evlist, pos) { 2306 if (perf_evsel__is_group_leader(pos)) 2307 nr_entries++; 2308 } 2309 2310 if (nr_entries == 1) 2311 goto single_entry; 2312 } 2313 2314 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 2315 hbt, min_pcnt, env); 2316 } 2317