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_hierarchy_entries; 36 u64 nr_callchain_rows; 37 }; 38 39 extern void hist_browser__init_hpp(void); 40 41 static int hists__browser_title(struct hists *hists, 42 struct hist_browser_timer *hbt, 43 char *bf, size_t size); 44 static void hist_browser__update_nr_entries(struct hist_browser *hb); 45 46 static struct rb_node *hists__filter_entries(struct rb_node *nd, 47 float min_pcnt); 48 49 static bool hist_browser__has_filter(struct hist_browser *hb) 50 { 51 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter; 52 } 53 54 static int hist_browser__get_folding(struct hist_browser *browser) 55 { 56 struct rb_node *nd; 57 struct hists *hists = browser->hists; 58 int unfolded_rows = 0; 59 60 for (nd = rb_first(&hists->entries); 61 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 62 nd = rb_hierarchy_next(nd)) { 63 struct hist_entry *he = 64 rb_entry(nd, struct hist_entry, rb_node); 65 66 if (he->leaf && he->unfolded) 67 unfolded_rows += he->nr_rows; 68 } 69 return unfolded_rows; 70 } 71 72 static u32 hist_browser__nr_entries(struct hist_browser *hb) 73 { 74 u32 nr_entries; 75 76 if (symbol_conf.report_hierarchy) 77 nr_entries = hb->nr_hierarchy_entries; 78 else if (hist_browser__has_filter(hb)) 79 nr_entries = hb->nr_non_filtered_entries; 80 else 81 nr_entries = hb->hists->nr_entries; 82 83 hb->nr_callchain_rows = hist_browser__get_folding(hb); 84 return nr_entries + hb->nr_callchain_rows; 85 } 86 87 static void hist_browser__update_rows(struct hist_browser *hb) 88 { 89 struct ui_browser *browser = &hb->b; 90 u16 header_offset = hb->show_headers ? 1 : 0, index_row; 91 92 browser->rows = browser->height - header_offset; 93 /* 94 * Verify if we were at the last line and that line isn't 95 * visibe because we now show the header line(s). 96 */ 97 index_row = browser->index - browser->top_idx; 98 if (index_row >= browser->rows) 99 browser->index -= index_row - browser->rows + 1; 100 } 101 102 static void hist_browser__refresh_dimensions(struct ui_browser *browser) 103 { 104 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 105 106 /* 3 == +/- toggle symbol before actual hist_entry rendering */ 107 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]")); 108 /* 109 * FIXME: Just keeping existing behaviour, but this really should be 110 * before updating browser->width, as it will invalidate the 111 * calculation above. Fix this and the fallout in another 112 * changeset. 113 */ 114 ui_browser__refresh_dimensions(browser); 115 hist_browser__update_rows(hb); 116 } 117 118 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column) 119 { 120 u16 header_offset = browser->show_headers ? 1 : 0; 121 122 ui_browser__gotorc(&browser->b, row + header_offset, column); 123 } 124 125 static void hist_browser__reset(struct hist_browser *browser) 126 { 127 /* 128 * The hists__remove_entry_filter() already folds non-filtered 129 * entries so we can assume it has 0 callchain rows. 130 */ 131 browser->nr_callchain_rows = 0; 132 133 hist_browser__update_nr_entries(browser); 134 browser->b.nr_entries = hist_browser__nr_entries(browser); 135 hist_browser__refresh_dimensions(&browser->b); 136 ui_browser__reset_index(&browser->b); 137 } 138 139 static char tree__folded_sign(bool unfolded) 140 { 141 return unfolded ? '-' : '+'; 142 } 143 144 static char hist_entry__folded(const struct hist_entry *he) 145 { 146 return he->has_children ? tree__folded_sign(he->unfolded) : ' '; 147 } 148 149 static char callchain_list__folded(const struct callchain_list *cl) 150 { 151 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; 152 } 153 154 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) 155 { 156 cl->unfolded = unfold ? cl->has_children : false; 157 } 158 159 static int callchain_node__count_rows_rb_tree(struct callchain_node *node) 160 { 161 int n = 0; 162 struct rb_node *nd; 163 164 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 165 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 166 struct callchain_list *chain; 167 char folded_sign = ' '; /* No children */ 168 169 list_for_each_entry(chain, &child->val, list) { 170 ++n; 171 /* We need this because we may not have children */ 172 folded_sign = callchain_list__folded(chain); 173 if (folded_sign == '+') 174 break; 175 } 176 177 if (folded_sign == '-') /* Have children and they're unfolded */ 178 n += callchain_node__count_rows_rb_tree(child); 179 } 180 181 return n; 182 } 183 184 static int callchain_node__count_flat_rows(struct callchain_node *node) 185 { 186 struct callchain_list *chain; 187 char folded_sign = 0; 188 int n = 0; 189 190 list_for_each_entry(chain, &node->parent_val, list) { 191 if (!folded_sign) { 192 /* only check first chain list entry */ 193 folded_sign = callchain_list__folded(chain); 194 if (folded_sign == '+') 195 return 1; 196 } 197 n++; 198 } 199 200 list_for_each_entry(chain, &node->val, list) { 201 if (!folded_sign) { 202 /* node->parent_val list might be empty */ 203 folded_sign = callchain_list__folded(chain); 204 if (folded_sign == '+') 205 return 1; 206 } 207 n++; 208 } 209 210 return n; 211 } 212 213 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused) 214 { 215 return 1; 216 } 217 218 static int callchain_node__count_rows(struct callchain_node *node) 219 { 220 struct callchain_list *chain; 221 bool unfolded = false; 222 int n = 0; 223 224 if (callchain_param.mode == CHAIN_FLAT) 225 return callchain_node__count_flat_rows(node); 226 else if (callchain_param.mode == CHAIN_FOLDED) 227 return callchain_node__count_folded_rows(node); 228 229 list_for_each_entry(chain, &node->val, list) { 230 ++n; 231 unfolded = chain->unfolded; 232 } 233 234 if (unfolded) 235 n += callchain_node__count_rows_rb_tree(node); 236 237 return n; 238 } 239 240 static int callchain__count_rows(struct rb_root *chain) 241 { 242 struct rb_node *nd; 243 int n = 0; 244 245 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 246 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 247 n += callchain_node__count_rows(node); 248 } 249 250 return n; 251 } 252 253 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he, 254 bool include_children) 255 { 256 int count = 0; 257 struct rb_node *node; 258 struct hist_entry *child; 259 260 if (he->leaf) 261 return callchain__count_rows(&he->sorted_chain); 262 263 node = rb_first(&he->hroot_out); 264 while (node) { 265 float percent; 266 267 child = rb_entry(node, struct hist_entry, rb_node); 268 percent = hist_entry__get_percent_limit(child); 269 270 if (!child->filtered && percent >= hb->min_pcnt) { 271 count++; 272 273 if (include_children && child->unfolded) 274 count += hierarchy_count_rows(hb, child, true); 275 } 276 277 node = rb_next(node); 278 } 279 return count; 280 } 281 282 static bool hist_entry__toggle_fold(struct hist_entry *he) 283 { 284 if (!he) 285 return false; 286 287 if (!he->has_children) 288 return false; 289 290 he->unfolded = !he->unfolded; 291 return true; 292 } 293 294 static bool callchain_list__toggle_fold(struct callchain_list *cl) 295 { 296 if (!cl) 297 return false; 298 299 if (!cl->has_children) 300 return false; 301 302 cl->unfolded = !cl->unfolded; 303 return true; 304 } 305 306 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node) 307 { 308 struct rb_node *nd = rb_first(&node->rb_root); 309 310 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 311 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 312 struct callchain_list *chain; 313 bool first = true; 314 315 list_for_each_entry(chain, &child->val, list) { 316 if (first) { 317 first = false; 318 chain->has_children = chain->list.next != &child->val || 319 !RB_EMPTY_ROOT(&child->rb_root); 320 } else 321 chain->has_children = chain->list.next == &child->val && 322 !RB_EMPTY_ROOT(&child->rb_root); 323 } 324 325 callchain_node__init_have_children_rb_tree(child); 326 } 327 } 328 329 static void callchain_node__init_have_children(struct callchain_node *node, 330 bool has_sibling) 331 { 332 struct callchain_list *chain; 333 334 chain = list_entry(node->val.next, struct callchain_list, list); 335 chain->has_children = has_sibling; 336 337 if (node->val.next != node->val.prev) { 338 chain = list_entry(node->val.prev, struct callchain_list, list); 339 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); 340 } 341 342 callchain_node__init_have_children_rb_tree(node); 343 } 344 345 static void callchain__init_have_children(struct rb_root *root) 346 { 347 struct rb_node *nd = rb_first(root); 348 bool has_sibling = nd && rb_next(nd); 349 350 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 351 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 352 callchain_node__init_have_children(node, has_sibling); 353 if (callchain_param.mode == CHAIN_FLAT || 354 callchain_param.mode == CHAIN_FOLDED) 355 callchain_node__make_parent_list(node); 356 } 357 } 358 359 static void hist_entry__init_have_children(struct hist_entry *he) 360 { 361 if (he->init_have_children) 362 return; 363 364 if (he->leaf) { 365 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 366 callchain__init_have_children(&he->sorted_chain); 367 } else { 368 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out); 369 } 370 371 he->init_have_children = true; 372 } 373 374 static bool hist_browser__toggle_fold(struct hist_browser *browser) 375 { 376 struct hist_entry *he = browser->he_selection; 377 struct map_symbol *ms = browser->selection; 378 struct callchain_list *cl = container_of(ms, struct callchain_list, ms); 379 bool has_children; 380 381 if (!he || !ms) 382 return false; 383 384 if (ms == &he->ms) 385 has_children = hist_entry__toggle_fold(he); 386 else 387 has_children = callchain_list__toggle_fold(cl); 388 389 if (has_children) { 390 int child_rows = 0; 391 392 hist_entry__init_have_children(he); 393 browser->b.nr_entries -= he->nr_rows; 394 395 if (he->leaf) 396 browser->nr_callchain_rows -= he->nr_rows; 397 else 398 browser->nr_hierarchy_entries -= he->nr_rows; 399 400 if (symbol_conf.report_hierarchy) 401 child_rows = hierarchy_count_rows(browser, he, true); 402 403 if (he->unfolded) { 404 if (he->leaf) 405 he->nr_rows = callchain__count_rows(&he->sorted_chain); 406 else 407 he->nr_rows = hierarchy_count_rows(browser, he, false); 408 409 /* account grand children */ 410 if (symbol_conf.report_hierarchy) 411 browser->b.nr_entries += child_rows - he->nr_rows; 412 } else { 413 if (symbol_conf.report_hierarchy) 414 browser->b.nr_entries -= child_rows - he->nr_rows; 415 416 he->nr_rows = 0; 417 } 418 419 browser->b.nr_entries += he->nr_rows; 420 421 if (he->leaf) 422 browser->nr_callchain_rows += he->nr_rows; 423 else 424 browser->nr_hierarchy_entries += he->nr_rows; 425 426 return true; 427 } 428 429 /* If it doesn't have children, no toggling performed */ 430 return false; 431 } 432 433 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold) 434 { 435 int n = 0; 436 struct rb_node *nd; 437 438 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 439 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 440 struct callchain_list *chain; 441 bool has_children = false; 442 443 list_for_each_entry(chain, &child->val, list) { 444 ++n; 445 callchain_list__set_folding(chain, unfold); 446 has_children = chain->has_children; 447 } 448 449 if (has_children) 450 n += callchain_node__set_folding_rb_tree(child, unfold); 451 } 452 453 return n; 454 } 455 456 static int callchain_node__set_folding(struct callchain_node *node, bool unfold) 457 { 458 struct callchain_list *chain; 459 bool has_children = false; 460 int n = 0; 461 462 list_for_each_entry(chain, &node->val, list) { 463 ++n; 464 callchain_list__set_folding(chain, unfold); 465 has_children = chain->has_children; 466 } 467 468 if (has_children) 469 n += callchain_node__set_folding_rb_tree(node, unfold); 470 471 return n; 472 } 473 474 static int callchain__set_folding(struct rb_root *chain, bool unfold) 475 { 476 struct rb_node *nd; 477 int n = 0; 478 479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 481 n += callchain_node__set_folding(node, unfold); 482 } 483 484 return n; 485 } 486 487 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, 488 bool unfold __maybe_unused) 489 { 490 float percent; 491 struct rb_node *nd; 492 struct hist_entry *child; 493 int n = 0; 494 495 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) { 496 child = rb_entry(nd, struct hist_entry, rb_node); 497 percent = hist_entry__get_percent_limit(child); 498 if (!child->filtered && percent >= hb->min_pcnt) 499 n++; 500 } 501 502 return n; 503 } 504 505 static void hist_entry__set_folding(struct hist_entry *he, 506 struct hist_browser *hb, bool unfold) 507 { 508 hist_entry__init_have_children(he); 509 he->unfolded = unfold ? he->has_children : false; 510 511 if (he->has_children) { 512 int n; 513 514 if (he->leaf) 515 n = callchain__set_folding(&he->sorted_chain, unfold); 516 else 517 n = hierarchy_set_folding(hb, he, unfold); 518 519 he->nr_rows = unfold ? n : 0; 520 } else 521 he->nr_rows = 0; 522 } 523 524 static void 525 __hist_browser__set_folding(struct hist_browser *browser, bool unfold) 526 { 527 struct rb_node *nd; 528 struct hist_entry *he; 529 double percent; 530 531 nd = rb_first(&browser->hists->entries); 532 while (nd) { 533 he = rb_entry(nd, struct hist_entry, rb_node); 534 535 /* set folding state even if it's currently folded */ 536 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); 537 538 hist_entry__set_folding(he, browser, unfold); 539 540 percent = hist_entry__get_percent_limit(he); 541 if (he->filtered || percent < browser->min_pcnt) 542 continue; 543 544 if (!he->depth || unfold) 545 browser->nr_hierarchy_entries++; 546 if (he->leaf) 547 browser->nr_callchain_rows += he->nr_rows; 548 } 549 } 550 551 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 552 { 553 browser->nr_hierarchy_entries = 0; 554 browser->nr_callchain_rows = 0; 555 __hist_browser__set_folding(browser, unfold); 556 557 browser->b.nr_entries = hist_browser__nr_entries(browser); 558 /* Go to the start, we may be way after valid entries after a collapse */ 559 ui_browser__reset_index(&browser->b); 560 } 561 562 static void ui_browser__warn_lost_events(struct ui_browser *browser) 563 { 564 ui_browser__warning(browser, 4, 565 "Events are being lost, check IO/CPU overload!\n\n" 566 "You may want to run 'perf' using a RT scheduler policy:\n\n" 567 " perf top -r 80\n\n" 568 "Or reduce the sampling frequency."); 569 } 570 571 static int hist_browser__run(struct hist_browser *browser, const char *help) 572 { 573 int key; 574 char title[160]; 575 struct hist_browser_timer *hbt = browser->hbt; 576 int delay_secs = hbt ? hbt->refresh : 0; 577 578 browser->b.entries = &browser->hists->entries; 579 browser->b.nr_entries = hist_browser__nr_entries(browser); 580 581 hists__browser_title(browser->hists, hbt, title, sizeof(title)); 582 583 if (ui_browser__show(&browser->b, title, "%s", help) < 0) 584 return -1; 585 586 while (1) { 587 key = ui_browser__run(&browser->b, delay_secs); 588 589 switch (key) { 590 case K_TIMER: { 591 u64 nr_entries; 592 hbt->timer(hbt->arg); 593 594 if (hist_browser__has_filter(browser)) 595 hist_browser__update_nr_entries(browser); 596 597 nr_entries = hist_browser__nr_entries(browser); 598 ui_browser__update_nr_entries(&browser->b, nr_entries); 599 600 if (browser->hists->stats.nr_lost_warned != 601 browser->hists->stats.nr_events[PERF_RECORD_LOST]) { 602 browser->hists->stats.nr_lost_warned = 603 browser->hists->stats.nr_events[PERF_RECORD_LOST]; 604 ui_browser__warn_lost_events(&browser->b); 605 } 606 607 hists__browser_title(browser->hists, 608 hbt, title, sizeof(title)); 609 ui_browser__show_title(&browser->b, title); 610 continue; 611 } 612 case 'D': { /* Debug */ 613 static int seq; 614 struct hist_entry *h = rb_entry(browser->b.top, 615 struct hist_entry, rb_node); 616 ui_helpline__pop(); 617 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 618 seq++, browser->b.nr_entries, 619 browser->hists->nr_entries, 620 browser->b.rows, 621 browser->b.index, 622 browser->b.top_idx, 623 h->row_offset, h->nr_rows); 624 } 625 break; 626 case 'C': 627 /* Collapse the whole world. */ 628 hist_browser__set_folding(browser, false); 629 break; 630 case 'E': 631 /* Expand the whole world. */ 632 hist_browser__set_folding(browser, true); 633 break; 634 case 'H': 635 browser->show_headers = !browser->show_headers; 636 hist_browser__update_rows(browser); 637 break; 638 case K_ENTER: 639 if (hist_browser__toggle_fold(browser)) 640 break; 641 /* fall thru */ 642 default: 643 goto out; 644 } 645 } 646 out: 647 ui_browser__hide(&browser->b); 648 return key; 649 } 650 651 struct callchain_print_arg { 652 /* for hists browser */ 653 off_t row_offset; 654 bool is_current_entry; 655 656 /* for file dump */ 657 FILE *fp; 658 int printed; 659 }; 660 661 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, 662 struct callchain_list *chain, 663 const char *str, int offset, 664 unsigned short row, 665 struct callchain_print_arg *arg); 666 667 static void hist_browser__show_callchain_entry(struct hist_browser *browser, 668 struct callchain_list *chain, 669 const char *str, int offset, 670 unsigned short row, 671 struct callchain_print_arg *arg) 672 { 673 int color, width; 674 char folded_sign = callchain_list__folded(chain); 675 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src; 676 677 color = HE_COLORSET_NORMAL; 678 width = browser->b.width - (offset + 2); 679 if (ui_browser__is_current_entry(&browser->b, row)) { 680 browser->selection = &chain->ms; 681 color = HE_COLORSET_SELECTED; 682 arg->is_current_entry = true; 683 } 684 685 ui_browser__set_color(&browser->b, color); 686 hist_browser__gotorc(browser, row, 0); 687 ui_browser__write_nstring(&browser->b, " ", offset); 688 ui_browser__printf(&browser->b, "%c", folded_sign); 689 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' '); 690 ui_browser__write_nstring(&browser->b, str, width); 691 } 692 693 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, 694 struct callchain_list *chain, 695 const char *str, int offset, 696 unsigned short row __maybe_unused, 697 struct callchain_print_arg *arg) 698 { 699 char folded_sign = callchain_list__folded(chain); 700 701 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", 702 folded_sign, str); 703 } 704 705 typedef bool (*check_output_full_fn)(struct hist_browser *browser, 706 unsigned short row); 707 708 static bool hist_browser__check_output_full(struct hist_browser *browser, 709 unsigned short row) 710 { 711 return browser->b.rows == row; 712 } 713 714 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, 715 unsigned short row __maybe_unused) 716 { 717 return false; 718 } 719 720 #define LEVEL_OFFSET_STEP 3 721 722 static int hist_browser__show_callchain_list(struct hist_browser *browser, 723 struct callchain_node *node, 724 struct callchain_list *chain, 725 unsigned short row, u64 total, 726 bool need_percent, int offset, 727 print_callchain_entry_fn print, 728 struct callchain_print_arg *arg) 729 { 730 char bf[1024], *alloc_str; 731 const char *str; 732 733 if (arg->row_offset != 0) { 734 arg->row_offset--; 735 return 0; 736 } 737 738 alloc_str = NULL; 739 str = callchain_list__sym_name(chain, bf, sizeof(bf), 740 browser->show_dso); 741 742 if (need_percent) { 743 char buf[64]; 744 745 callchain_node__scnprintf_value(node, buf, sizeof(buf), 746 total); 747 748 if (asprintf(&alloc_str, "%s %s", buf, str) < 0) 749 str = "Not enough memory!"; 750 else 751 str = alloc_str; 752 } 753 754 print(browser, chain, str, offset, row, arg); 755 756 free(alloc_str); 757 return 1; 758 } 759 760 static bool check_percent_display(struct rb_node *node, u64 parent_total) 761 { 762 struct callchain_node *child; 763 764 if (node == NULL) 765 return false; 766 767 if (rb_next(node)) 768 return true; 769 770 child = rb_entry(node, struct callchain_node, rb_node); 771 return callchain_cumul_hits(child) != parent_total; 772 } 773 774 static int hist_browser__show_callchain_flat(struct hist_browser *browser, 775 struct rb_root *root, 776 unsigned short row, u64 total, 777 u64 parent_total, 778 print_callchain_entry_fn print, 779 struct callchain_print_arg *arg, 780 check_output_full_fn is_output_full) 781 { 782 struct rb_node *node; 783 int first_row = row, offset = LEVEL_OFFSET_STEP; 784 bool need_percent; 785 786 node = rb_first(root); 787 need_percent = check_percent_display(node, parent_total); 788 789 while (node) { 790 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 791 struct rb_node *next = rb_next(node); 792 struct callchain_list *chain; 793 char folded_sign = ' '; 794 int first = true; 795 int extra_offset = 0; 796 797 list_for_each_entry(chain, &child->parent_val, list) { 798 bool was_first = first; 799 800 if (first) 801 first = false; 802 else if (need_percent) 803 extra_offset = LEVEL_OFFSET_STEP; 804 805 folded_sign = callchain_list__folded(chain); 806 807 row += hist_browser__show_callchain_list(browser, child, 808 chain, row, total, 809 was_first && need_percent, 810 offset + extra_offset, 811 print, arg); 812 813 if (is_output_full(browser, row)) 814 goto out; 815 816 if (folded_sign == '+') 817 goto next; 818 } 819 820 list_for_each_entry(chain, &child->val, list) { 821 bool was_first = first; 822 823 if (first) 824 first = false; 825 else if (need_percent) 826 extra_offset = LEVEL_OFFSET_STEP; 827 828 folded_sign = callchain_list__folded(chain); 829 830 row += hist_browser__show_callchain_list(browser, child, 831 chain, row, total, 832 was_first && need_percent, 833 offset + extra_offset, 834 print, arg); 835 836 if (is_output_full(browser, row)) 837 goto out; 838 839 if (folded_sign == '+') 840 break; 841 } 842 843 next: 844 if (is_output_full(browser, row)) 845 break; 846 node = next; 847 } 848 out: 849 return row - first_row; 850 } 851 852 static char *hist_browser__folded_callchain_str(struct hist_browser *browser, 853 struct callchain_list *chain, 854 char *value_str, char *old_str) 855 { 856 char bf[1024]; 857 const char *str; 858 char *new; 859 860 str = callchain_list__sym_name(chain, bf, sizeof(bf), 861 browser->show_dso); 862 if (old_str) { 863 if (asprintf(&new, "%s%s%s", old_str, 864 symbol_conf.field_sep ?: ";", str) < 0) 865 new = NULL; 866 } else { 867 if (value_str) { 868 if (asprintf(&new, "%s %s", value_str, str) < 0) 869 new = NULL; 870 } else { 871 if (asprintf(&new, "%s", str) < 0) 872 new = NULL; 873 } 874 } 875 return new; 876 } 877 878 static int hist_browser__show_callchain_folded(struct hist_browser *browser, 879 struct rb_root *root, 880 unsigned short row, u64 total, 881 u64 parent_total, 882 print_callchain_entry_fn print, 883 struct callchain_print_arg *arg, 884 check_output_full_fn is_output_full) 885 { 886 struct rb_node *node; 887 int first_row = row, offset = LEVEL_OFFSET_STEP; 888 bool need_percent; 889 890 node = rb_first(root); 891 need_percent = check_percent_display(node, parent_total); 892 893 while (node) { 894 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 895 struct rb_node *next = rb_next(node); 896 struct callchain_list *chain, *first_chain = NULL; 897 int first = true; 898 char *value_str = NULL, *value_str_alloc = NULL; 899 char *chain_str = NULL, *chain_str_alloc = NULL; 900 901 if (arg->row_offset != 0) { 902 arg->row_offset--; 903 goto next; 904 } 905 906 if (need_percent) { 907 char buf[64]; 908 909 callchain_node__scnprintf_value(child, buf, sizeof(buf), total); 910 if (asprintf(&value_str, "%s", buf) < 0) { 911 value_str = (char *)"<...>"; 912 goto do_print; 913 } 914 value_str_alloc = value_str; 915 } 916 917 list_for_each_entry(chain, &child->parent_val, list) { 918 chain_str = hist_browser__folded_callchain_str(browser, 919 chain, value_str, chain_str); 920 if (first) { 921 first = false; 922 first_chain = chain; 923 } 924 925 if (chain_str == NULL) { 926 chain_str = (char *)"Not enough memory!"; 927 goto do_print; 928 } 929 930 chain_str_alloc = chain_str; 931 } 932 933 list_for_each_entry(chain, &child->val, list) { 934 chain_str = hist_browser__folded_callchain_str(browser, 935 chain, value_str, chain_str); 936 if (first) { 937 first = false; 938 first_chain = chain; 939 } 940 941 if (chain_str == NULL) { 942 chain_str = (char *)"Not enough memory!"; 943 goto do_print; 944 } 945 946 chain_str_alloc = chain_str; 947 } 948 949 do_print: 950 print(browser, first_chain, chain_str, offset, row++, arg); 951 free(value_str_alloc); 952 free(chain_str_alloc); 953 954 next: 955 if (is_output_full(browser, row)) 956 break; 957 node = next; 958 } 959 960 return row - first_row; 961 } 962 963 static int hist_browser__show_callchain_graph(struct hist_browser *browser, 964 struct rb_root *root, int level, 965 unsigned short row, u64 total, 966 u64 parent_total, 967 print_callchain_entry_fn print, 968 struct callchain_print_arg *arg, 969 check_output_full_fn is_output_full) 970 { 971 struct rb_node *node; 972 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 973 bool need_percent; 974 u64 percent_total = total; 975 976 if (callchain_param.mode == CHAIN_GRAPH_REL) 977 percent_total = parent_total; 978 979 node = rb_first(root); 980 need_percent = check_percent_display(node, parent_total); 981 982 while (node) { 983 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 984 struct rb_node *next = rb_next(node); 985 struct callchain_list *chain; 986 char folded_sign = ' '; 987 int first = true; 988 int extra_offset = 0; 989 990 list_for_each_entry(chain, &child->val, list) { 991 bool was_first = first; 992 993 if (first) 994 first = false; 995 else if (need_percent) 996 extra_offset = LEVEL_OFFSET_STEP; 997 998 folded_sign = callchain_list__folded(chain); 999 1000 row += hist_browser__show_callchain_list(browser, child, 1001 chain, row, percent_total, 1002 was_first && need_percent, 1003 offset + extra_offset, 1004 print, arg); 1005 1006 if (is_output_full(browser, row)) 1007 goto out; 1008 1009 if (folded_sign == '+') 1010 break; 1011 } 1012 1013 if (folded_sign == '-') { 1014 const int new_level = level + (extra_offset ? 2 : 1); 1015 1016 row += hist_browser__show_callchain_graph(browser, &child->rb_root, 1017 new_level, row, total, 1018 child->children_hit, 1019 print, arg, is_output_full); 1020 } 1021 if (is_output_full(browser, row)) 1022 break; 1023 node = next; 1024 } 1025 out: 1026 return row - first_row; 1027 } 1028 1029 static int hist_browser__show_callchain(struct hist_browser *browser, 1030 struct hist_entry *entry, int level, 1031 unsigned short row, 1032 print_callchain_entry_fn print, 1033 struct callchain_print_arg *arg, 1034 check_output_full_fn is_output_full) 1035 { 1036 u64 total = hists__total_period(entry->hists); 1037 u64 parent_total; 1038 int printed; 1039 1040 if (symbol_conf.cumulate_callchain) 1041 parent_total = entry->stat_acc->period; 1042 else 1043 parent_total = entry->stat.period; 1044 1045 if (callchain_param.mode == CHAIN_FLAT) { 1046 printed = hist_browser__show_callchain_flat(browser, 1047 &entry->sorted_chain, row, 1048 total, parent_total, print, arg, 1049 is_output_full); 1050 } else if (callchain_param.mode == CHAIN_FOLDED) { 1051 printed = hist_browser__show_callchain_folded(browser, 1052 &entry->sorted_chain, row, 1053 total, parent_total, print, arg, 1054 is_output_full); 1055 } else { 1056 printed = hist_browser__show_callchain_graph(browser, 1057 &entry->sorted_chain, level, row, 1058 total, parent_total, print, arg, 1059 is_output_full); 1060 } 1061 1062 if (arg->is_current_entry) 1063 browser->he_selection = entry; 1064 1065 return printed; 1066 } 1067 1068 struct hpp_arg { 1069 struct ui_browser *b; 1070 char folded_sign; 1071 bool current_entry; 1072 }; 1073 1074 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 1075 { 1076 struct hpp_arg *arg = hpp->ptr; 1077 int ret, len; 1078 va_list args; 1079 double percent; 1080 1081 va_start(args, fmt); 1082 len = va_arg(args, int); 1083 percent = va_arg(args, double); 1084 va_end(args); 1085 1086 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 1087 1088 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 1089 ui_browser__printf(arg->b, "%s", hpp->buf); 1090 1091 advance_hpp(hpp, ret); 1092 return ret; 1093 } 1094 1095 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 1096 static u64 __hpp_get_##_field(struct hist_entry *he) \ 1097 { \ 1098 return he->stat._field; \ 1099 } \ 1100 \ 1101 static int \ 1102 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 1103 struct perf_hpp *hpp, \ 1104 struct hist_entry *he) \ 1105 { \ 1106 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 1107 __hpp__slsmg_color_printf, true); \ 1108 } 1109 1110 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 1111 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 1112 { \ 1113 return he->stat_acc->_field; \ 1114 } \ 1115 \ 1116 static int \ 1117 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 1118 struct perf_hpp *hpp, \ 1119 struct hist_entry *he) \ 1120 { \ 1121 if (!symbol_conf.cumulate_callchain) { \ 1122 struct hpp_arg *arg = hpp->ptr; \ 1123 int len = fmt->user_len ?: fmt->len; \ 1124 int ret = scnprintf(hpp->buf, hpp->size, \ 1125 "%*s", len, "N/A"); \ 1126 ui_browser__printf(arg->b, "%s", hpp->buf); \ 1127 \ 1128 return ret; \ 1129 } \ 1130 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ 1131 " %*.2f%%", __hpp__slsmg_color_printf, true); \ 1132 } 1133 1134 __HPP_COLOR_PERCENT_FN(overhead, period) 1135 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 1136 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 1137 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 1138 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 1139 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 1140 1141 #undef __HPP_COLOR_PERCENT_FN 1142 #undef __HPP_COLOR_ACC_PERCENT_FN 1143 1144 void hist_browser__init_hpp(void) 1145 { 1146 perf_hpp__format[PERF_HPP__OVERHEAD].color = 1147 hist_browser__hpp_color_overhead; 1148 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 1149 hist_browser__hpp_color_overhead_sys; 1150 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 1151 hist_browser__hpp_color_overhead_us; 1152 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 1153 hist_browser__hpp_color_overhead_guest_sys; 1154 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 1155 hist_browser__hpp_color_overhead_guest_us; 1156 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 1157 hist_browser__hpp_color_overhead_acc; 1158 } 1159 1160 static int hist_browser__show_entry(struct hist_browser *browser, 1161 struct hist_entry *entry, 1162 unsigned short row) 1163 { 1164 int printed = 0; 1165 int width = browser->b.width; 1166 char folded_sign = ' '; 1167 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1168 off_t row_offset = entry->row_offset; 1169 bool first = true; 1170 struct perf_hpp_fmt *fmt; 1171 1172 if (current_entry) { 1173 browser->he_selection = entry; 1174 browser->selection = &entry->ms; 1175 } 1176 1177 if (symbol_conf.use_callchain) { 1178 hist_entry__init_have_children(entry); 1179 folded_sign = hist_entry__folded(entry); 1180 } 1181 1182 if (row_offset == 0) { 1183 struct hpp_arg arg = { 1184 .b = &browser->b, 1185 .folded_sign = folded_sign, 1186 .current_entry = current_entry, 1187 }; 1188 int column = 0; 1189 1190 hist_browser__gotorc(browser, row, 0); 1191 1192 hists__for_each_format(browser->hists, fmt) { 1193 char s[2048]; 1194 struct perf_hpp hpp = { 1195 .buf = s, 1196 .size = sizeof(s), 1197 .ptr = &arg, 1198 }; 1199 1200 if (perf_hpp__should_skip(fmt, entry->hists) || 1201 column++ < browser->b.horiz_scroll) 1202 continue; 1203 1204 if (current_entry && browser->b.navkeypressed) { 1205 ui_browser__set_color(&browser->b, 1206 HE_COLORSET_SELECTED); 1207 } else { 1208 ui_browser__set_color(&browser->b, 1209 HE_COLORSET_NORMAL); 1210 } 1211 1212 if (first) { 1213 if (symbol_conf.use_callchain) { 1214 ui_browser__printf(&browser->b, "%c ", folded_sign); 1215 width -= 2; 1216 } 1217 first = false; 1218 } else { 1219 ui_browser__printf(&browser->b, " "); 1220 width -= 2; 1221 } 1222 1223 if (fmt->color) { 1224 int ret = fmt->color(fmt, &hpp, entry); 1225 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1226 /* 1227 * fmt->color() already used ui_browser to 1228 * print the non alignment bits, skip it (+ret): 1229 */ 1230 ui_browser__printf(&browser->b, "%s", s + ret); 1231 } else { 1232 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry)); 1233 ui_browser__printf(&browser->b, "%s", s); 1234 } 1235 width -= hpp.buf - s; 1236 } 1237 1238 /* The scroll bar isn't being used */ 1239 if (!browser->b.navkeypressed) 1240 width += 1; 1241 1242 ui_browser__write_nstring(&browser->b, "", width); 1243 1244 ++row; 1245 ++printed; 1246 } else 1247 --row_offset; 1248 1249 if (folded_sign == '-' && row != browser->b.rows) { 1250 struct callchain_print_arg arg = { 1251 .row_offset = row_offset, 1252 .is_current_entry = current_entry, 1253 }; 1254 1255 printed += hist_browser__show_callchain(browser, entry, 1, row, 1256 hist_browser__show_callchain_entry, &arg, 1257 hist_browser__check_output_full); 1258 } 1259 1260 return printed; 1261 } 1262 1263 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, 1264 struct hist_entry *entry, 1265 unsigned short row, 1266 int level, int nr_sort_keys) 1267 { 1268 int printed = 0; 1269 int width = browser->b.width; 1270 char folded_sign = ' '; 1271 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1272 off_t row_offset = entry->row_offset; 1273 bool first = true; 1274 struct perf_hpp_fmt *fmt; 1275 struct hpp_arg arg = { 1276 .b = &browser->b, 1277 .current_entry = current_entry, 1278 }; 1279 int column = 0; 1280 int hierarchy_indent = (nr_sort_keys - 1) * HIERARCHY_INDENT; 1281 1282 if (current_entry) { 1283 browser->he_selection = entry; 1284 browser->selection = &entry->ms; 1285 } 1286 1287 hist_entry__init_have_children(entry); 1288 folded_sign = hist_entry__folded(entry); 1289 arg.folded_sign = folded_sign; 1290 1291 if (entry->leaf && row_offset) { 1292 row_offset--; 1293 goto show_callchain; 1294 } 1295 1296 hist_browser__gotorc(browser, row, 0); 1297 1298 if (current_entry && browser->b.navkeypressed) 1299 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); 1300 else 1301 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); 1302 1303 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); 1304 width -= level * HIERARCHY_INDENT; 1305 1306 hists__for_each_format(entry->hists, fmt) { 1307 char s[2048]; 1308 struct perf_hpp hpp = { 1309 .buf = s, 1310 .size = sizeof(s), 1311 .ptr = &arg, 1312 }; 1313 1314 if (perf_hpp__should_skip(fmt, entry->hists) || 1315 column++ < browser->b.horiz_scroll) 1316 continue; 1317 1318 if (perf_hpp__is_sort_entry(fmt) || 1319 perf_hpp__is_dynamic_entry(fmt)) 1320 break; 1321 1322 if (current_entry && browser->b.navkeypressed) { 1323 ui_browser__set_color(&browser->b, 1324 HE_COLORSET_SELECTED); 1325 } else { 1326 ui_browser__set_color(&browser->b, 1327 HE_COLORSET_NORMAL); 1328 } 1329 1330 if (first) { 1331 ui_browser__printf(&browser->b, "%c", folded_sign); 1332 width--; 1333 first = false; 1334 } else { 1335 ui_browser__printf(&browser->b, " "); 1336 width -= 2; 1337 } 1338 1339 if (fmt->color) { 1340 int ret = fmt->color(fmt, &hpp, entry); 1341 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1342 /* 1343 * fmt->color() already used ui_browser to 1344 * print the non alignment bits, skip it (+ret): 1345 */ 1346 ui_browser__printf(&browser->b, "%s", s + ret); 1347 } else { 1348 int ret = fmt->entry(fmt, &hpp, entry); 1349 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1350 ui_browser__printf(&browser->b, "%s", s); 1351 } 1352 width -= hpp.buf - s; 1353 } 1354 1355 ui_browser__write_nstring(&browser->b, "", hierarchy_indent); 1356 width -= hierarchy_indent; 1357 1358 if (column >= browser->b.horiz_scroll) { 1359 char s[2048]; 1360 struct perf_hpp hpp = { 1361 .buf = s, 1362 .size = sizeof(s), 1363 .ptr = &arg, 1364 }; 1365 1366 if (current_entry && browser->b.navkeypressed) { 1367 ui_browser__set_color(&browser->b, 1368 HE_COLORSET_SELECTED); 1369 } else { 1370 ui_browser__set_color(&browser->b, 1371 HE_COLORSET_NORMAL); 1372 } 1373 1374 ui_browser__write_nstring(&browser->b, "", 2); 1375 width -= 2; 1376 1377 /* 1378 * No need to call hist_entry__snprintf_alignment() 1379 * since this fmt is always the last column in the 1380 * hierarchy mode. 1381 */ 1382 fmt = entry->fmt; 1383 if (fmt->color) { 1384 width -= fmt->color(fmt, &hpp, entry); 1385 } else { 1386 width -= fmt->entry(fmt, &hpp, entry); 1387 ui_browser__printf(&browser->b, "%s", s); 1388 } 1389 } 1390 1391 /* The scroll bar isn't being used */ 1392 if (!browser->b.navkeypressed) 1393 width += 1; 1394 1395 ui_browser__write_nstring(&browser->b, "", width); 1396 1397 ++row; 1398 ++printed; 1399 1400 show_callchain: 1401 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) { 1402 struct callchain_print_arg carg = { 1403 .row_offset = row_offset, 1404 }; 1405 1406 printed += hist_browser__show_callchain(browser, entry, 1407 level + 1, row, 1408 hist_browser__show_callchain_entry, &carg, 1409 hist_browser__check_output_full); 1410 } 1411 1412 return printed; 1413 } 1414 1415 static int advance_hpp_check(struct perf_hpp *hpp, int inc) 1416 { 1417 advance_hpp(hpp, inc); 1418 return hpp->size <= 0; 1419 } 1420 1421 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size) 1422 { 1423 struct hists *hists = browser->hists; 1424 struct perf_hpp dummy_hpp = { 1425 .buf = buf, 1426 .size = size, 1427 }; 1428 struct perf_hpp_fmt *fmt; 1429 size_t ret = 0; 1430 int column = 0; 1431 1432 if (symbol_conf.use_callchain) { 1433 ret = scnprintf(buf, size, " "); 1434 if (advance_hpp_check(&dummy_hpp, ret)) 1435 return ret; 1436 } 1437 1438 hists__for_each_format(browser->hists, fmt) { 1439 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) 1440 continue; 1441 1442 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); 1443 if (advance_hpp_check(&dummy_hpp, ret)) 1444 break; 1445 1446 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 1447 if (advance_hpp_check(&dummy_hpp, ret)) 1448 break; 1449 } 1450 1451 return ret; 1452 } 1453 1454 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) 1455 { 1456 struct hists *hists = browser->hists; 1457 struct perf_hpp dummy_hpp = { 1458 .buf = buf, 1459 .size = size, 1460 }; 1461 struct perf_hpp_fmt *fmt; 1462 size_t ret = 0; 1463 int column = 0; 1464 int nr_sort_keys = hists->hpp_list->nr_sort_keys; 1465 bool first = true; 1466 1467 ret = scnprintf(buf, size, " "); 1468 if (advance_hpp_check(&dummy_hpp, ret)) 1469 return ret; 1470 1471 hists__for_each_format(hists, fmt) { 1472 if (column++ < browser->b.horiz_scroll) 1473 continue; 1474 1475 if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) 1476 break; 1477 1478 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); 1479 if (advance_hpp_check(&dummy_hpp, ret)) 1480 break; 1481 1482 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 1483 if (advance_hpp_check(&dummy_hpp, ret)) 1484 break; 1485 } 1486 1487 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", 1488 (nr_sort_keys - 1) * HIERARCHY_INDENT, ""); 1489 if (advance_hpp_check(&dummy_hpp, ret)) 1490 return ret; 1491 1492 hists__for_each_format(hists, fmt) { 1493 if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt)) 1494 continue; 1495 if (perf_hpp__should_skip(fmt, hists)) 1496 continue; 1497 1498 if (first) { 1499 first = false; 1500 } else { 1501 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / "); 1502 if (advance_hpp_check(&dummy_hpp, ret)) 1503 break; 1504 } 1505 1506 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); 1507 dummy_hpp.buf[ret] = '\0'; 1508 rtrim(dummy_hpp.buf); 1509 1510 ret = strlen(dummy_hpp.buf); 1511 if (advance_hpp_check(&dummy_hpp, ret)) 1512 break; 1513 } 1514 1515 return ret; 1516 } 1517 1518 static void hist_browser__show_headers(struct hist_browser *browser) 1519 { 1520 char headers[1024]; 1521 1522 if (symbol_conf.report_hierarchy) 1523 hists_browser__scnprintf_hierarchy_headers(browser, headers, 1524 sizeof(headers)); 1525 else 1526 hists_browser__scnprintf_headers(browser, headers, 1527 sizeof(headers)); 1528 ui_browser__gotorc(&browser->b, 0, 0); 1529 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1530 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1531 } 1532 1533 static void ui_browser__hists_init_top(struct ui_browser *browser) 1534 { 1535 if (browser->top == NULL) { 1536 struct hist_browser *hb; 1537 1538 hb = container_of(browser, struct hist_browser, b); 1539 browser->top = rb_first(&hb->hists->entries); 1540 } 1541 } 1542 1543 static unsigned int hist_browser__refresh(struct ui_browser *browser) 1544 { 1545 unsigned row = 0; 1546 u16 header_offset = 0; 1547 struct rb_node *nd; 1548 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 1549 int nr_sort = hb->hists->hpp_list->nr_sort_keys; 1550 1551 if (hb->show_headers) { 1552 hist_browser__show_headers(hb); 1553 header_offset = 1; 1554 } 1555 1556 ui_browser__hists_init_top(browser); 1557 hb->he_selection = NULL; 1558 hb->selection = NULL; 1559 1560 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) { 1561 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1562 float percent; 1563 1564 if (h->filtered) { 1565 /* let it move to sibling */ 1566 h->unfolded = false; 1567 continue; 1568 } 1569 1570 percent = hist_entry__get_percent_limit(h); 1571 if (percent < hb->min_pcnt) 1572 continue; 1573 1574 if (symbol_conf.report_hierarchy) { 1575 row += hist_browser__show_hierarchy_entry(hb, h, row, 1576 h->depth, 1577 nr_sort); 1578 } else { 1579 row += hist_browser__show_entry(hb, h, row); 1580 } 1581 1582 if (row == browser->rows) 1583 break; 1584 } 1585 1586 return row + header_offset; 1587 } 1588 1589 static struct rb_node *hists__filter_entries(struct rb_node *nd, 1590 float min_pcnt) 1591 { 1592 while (nd != NULL) { 1593 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1594 float percent = hist_entry__get_percent_limit(h); 1595 1596 if (!h->filtered && percent >= min_pcnt) 1597 return nd; 1598 1599 /* 1600 * If it's filtered, its all children also were filtered. 1601 * So move to sibling node. 1602 */ 1603 if (rb_next(nd)) 1604 nd = rb_next(nd); 1605 else 1606 nd = rb_hierarchy_next(nd); 1607 } 1608 1609 return NULL; 1610 } 1611 1612 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 1613 float min_pcnt) 1614 { 1615 while (nd != NULL) { 1616 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1617 float percent = hist_entry__get_percent_limit(h); 1618 1619 if (!h->filtered && percent >= min_pcnt) 1620 return nd; 1621 1622 nd = rb_hierarchy_prev(nd); 1623 } 1624 1625 return NULL; 1626 } 1627 1628 static void ui_browser__hists_seek(struct ui_browser *browser, 1629 off_t offset, int whence) 1630 { 1631 struct hist_entry *h; 1632 struct rb_node *nd; 1633 bool first = true; 1634 struct hist_browser *hb; 1635 1636 hb = container_of(browser, struct hist_browser, b); 1637 1638 if (browser->nr_entries == 0) 1639 return; 1640 1641 ui_browser__hists_init_top(browser); 1642 1643 switch (whence) { 1644 case SEEK_SET: 1645 nd = hists__filter_entries(rb_first(browser->entries), 1646 hb->min_pcnt); 1647 break; 1648 case SEEK_CUR: 1649 nd = browser->top; 1650 goto do_offset; 1651 case SEEK_END: 1652 nd = rb_hierarchy_last(rb_last(browser->entries)); 1653 nd = hists__filter_prev_entries(nd, hb->min_pcnt); 1654 first = false; 1655 break; 1656 default: 1657 return; 1658 } 1659 1660 /* 1661 * Moves not relative to the first visible entry invalidates its 1662 * row_offset: 1663 */ 1664 h = rb_entry(browser->top, struct hist_entry, rb_node); 1665 h->row_offset = 0; 1666 1667 /* 1668 * Here we have to check if nd is expanded (+), if it is we can't go 1669 * the next top level hist_entry, instead we must compute an offset of 1670 * what _not_ to show and not change the first visible entry. 1671 * 1672 * This offset increments when we are going from top to bottom and 1673 * decreases when we're going from bottom to top. 1674 * 1675 * As we don't have backpointers to the top level in the callchains 1676 * structure, we need to always print the whole hist_entry callchain, 1677 * skipping the first ones that are before the first visible entry 1678 * and stop when we printed enough lines to fill the screen. 1679 */ 1680 do_offset: 1681 if (!nd) 1682 return; 1683 1684 if (offset > 0) { 1685 do { 1686 h = rb_entry(nd, struct hist_entry, rb_node); 1687 if (h->unfolded && h->leaf) { 1688 u16 remaining = h->nr_rows - h->row_offset; 1689 if (offset > remaining) { 1690 offset -= remaining; 1691 h->row_offset = 0; 1692 } else { 1693 h->row_offset += offset; 1694 offset = 0; 1695 browser->top = nd; 1696 break; 1697 } 1698 } 1699 nd = hists__filter_entries(rb_hierarchy_next(nd), 1700 hb->min_pcnt); 1701 if (nd == NULL) 1702 break; 1703 --offset; 1704 browser->top = nd; 1705 } while (offset != 0); 1706 } else if (offset < 0) { 1707 while (1) { 1708 h = rb_entry(nd, struct hist_entry, rb_node); 1709 if (h->unfolded && h->leaf) { 1710 if (first) { 1711 if (-offset > h->row_offset) { 1712 offset += h->row_offset; 1713 h->row_offset = 0; 1714 } else { 1715 h->row_offset += offset; 1716 offset = 0; 1717 browser->top = nd; 1718 break; 1719 } 1720 } else { 1721 if (-offset > h->nr_rows) { 1722 offset += h->nr_rows; 1723 h->row_offset = 0; 1724 } else { 1725 h->row_offset = h->nr_rows + offset; 1726 offset = 0; 1727 browser->top = nd; 1728 break; 1729 } 1730 } 1731 } 1732 1733 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd), 1734 hb->min_pcnt); 1735 if (nd == NULL) 1736 break; 1737 ++offset; 1738 browser->top = nd; 1739 if (offset == 0) { 1740 /* 1741 * Last unfiltered hist_entry, check if it is 1742 * unfolded, if it is then we should have 1743 * row_offset at its last entry. 1744 */ 1745 h = rb_entry(nd, struct hist_entry, rb_node); 1746 if (h->unfolded && h->leaf) 1747 h->row_offset = h->nr_rows; 1748 break; 1749 } 1750 first = false; 1751 } 1752 } else { 1753 browser->top = nd; 1754 h = rb_entry(nd, struct hist_entry, rb_node); 1755 h->row_offset = 0; 1756 } 1757 } 1758 1759 static int hist_browser__fprintf_callchain(struct hist_browser *browser, 1760 struct hist_entry *he, FILE *fp, 1761 int level) 1762 { 1763 struct callchain_print_arg arg = { 1764 .fp = fp, 1765 }; 1766 1767 hist_browser__show_callchain(browser, he, level, 0, 1768 hist_browser__fprintf_callchain_entry, &arg, 1769 hist_browser__check_dump_full); 1770 return arg.printed; 1771 } 1772 1773 static int hist_browser__fprintf_entry(struct hist_browser *browser, 1774 struct hist_entry *he, FILE *fp) 1775 { 1776 char s[8192]; 1777 int printed = 0; 1778 char folded_sign = ' '; 1779 struct perf_hpp hpp = { 1780 .buf = s, 1781 .size = sizeof(s), 1782 }; 1783 struct perf_hpp_fmt *fmt; 1784 bool first = true; 1785 int ret; 1786 1787 if (symbol_conf.use_callchain) 1788 folded_sign = hist_entry__folded(he); 1789 1790 if (symbol_conf.use_callchain) 1791 printed += fprintf(fp, "%c ", folded_sign); 1792 1793 hists__for_each_format(browser->hists, fmt) { 1794 if (perf_hpp__should_skip(fmt, he->hists)) 1795 continue; 1796 1797 if (!first) { 1798 ret = scnprintf(hpp.buf, hpp.size, " "); 1799 advance_hpp(&hpp, ret); 1800 } else 1801 first = false; 1802 1803 ret = fmt->entry(fmt, &hpp, he); 1804 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret); 1805 advance_hpp(&hpp, ret); 1806 } 1807 printed += fprintf(fp, "%s\n", s); 1808 1809 if (folded_sign == '-') 1810 printed += hist_browser__fprintf_callchain(browser, he, fp, 1); 1811 1812 return printed; 1813 } 1814 1815 1816 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, 1817 struct hist_entry *he, 1818 FILE *fp, int level, 1819 int nr_sort_keys) 1820 { 1821 char s[8192]; 1822 int printed = 0; 1823 char folded_sign = ' '; 1824 struct perf_hpp hpp = { 1825 .buf = s, 1826 .size = sizeof(s), 1827 }; 1828 struct perf_hpp_fmt *fmt; 1829 bool first = true; 1830 int ret; 1831 int hierarchy_indent = (nr_sort_keys + 1) * HIERARCHY_INDENT; 1832 1833 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, ""); 1834 1835 folded_sign = hist_entry__folded(he); 1836 printed += fprintf(fp, "%c", folded_sign); 1837 1838 hists__for_each_format(he->hists, fmt) { 1839 if (perf_hpp__should_skip(fmt, he->hists)) 1840 continue; 1841 1842 if (perf_hpp__is_sort_entry(fmt) || 1843 perf_hpp__is_dynamic_entry(fmt)) 1844 break; 1845 1846 if (!first) { 1847 ret = scnprintf(hpp.buf, hpp.size, " "); 1848 advance_hpp(&hpp, ret); 1849 } else 1850 first = false; 1851 1852 ret = fmt->entry(fmt, &hpp, he); 1853 advance_hpp(&hpp, ret); 1854 } 1855 1856 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, ""); 1857 advance_hpp(&hpp, ret); 1858 1859 fmt = he->fmt; 1860 ret = fmt->entry(fmt, &hpp, he); 1861 advance_hpp(&hpp, ret); 1862 1863 printed += fprintf(fp, "%s\n", rtrim(s)); 1864 1865 if (he->leaf && folded_sign == '-') { 1866 printed += hist_browser__fprintf_callchain(browser, he, fp, 1867 he->depth + 1); 1868 } 1869 1870 return printed; 1871 } 1872 1873 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 1874 { 1875 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 1876 browser->min_pcnt); 1877 int printed = 0; 1878 int nr_sort = browser->hists->hpp_list->nr_sort_keys; 1879 1880 while (nd) { 1881 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1882 1883 if (symbol_conf.report_hierarchy) { 1884 printed += hist_browser__fprintf_hierarchy_entry(browser, 1885 h, fp, 1886 h->depth, 1887 nr_sort); 1888 } else { 1889 printed += hist_browser__fprintf_entry(browser, h, fp); 1890 } 1891 1892 nd = hists__filter_entries(rb_hierarchy_next(nd), 1893 browser->min_pcnt); 1894 } 1895 1896 return printed; 1897 } 1898 1899 static int hist_browser__dump(struct hist_browser *browser) 1900 { 1901 char filename[64]; 1902 FILE *fp; 1903 1904 while (1) { 1905 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 1906 if (access(filename, F_OK)) 1907 break; 1908 /* 1909 * XXX: Just an arbitrary lazy upper limit 1910 */ 1911 if (++browser->print_seq == 8192) { 1912 ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 1913 return -1; 1914 } 1915 } 1916 1917 fp = fopen(filename, "w"); 1918 if (fp == NULL) { 1919 char bf[64]; 1920 const char *err = strerror_r(errno, bf, sizeof(bf)); 1921 ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 1922 return -1; 1923 } 1924 1925 ++browser->print_seq; 1926 hist_browser__fprintf(browser, fp); 1927 fclose(fp); 1928 ui_helpline__fpush("%s written!", filename); 1929 1930 return 0; 1931 } 1932 1933 static struct hist_browser *hist_browser__new(struct hists *hists, 1934 struct hist_browser_timer *hbt, 1935 struct perf_env *env) 1936 { 1937 struct hist_browser *browser = zalloc(sizeof(*browser)); 1938 1939 if (browser) { 1940 browser->hists = hists; 1941 browser->b.refresh = hist_browser__refresh; 1942 browser->b.refresh_dimensions = hist_browser__refresh_dimensions; 1943 browser->b.seek = ui_browser__hists_seek; 1944 browser->b.use_navkeypressed = true; 1945 browser->show_headers = symbol_conf.show_hist_headers; 1946 browser->hbt = hbt; 1947 browser->env = env; 1948 } 1949 1950 return browser; 1951 } 1952 1953 static void hist_browser__delete(struct hist_browser *browser) 1954 { 1955 free(browser); 1956 } 1957 1958 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 1959 { 1960 return browser->he_selection; 1961 } 1962 1963 static struct thread *hist_browser__selected_thread(struct hist_browser *browser) 1964 { 1965 return browser->he_selection->thread; 1966 } 1967 1968 /* Check whether the browser is for 'top' or 'report' */ 1969 static inline bool is_report_browser(void *timer) 1970 { 1971 return timer == NULL; 1972 } 1973 1974 static int hists__browser_title(struct hists *hists, 1975 struct hist_browser_timer *hbt, 1976 char *bf, size_t size) 1977 { 1978 char unit; 1979 int printed; 1980 const struct dso *dso = hists->dso_filter; 1981 const struct thread *thread = hists->thread_filter; 1982 int socket_id = hists->socket_filter; 1983 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1984 u64 nr_events = hists->stats.total_period; 1985 struct perf_evsel *evsel = hists_to_evsel(hists); 1986 const char *ev_name = perf_evsel__name(evsel); 1987 char buf[512]; 1988 size_t buflen = sizeof(buf); 1989 char ref[30] = " show reference callgraph, "; 1990 bool enable_ref = false; 1991 1992 if (symbol_conf.filter_relative) { 1993 nr_samples = hists->stats.nr_non_filtered_samples; 1994 nr_events = hists->stats.total_non_filtered_period; 1995 } 1996 1997 if (perf_evsel__is_group_event(evsel)) { 1998 struct perf_evsel *pos; 1999 2000 perf_evsel__group_desc(evsel, buf, buflen); 2001 ev_name = buf; 2002 2003 for_each_group_member(pos, evsel) { 2004 struct hists *pos_hists = evsel__hists(pos); 2005 2006 if (symbol_conf.filter_relative) { 2007 nr_samples += pos_hists->stats.nr_non_filtered_samples; 2008 nr_events += pos_hists->stats.total_non_filtered_period; 2009 } else { 2010 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2011 nr_events += pos_hists->stats.total_period; 2012 } 2013 } 2014 } 2015 2016 if (symbol_conf.show_ref_callgraph && 2017 strstr(ev_name, "call-graph=no")) 2018 enable_ref = true; 2019 nr_samples = convert_unit(nr_samples, &unit); 2020 printed = scnprintf(bf, size, 2021 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, 2022 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); 2023 2024 2025 if (hists->uid_filter_str) 2026 printed += snprintf(bf + printed, size - printed, 2027 ", UID: %s", hists->uid_filter_str); 2028 if (thread) 2029 printed += scnprintf(bf + printed, size - printed, 2030 ", Thread: %s(%d)", 2031 (thread->comm_set ? thread__comm_str(thread) : ""), 2032 thread->tid); 2033 if (dso) 2034 printed += scnprintf(bf + printed, size - printed, 2035 ", DSO: %s", dso->short_name); 2036 if (socket_id > -1) 2037 printed += scnprintf(bf + printed, size - printed, 2038 ", Processor Socket: %d", socket_id); 2039 if (!is_report_browser(hbt)) { 2040 struct perf_top *top = hbt->arg; 2041 2042 if (top->zero) 2043 printed += scnprintf(bf + printed, size - printed, " [z]"); 2044 } 2045 2046 return printed; 2047 } 2048 2049 static inline void free_popup_options(char **options, int n) 2050 { 2051 int i; 2052 2053 for (i = 0; i < n; ++i) 2054 zfree(&options[i]); 2055 } 2056 2057 /* 2058 * Only runtime switching of perf data file will make "input_name" point 2059 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 2060 * whether we need to call free() for current "input_name" during the switch. 2061 */ 2062 static bool is_input_name_malloced = false; 2063 2064 static int switch_data_file(void) 2065 { 2066 char *pwd, *options[32], *abs_path[32], *tmp; 2067 DIR *pwd_dir; 2068 int nr_options = 0, choice = -1, ret = -1; 2069 struct dirent *dent; 2070 2071 pwd = getenv("PWD"); 2072 if (!pwd) 2073 return ret; 2074 2075 pwd_dir = opendir(pwd); 2076 if (!pwd_dir) 2077 return ret; 2078 2079 memset(options, 0, sizeof(options)); 2080 memset(options, 0, sizeof(abs_path)); 2081 2082 while ((dent = readdir(pwd_dir))) { 2083 char path[PATH_MAX]; 2084 u64 magic; 2085 char *name = dent->d_name; 2086 FILE *file; 2087 2088 if (!(dent->d_type == DT_REG)) 2089 continue; 2090 2091 snprintf(path, sizeof(path), "%s/%s", pwd, name); 2092 2093 file = fopen(path, "r"); 2094 if (!file) 2095 continue; 2096 2097 if (fread(&magic, 1, 8, file) < 8) 2098 goto close_file_and_continue; 2099 2100 if (is_perf_magic(magic)) { 2101 options[nr_options] = strdup(name); 2102 if (!options[nr_options]) 2103 goto close_file_and_continue; 2104 2105 abs_path[nr_options] = strdup(path); 2106 if (!abs_path[nr_options]) { 2107 zfree(&options[nr_options]); 2108 ui__warning("Can't search all data files due to memory shortage.\n"); 2109 fclose(file); 2110 break; 2111 } 2112 2113 nr_options++; 2114 } 2115 2116 close_file_and_continue: 2117 fclose(file); 2118 if (nr_options >= 32) { 2119 ui__warning("Too many perf data files in PWD!\n" 2120 "Only the first 32 files will be listed.\n"); 2121 break; 2122 } 2123 } 2124 closedir(pwd_dir); 2125 2126 if (nr_options) { 2127 choice = ui__popup_menu(nr_options, options); 2128 if (choice < nr_options && choice >= 0) { 2129 tmp = strdup(abs_path[choice]); 2130 if (tmp) { 2131 if (is_input_name_malloced) 2132 free((void *)input_name); 2133 input_name = tmp; 2134 is_input_name_malloced = true; 2135 ret = 0; 2136 } else 2137 ui__warning("Data switch failed due to memory shortage!\n"); 2138 } 2139 } 2140 2141 free_popup_options(options, nr_options); 2142 free_popup_options(abs_path, nr_options); 2143 return ret; 2144 } 2145 2146 struct popup_action { 2147 struct thread *thread; 2148 struct map_symbol ms; 2149 int socket; 2150 2151 int (*fn)(struct hist_browser *browser, struct popup_action *act); 2152 }; 2153 2154 static int 2155 do_annotate(struct hist_browser *browser, struct popup_action *act) 2156 { 2157 struct perf_evsel *evsel; 2158 struct annotation *notes; 2159 struct hist_entry *he; 2160 int err; 2161 2162 if (!objdump_path && perf_env__lookup_objdump(browser->env)) 2163 return 0; 2164 2165 notes = symbol__annotation(act->ms.sym); 2166 if (!notes->src) 2167 return 0; 2168 2169 evsel = hists_to_evsel(browser->hists); 2170 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); 2171 he = hist_browser__selected_entry(browser); 2172 /* 2173 * offer option to annotate the other branch source or target 2174 * (if they exists) when returning from annotate 2175 */ 2176 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 2177 return 1; 2178 2179 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 2180 if (err) 2181 ui_browser__handle_resize(&browser->b); 2182 return 0; 2183 } 2184 2185 static int 2186 add_annotate_opt(struct hist_browser *browser __maybe_unused, 2187 struct popup_action *act, char **optstr, 2188 struct map *map, struct symbol *sym) 2189 { 2190 if (sym == NULL || map->dso->annotate_warned) 2191 return 0; 2192 2193 if (asprintf(optstr, "Annotate %s", sym->name) < 0) 2194 return 0; 2195 2196 act->ms.map = map; 2197 act->ms.sym = sym; 2198 act->fn = do_annotate; 2199 return 1; 2200 } 2201 2202 static int 2203 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 2204 { 2205 struct thread *thread = act->thread; 2206 2207 if (browser->hists->thread_filter) { 2208 pstack__remove(browser->pstack, &browser->hists->thread_filter); 2209 perf_hpp__set_elide(HISTC_THREAD, false); 2210 thread__zput(browser->hists->thread_filter); 2211 ui_helpline__pop(); 2212 } else { 2213 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 2214 thread->comm_set ? thread__comm_str(thread) : "", 2215 thread->tid); 2216 browser->hists->thread_filter = thread__get(thread); 2217 perf_hpp__set_elide(HISTC_THREAD, false); 2218 pstack__push(browser->pstack, &browser->hists->thread_filter); 2219 } 2220 2221 hists__filter_by_thread(browser->hists); 2222 hist_browser__reset(browser); 2223 return 0; 2224 } 2225 2226 static int 2227 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 2228 char **optstr, struct thread *thread) 2229 { 2230 if (!sort__has_thread || thread == NULL) 2231 return 0; 2232 2233 if (asprintf(optstr, "Zoom %s %s(%d) thread", 2234 browser->hists->thread_filter ? "out of" : "into", 2235 thread->comm_set ? thread__comm_str(thread) : "", 2236 thread->tid) < 0) 2237 return 0; 2238 2239 act->thread = thread; 2240 act->fn = do_zoom_thread; 2241 return 1; 2242 } 2243 2244 static int 2245 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 2246 { 2247 struct map *map = act->ms.map; 2248 2249 if (browser->hists->dso_filter) { 2250 pstack__remove(browser->pstack, &browser->hists->dso_filter); 2251 perf_hpp__set_elide(HISTC_DSO, false); 2252 browser->hists->dso_filter = NULL; 2253 ui_helpline__pop(); 2254 } else { 2255 if (map == NULL) 2256 return 0; 2257 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", 2258 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name); 2259 browser->hists->dso_filter = map->dso; 2260 perf_hpp__set_elide(HISTC_DSO, true); 2261 pstack__push(browser->pstack, &browser->hists->dso_filter); 2262 } 2263 2264 hists__filter_by_dso(browser->hists); 2265 hist_browser__reset(browser); 2266 return 0; 2267 } 2268 2269 static int 2270 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 2271 char **optstr, struct map *map) 2272 { 2273 if (!sort__has_dso || map == NULL) 2274 return 0; 2275 2276 if (asprintf(optstr, "Zoom %s %s DSO", 2277 browser->hists->dso_filter ? "out of" : "into", 2278 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) 2279 return 0; 2280 2281 act->ms.map = map; 2282 act->fn = do_zoom_dso; 2283 return 1; 2284 } 2285 2286 static int 2287 do_browse_map(struct hist_browser *browser __maybe_unused, 2288 struct popup_action *act) 2289 { 2290 map__browse(act->ms.map); 2291 return 0; 2292 } 2293 2294 static int 2295 add_map_opt(struct hist_browser *browser __maybe_unused, 2296 struct popup_action *act, char **optstr, struct map *map) 2297 { 2298 if (!sort__has_dso || map == NULL) 2299 return 0; 2300 2301 if (asprintf(optstr, "Browse map details") < 0) 2302 return 0; 2303 2304 act->ms.map = map; 2305 act->fn = do_browse_map; 2306 return 1; 2307 } 2308 2309 static int 2310 do_run_script(struct hist_browser *browser __maybe_unused, 2311 struct popup_action *act) 2312 { 2313 char script_opt[64]; 2314 memset(script_opt, 0, sizeof(script_opt)); 2315 2316 if (act->thread) { 2317 scnprintf(script_opt, sizeof(script_opt), " -c %s ", 2318 thread__comm_str(act->thread)); 2319 } else if (act->ms.sym) { 2320 scnprintf(script_opt, sizeof(script_opt), " -S %s ", 2321 act->ms.sym->name); 2322 } 2323 2324 script_browse(script_opt); 2325 return 0; 2326 } 2327 2328 static int 2329 add_script_opt(struct hist_browser *browser __maybe_unused, 2330 struct popup_action *act, char **optstr, 2331 struct thread *thread, struct symbol *sym) 2332 { 2333 if (thread) { 2334 if (asprintf(optstr, "Run scripts for samples of thread [%s]", 2335 thread__comm_str(thread)) < 0) 2336 return 0; 2337 } else if (sym) { 2338 if (asprintf(optstr, "Run scripts for samples of symbol [%s]", 2339 sym->name) < 0) 2340 return 0; 2341 } else { 2342 if (asprintf(optstr, "Run scripts for all samples") < 0) 2343 return 0; 2344 } 2345 2346 act->thread = thread; 2347 act->ms.sym = sym; 2348 act->fn = do_run_script; 2349 return 1; 2350 } 2351 2352 static int 2353 do_switch_data(struct hist_browser *browser __maybe_unused, 2354 struct popup_action *act __maybe_unused) 2355 { 2356 if (switch_data_file()) { 2357 ui__warning("Won't switch the data files due to\n" 2358 "no valid data file get selected!\n"); 2359 return 0; 2360 } 2361 2362 return K_SWITCH_INPUT_DATA; 2363 } 2364 2365 static int 2366 add_switch_opt(struct hist_browser *browser, 2367 struct popup_action *act, char **optstr) 2368 { 2369 if (!is_report_browser(browser->hbt)) 2370 return 0; 2371 2372 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 2373 return 0; 2374 2375 act->fn = do_switch_data; 2376 return 1; 2377 } 2378 2379 static int 2380 do_exit_browser(struct hist_browser *browser __maybe_unused, 2381 struct popup_action *act __maybe_unused) 2382 { 2383 return 0; 2384 } 2385 2386 static int 2387 add_exit_opt(struct hist_browser *browser __maybe_unused, 2388 struct popup_action *act, char **optstr) 2389 { 2390 if (asprintf(optstr, "Exit") < 0) 2391 return 0; 2392 2393 act->fn = do_exit_browser; 2394 return 1; 2395 } 2396 2397 static int 2398 do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 2399 { 2400 if (browser->hists->socket_filter > -1) { 2401 pstack__remove(browser->pstack, &browser->hists->socket_filter); 2402 browser->hists->socket_filter = -1; 2403 perf_hpp__set_elide(HISTC_SOCKET, false); 2404 } else { 2405 browser->hists->socket_filter = act->socket; 2406 perf_hpp__set_elide(HISTC_SOCKET, true); 2407 pstack__push(browser->pstack, &browser->hists->socket_filter); 2408 } 2409 2410 hists__filter_by_socket(browser->hists); 2411 hist_browser__reset(browser); 2412 return 0; 2413 } 2414 2415 static int 2416 add_socket_opt(struct hist_browser *browser, struct popup_action *act, 2417 char **optstr, int socket_id) 2418 { 2419 if (!sort__has_socket || socket_id < 0) 2420 return 0; 2421 2422 if (asprintf(optstr, "Zoom %s Processor Socket %d", 2423 (browser->hists->socket_filter > -1) ? "out of" : "into", 2424 socket_id) < 0) 2425 return 0; 2426 2427 act->socket = socket_id; 2428 act->fn = do_zoom_socket; 2429 return 1; 2430 } 2431 2432 static void hist_browser__update_nr_entries(struct hist_browser *hb) 2433 { 2434 u64 nr_entries = 0; 2435 struct rb_node *nd = rb_first(&hb->hists->entries); 2436 2437 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { 2438 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 2439 return; 2440 } 2441 2442 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2443 nr_entries++; 2444 nd = rb_hierarchy_next(nd); 2445 } 2446 2447 hb->nr_non_filtered_entries = nr_entries; 2448 hb->nr_hierarchy_entries = nr_entries; 2449 } 2450 2451 static void hist_browser__update_percent_limit(struct hist_browser *hb, 2452 double percent) 2453 { 2454 struct hist_entry *he; 2455 struct rb_node *nd = rb_first(&hb->hists->entries); 2456 u64 total = hists__total_period(hb->hists); 2457 u64 min_callchain_hits = total * (percent / 100); 2458 2459 hb->min_pcnt = callchain_param.min_percent = percent; 2460 2461 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2462 he = rb_entry(nd, struct hist_entry, rb_node); 2463 2464 if (!he->leaf || !symbol_conf.use_callchain) 2465 goto next; 2466 2467 if (callchain_param.mode == CHAIN_GRAPH_REL) { 2468 total = he->stat.period; 2469 2470 if (symbol_conf.cumulate_callchain) 2471 total = he->stat_acc->period; 2472 2473 min_callchain_hits = total * (percent / 100); 2474 } 2475 2476 callchain_param.sort(&he->sorted_chain, he->callchain, 2477 min_callchain_hits, &callchain_param); 2478 2479 next: 2480 /* 2481 * Tentatively set unfolded so that the rb_hierarchy_next() 2482 * can toggle children of folded entries too. 2483 */ 2484 he->unfolded = he->has_children; 2485 nd = rb_hierarchy_next(nd); 2486 2487 /* force to re-evaluate folding state of callchains */ 2488 he->init_have_children = false; 2489 hist_entry__set_folding(he, hb, false); 2490 } 2491 } 2492 2493 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 2494 const char *helpline, 2495 bool left_exits, 2496 struct hist_browser_timer *hbt, 2497 float min_pcnt, 2498 struct perf_env *env) 2499 { 2500 struct hists *hists = evsel__hists(evsel); 2501 struct hist_browser *browser = hist_browser__new(hists, hbt, env); 2502 struct branch_info *bi; 2503 #define MAX_OPTIONS 16 2504 char *options[MAX_OPTIONS]; 2505 struct popup_action actions[MAX_OPTIONS]; 2506 int nr_options = 0; 2507 int key = -1; 2508 char buf[64]; 2509 int delay_secs = hbt ? hbt->refresh : 0; 2510 struct perf_hpp_fmt *fmt; 2511 2512 #define HIST_BROWSER_HELP_COMMON \ 2513 "h/?/F1 Show this window\n" \ 2514 "UP/DOWN/PGUP\n" \ 2515 "PGDN/SPACE Navigate\n" \ 2516 "q/ESC/CTRL+C Exit browser\n\n" \ 2517 "For multiple event sessions:\n\n" \ 2518 "TAB/UNTAB Switch events\n\n" \ 2519 "For symbolic views (--sort has sym):\n\n" \ 2520 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ 2521 "ESC Zoom out\n" \ 2522 "a Annotate current symbol\n" \ 2523 "C Collapse all callchains\n" \ 2524 "d Zoom into current DSO\n" \ 2525 "E Expand all callchains\n" \ 2526 "F Toggle percentage of filtered entries\n" \ 2527 "H Display column headers\n" \ 2528 "L Change percent limit\n" \ 2529 "m Display context menu\n" \ 2530 "S Zoom into current Processor Socket\n" \ 2531 2532 /* help messages are sorted by lexical order of the hotkey */ 2533 const char report_help[] = HIST_BROWSER_HELP_COMMON 2534 "i Show header information\n" 2535 "P Print histograms to perf.hist.N\n" 2536 "r Run available scripts\n" 2537 "s Switch to another data file in PWD\n" 2538 "t Zoom into current Thread\n" 2539 "V Verbose (DSO names in callchains, etc)\n" 2540 "/ Filter symbol by name"; 2541 const char top_help[] = HIST_BROWSER_HELP_COMMON 2542 "P Print histograms to perf.hist.N\n" 2543 "t Zoom into current Thread\n" 2544 "V Verbose (DSO names in callchains, etc)\n" 2545 "z Toggle zeroing of samples\n" 2546 "f Enable/Disable events\n" 2547 "/ Filter symbol by name"; 2548 2549 if (browser == NULL) 2550 return -1; 2551 2552 /* reset abort key so that it can get Ctrl-C as a key */ 2553 SLang_reset_tty(); 2554 SLang_init_tty(0, 0, 0); 2555 2556 if (min_pcnt) 2557 browser->min_pcnt = min_pcnt; 2558 hist_browser__update_nr_entries(browser); 2559 2560 browser->pstack = pstack__new(3); 2561 if (browser->pstack == NULL) 2562 goto out; 2563 2564 ui_helpline__push(helpline); 2565 2566 memset(options, 0, sizeof(options)); 2567 memset(actions, 0, sizeof(actions)); 2568 2569 hists__for_each_format(browser->hists, fmt) { 2570 perf_hpp__reset_width(fmt, hists); 2571 /* 2572 * This is done just once, and activates the horizontal scrolling 2573 * code in the ui_browser code, it would be better to have a the 2574 * counter in the perf_hpp code, but I couldn't find doing it here 2575 * works, FIXME by setting this in hist_browser__new, for now, be 2576 * clever 8-) 2577 */ 2578 ++browser->b.columns; 2579 } 2580 2581 if (symbol_conf.col_width_list_str) 2582 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 2583 2584 while (1) { 2585 struct thread *thread = NULL; 2586 struct map *map = NULL; 2587 int choice = 0; 2588 int socked_id = -1; 2589 2590 nr_options = 0; 2591 2592 key = hist_browser__run(browser, helpline); 2593 2594 if (browser->he_selection != NULL) { 2595 thread = hist_browser__selected_thread(browser); 2596 map = browser->selection->map; 2597 socked_id = browser->he_selection->socket; 2598 } 2599 switch (key) { 2600 case K_TAB: 2601 case K_UNTAB: 2602 if (nr_events == 1) 2603 continue; 2604 /* 2605 * Exit the browser, let hists__browser_tree 2606 * go to the next or previous 2607 */ 2608 goto out_free_stack; 2609 case 'a': 2610 if (!sort__has_sym) { 2611 ui_browser__warning(&browser->b, delay_secs * 2, 2612 "Annotation is only available for symbolic views, " 2613 "include \"sym*\" in --sort to use it."); 2614 continue; 2615 } 2616 2617 if (browser->selection == NULL || 2618 browser->selection->sym == NULL || 2619 browser->selection->map->dso->annotate_warned) 2620 continue; 2621 2622 actions->ms.map = browser->selection->map; 2623 actions->ms.sym = browser->selection->sym; 2624 do_annotate(browser, actions); 2625 continue; 2626 case 'P': 2627 hist_browser__dump(browser); 2628 continue; 2629 case 'd': 2630 actions->ms.map = map; 2631 do_zoom_dso(browser, actions); 2632 continue; 2633 case 'V': 2634 browser->show_dso = !browser->show_dso; 2635 continue; 2636 case 't': 2637 actions->thread = thread; 2638 do_zoom_thread(browser, actions); 2639 continue; 2640 case 'S': 2641 actions->socket = socked_id; 2642 do_zoom_socket(browser, actions); 2643 continue; 2644 case '/': 2645 if (ui_browser__input_window("Symbol to show", 2646 "Please enter the name of symbol you want to see.\n" 2647 "To remove the filter later, press / + ENTER.", 2648 buf, "ENTER: OK, ESC: Cancel", 2649 delay_secs * 2) == K_ENTER) { 2650 hists->symbol_filter_str = *buf ? buf : NULL; 2651 hists__filter_by_symbol(hists); 2652 hist_browser__reset(browser); 2653 } 2654 continue; 2655 case 'r': 2656 if (is_report_browser(hbt)) { 2657 actions->thread = NULL; 2658 actions->ms.sym = NULL; 2659 do_run_script(browser, actions); 2660 } 2661 continue; 2662 case 's': 2663 if (is_report_browser(hbt)) { 2664 key = do_switch_data(browser, actions); 2665 if (key == K_SWITCH_INPUT_DATA) 2666 goto out_free_stack; 2667 } 2668 continue; 2669 case 'i': 2670 /* env->arch is NULL for live-mode (i.e. perf top) */ 2671 if (env->arch) 2672 tui__header_window(env); 2673 continue; 2674 case 'F': 2675 symbol_conf.filter_relative ^= 1; 2676 continue; 2677 case 'z': 2678 if (!is_report_browser(hbt)) { 2679 struct perf_top *top = hbt->arg; 2680 2681 top->zero = !top->zero; 2682 } 2683 continue; 2684 case 'L': 2685 if (ui_browser__input_window("Percent Limit", 2686 "Please enter the value you want to hide entries under that percent.", 2687 buf, "ENTER: OK, ESC: Cancel", 2688 delay_secs * 2) == K_ENTER) { 2689 char *end; 2690 double new_percent = strtod(buf, &end); 2691 2692 if (new_percent < 0 || new_percent > 100) { 2693 ui_browser__warning(&browser->b, delay_secs * 2, 2694 "Invalid percent: %.2f", new_percent); 2695 continue; 2696 } 2697 2698 hist_browser__update_percent_limit(browser, new_percent); 2699 hist_browser__reset(browser); 2700 } 2701 continue; 2702 case K_F1: 2703 case 'h': 2704 case '?': 2705 ui_browser__help_window(&browser->b, 2706 is_report_browser(hbt) ? report_help : top_help); 2707 continue; 2708 case K_ENTER: 2709 case K_RIGHT: 2710 case 'm': 2711 /* menu */ 2712 break; 2713 case K_ESC: 2714 case K_LEFT: { 2715 const void *top; 2716 2717 if (pstack__empty(browser->pstack)) { 2718 /* 2719 * Go back to the perf_evsel_menu__run or other user 2720 */ 2721 if (left_exits) 2722 goto out_free_stack; 2723 2724 if (key == K_ESC && 2725 ui_browser__dialog_yesno(&browser->b, 2726 "Do you really want to exit?")) 2727 goto out_free_stack; 2728 2729 continue; 2730 } 2731 top = pstack__peek(browser->pstack); 2732 if (top == &browser->hists->dso_filter) { 2733 /* 2734 * No need to set actions->dso here since 2735 * it's just to remove the current filter. 2736 * Ditto for thread below. 2737 */ 2738 do_zoom_dso(browser, actions); 2739 } else if (top == &browser->hists->thread_filter) { 2740 do_zoom_thread(browser, actions); 2741 } else if (top == &browser->hists->socket_filter) { 2742 do_zoom_socket(browser, actions); 2743 } 2744 continue; 2745 } 2746 case 'q': 2747 case CTRL('c'): 2748 goto out_free_stack; 2749 case 'f': 2750 if (!is_report_browser(hbt)) { 2751 struct perf_top *top = hbt->arg; 2752 2753 perf_evlist__toggle_enable(top->evlist); 2754 /* 2755 * No need to refresh, resort/decay histogram 2756 * entries if we are not collecting samples: 2757 */ 2758 if (top->evlist->enabled) { 2759 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 2760 hbt->refresh = delay_secs; 2761 } else { 2762 helpline = "Press 'f' again to re-enable the events"; 2763 hbt->refresh = 0; 2764 } 2765 continue; 2766 } 2767 /* Fall thru */ 2768 default: 2769 helpline = "Press '?' for help on key bindings"; 2770 continue; 2771 } 2772 2773 if (!sort__has_sym || browser->selection == NULL) 2774 goto skip_annotation; 2775 2776 if (sort__mode == SORT_MODE__BRANCH) { 2777 bi = browser->he_selection->branch_info; 2778 2779 if (bi == NULL) 2780 goto skip_annotation; 2781 2782 nr_options += add_annotate_opt(browser, 2783 &actions[nr_options], 2784 &options[nr_options], 2785 bi->from.map, 2786 bi->from.sym); 2787 if (bi->to.sym != bi->from.sym) 2788 nr_options += add_annotate_opt(browser, 2789 &actions[nr_options], 2790 &options[nr_options], 2791 bi->to.map, 2792 bi->to.sym); 2793 } else { 2794 nr_options += add_annotate_opt(browser, 2795 &actions[nr_options], 2796 &options[nr_options], 2797 browser->selection->map, 2798 browser->selection->sym); 2799 } 2800 skip_annotation: 2801 nr_options += add_thread_opt(browser, &actions[nr_options], 2802 &options[nr_options], thread); 2803 nr_options += add_dso_opt(browser, &actions[nr_options], 2804 &options[nr_options], map); 2805 nr_options += add_map_opt(browser, &actions[nr_options], 2806 &options[nr_options], 2807 browser->selection ? 2808 browser->selection->map : NULL); 2809 nr_options += add_socket_opt(browser, &actions[nr_options], 2810 &options[nr_options], 2811 socked_id); 2812 /* perf script support */ 2813 if (!is_report_browser(hbt)) 2814 goto skip_scripting; 2815 2816 if (browser->he_selection) { 2817 if (sort__has_thread && thread) { 2818 nr_options += add_script_opt(browser, 2819 &actions[nr_options], 2820 &options[nr_options], 2821 thread, NULL); 2822 } 2823 /* 2824 * Note that browser->selection != NULL 2825 * when browser->he_selection is not NULL, 2826 * so we don't need to check browser->selection 2827 * before fetching browser->selection->sym like what 2828 * we do before fetching browser->selection->map. 2829 * 2830 * See hist_browser__show_entry. 2831 */ 2832 if (sort__has_sym && browser->selection->sym) { 2833 nr_options += add_script_opt(browser, 2834 &actions[nr_options], 2835 &options[nr_options], 2836 NULL, browser->selection->sym); 2837 } 2838 } 2839 nr_options += add_script_opt(browser, &actions[nr_options], 2840 &options[nr_options], NULL, NULL); 2841 nr_options += add_switch_opt(browser, &actions[nr_options], 2842 &options[nr_options]); 2843 skip_scripting: 2844 nr_options += add_exit_opt(browser, &actions[nr_options], 2845 &options[nr_options]); 2846 2847 do { 2848 struct popup_action *act; 2849 2850 choice = ui__popup_menu(nr_options, options); 2851 if (choice == -1 || choice >= nr_options) 2852 break; 2853 2854 act = &actions[choice]; 2855 key = act->fn(browser, act); 2856 } while (key == 1); 2857 2858 if (key == K_SWITCH_INPUT_DATA) 2859 break; 2860 } 2861 out_free_stack: 2862 pstack__delete(browser->pstack); 2863 out: 2864 hist_browser__delete(browser); 2865 free_popup_options(options, MAX_OPTIONS); 2866 return key; 2867 } 2868 2869 struct perf_evsel_menu { 2870 struct ui_browser b; 2871 struct perf_evsel *selection; 2872 bool lost_events, lost_events_warned; 2873 float min_pcnt; 2874 struct perf_env *env; 2875 }; 2876 2877 static void perf_evsel_menu__write(struct ui_browser *browser, 2878 void *entry, int row) 2879 { 2880 struct perf_evsel_menu *menu = container_of(browser, 2881 struct perf_evsel_menu, b); 2882 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2883 struct hists *hists = evsel__hists(evsel); 2884 bool current_entry = ui_browser__is_current_entry(browser, row); 2885 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2886 const char *ev_name = perf_evsel__name(evsel); 2887 char bf[256], unit; 2888 const char *warn = " "; 2889 size_t printed; 2890 2891 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 2892 HE_COLORSET_NORMAL); 2893 2894 if (perf_evsel__is_group_event(evsel)) { 2895 struct perf_evsel *pos; 2896 2897 ev_name = perf_evsel__group_name(evsel); 2898 2899 for_each_group_member(pos, evsel) { 2900 struct hists *pos_hists = evsel__hists(pos); 2901 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2902 } 2903 } 2904 2905 nr_events = convert_unit(nr_events, &unit); 2906 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 2907 unit, unit == ' ' ? "" : " ", ev_name); 2908 ui_browser__printf(browser, "%s", bf); 2909 2910 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 2911 if (nr_events != 0) { 2912 menu->lost_events = true; 2913 if (!current_entry) 2914 ui_browser__set_color(browser, HE_COLORSET_TOP); 2915 nr_events = convert_unit(nr_events, &unit); 2916 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 2917 nr_events, unit, unit == ' ' ? "" : " "); 2918 warn = bf; 2919 } 2920 2921 ui_browser__write_nstring(browser, warn, browser->width - printed); 2922 2923 if (current_entry) 2924 menu->selection = evsel; 2925 } 2926 2927 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 2928 int nr_events, const char *help, 2929 struct hist_browser_timer *hbt) 2930 { 2931 struct perf_evlist *evlist = menu->b.priv; 2932 struct perf_evsel *pos; 2933 const char *title = "Available samples"; 2934 int delay_secs = hbt ? hbt->refresh : 0; 2935 int key; 2936 2937 if (ui_browser__show(&menu->b, title, 2938 "ESC: exit, ENTER|->: Browse histograms") < 0) 2939 return -1; 2940 2941 while (1) { 2942 key = ui_browser__run(&menu->b, delay_secs); 2943 2944 switch (key) { 2945 case K_TIMER: 2946 hbt->timer(hbt->arg); 2947 2948 if (!menu->lost_events_warned && menu->lost_events) { 2949 ui_browser__warn_lost_events(&menu->b); 2950 menu->lost_events_warned = true; 2951 } 2952 continue; 2953 case K_RIGHT: 2954 case K_ENTER: 2955 if (!menu->selection) 2956 continue; 2957 pos = menu->selection; 2958 browse_hists: 2959 perf_evlist__set_selected(evlist, pos); 2960 /* 2961 * Give the calling tool a chance to populate the non 2962 * default evsel resorted hists tree. 2963 */ 2964 if (hbt) 2965 hbt->timer(hbt->arg); 2966 key = perf_evsel__hists_browse(pos, nr_events, help, 2967 true, hbt, 2968 menu->min_pcnt, 2969 menu->env); 2970 ui_browser__show_title(&menu->b, title); 2971 switch (key) { 2972 case K_TAB: 2973 if (pos->node.next == &evlist->entries) 2974 pos = perf_evlist__first(evlist); 2975 else 2976 pos = perf_evsel__next(pos); 2977 goto browse_hists; 2978 case K_UNTAB: 2979 if (pos->node.prev == &evlist->entries) 2980 pos = perf_evlist__last(evlist); 2981 else 2982 pos = perf_evsel__prev(pos); 2983 goto browse_hists; 2984 case K_SWITCH_INPUT_DATA: 2985 case 'q': 2986 case CTRL('c'): 2987 goto out; 2988 case K_ESC: 2989 default: 2990 continue; 2991 } 2992 case K_LEFT: 2993 continue; 2994 case K_ESC: 2995 if (!ui_browser__dialog_yesno(&menu->b, 2996 "Do you really want to exit?")) 2997 continue; 2998 /* Fall thru */ 2999 case 'q': 3000 case CTRL('c'): 3001 goto out; 3002 default: 3003 continue; 3004 } 3005 } 3006 3007 out: 3008 ui_browser__hide(&menu->b); 3009 return key; 3010 } 3011 3012 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 3013 void *entry) 3014 { 3015 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 3016 3017 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 3018 return true; 3019 3020 return false; 3021 } 3022 3023 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 3024 int nr_entries, const char *help, 3025 struct hist_browser_timer *hbt, 3026 float min_pcnt, 3027 struct perf_env *env) 3028 { 3029 struct perf_evsel *pos; 3030 struct perf_evsel_menu menu = { 3031 .b = { 3032 .entries = &evlist->entries, 3033 .refresh = ui_browser__list_head_refresh, 3034 .seek = ui_browser__list_head_seek, 3035 .write = perf_evsel_menu__write, 3036 .filter = filter_group_entries, 3037 .nr_entries = nr_entries, 3038 .priv = evlist, 3039 }, 3040 .min_pcnt = min_pcnt, 3041 .env = env, 3042 }; 3043 3044 ui_helpline__push("Press ESC to exit"); 3045 3046 evlist__for_each(evlist, pos) { 3047 const char *ev_name = perf_evsel__name(pos); 3048 size_t line_len = strlen(ev_name) + 7; 3049 3050 if (menu.b.width < line_len) 3051 menu.b.width = line_len; 3052 } 3053 3054 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 3055 } 3056 3057 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 3058 struct hist_browser_timer *hbt, 3059 float min_pcnt, 3060 struct perf_env *env) 3061 { 3062 int nr_entries = evlist->nr_entries; 3063 3064 single_entry: 3065 if (nr_entries == 1) { 3066 struct perf_evsel *first = perf_evlist__first(evlist); 3067 3068 return perf_evsel__hists_browse(first, nr_entries, help, 3069 false, hbt, min_pcnt, 3070 env); 3071 } 3072 3073 if (symbol_conf.event_group) { 3074 struct perf_evsel *pos; 3075 3076 nr_entries = 0; 3077 evlist__for_each(evlist, pos) { 3078 if (perf_evsel__is_group_leader(pos)) 3079 nr_entries++; 3080 } 3081 3082 if (nr_entries == 1) 3083 goto single_entry; 3084 } 3085 3086 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 3087 hbt, min_pcnt, env); 3088 } 3089