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