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