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