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 if (browser->block_evsel) 2389 evsel = browser->block_evsel; 2390 else 2391 evsel = hists_to_evsel(browser->hists); 2392 2393 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, 2394 browser->annotation_opts); 2395 he = hist_browser__selected_entry(browser); 2396 /* 2397 * offer option to annotate the other branch source or target 2398 * (if they exists) when returning from annotate 2399 */ 2400 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 2401 return 1; 2402 2403 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 2404 if (err) 2405 ui_browser__handle_resize(&browser->b); 2406 return 0; 2407 } 2408 2409 static int 2410 add_annotate_opt(struct hist_browser *browser __maybe_unused, 2411 struct popup_action *act, char **optstr, 2412 struct map_symbol *ms) 2413 { 2414 if (ms->sym == NULL || ms->map->dso->annotate_warned) 2415 return 0; 2416 2417 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) 2418 return 0; 2419 2420 act->ms = *ms; 2421 act->fn = do_annotate; 2422 return 1; 2423 } 2424 2425 static int 2426 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 2427 { 2428 struct thread *thread = act->thread; 2429 2430 if ((!hists__has(browser->hists, thread) && 2431 !hists__has(browser->hists, comm)) || thread == NULL) 2432 return 0; 2433 2434 if (browser->hists->thread_filter) { 2435 pstack__remove(browser->pstack, &browser->hists->thread_filter); 2436 perf_hpp__set_elide(HISTC_THREAD, false); 2437 thread__zput(browser->hists->thread_filter); 2438 ui_helpline__pop(); 2439 } else { 2440 if (hists__has(browser->hists, thread)) { 2441 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 2442 thread->comm_set ? thread__comm_str(thread) : "", 2443 thread->tid); 2444 } else { 2445 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"", 2446 thread->comm_set ? thread__comm_str(thread) : ""); 2447 } 2448 2449 browser->hists->thread_filter = thread__get(thread); 2450 perf_hpp__set_elide(HISTC_THREAD, false); 2451 pstack__push(browser->pstack, &browser->hists->thread_filter); 2452 } 2453 2454 hists__filter_by_thread(browser->hists); 2455 hist_browser__reset(browser); 2456 return 0; 2457 } 2458 2459 static int 2460 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 2461 char **optstr, struct thread *thread) 2462 { 2463 int ret; 2464 2465 if ((!hists__has(browser->hists, thread) && 2466 !hists__has(browser->hists, comm)) || thread == NULL) 2467 return 0; 2468 2469 if (hists__has(browser->hists, thread)) { 2470 ret = asprintf(optstr, "Zoom %s %s(%d) thread", 2471 browser->hists->thread_filter ? "out of" : "into", 2472 thread->comm_set ? thread__comm_str(thread) : "", 2473 thread->tid); 2474 } else { 2475 ret = asprintf(optstr, "Zoom %s %s thread", 2476 browser->hists->thread_filter ? "out of" : "into", 2477 thread->comm_set ? thread__comm_str(thread) : ""); 2478 } 2479 if (ret < 0) 2480 return 0; 2481 2482 act->thread = thread; 2483 act->fn = do_zoom_thread; 2484 return 1; 2485 } 2486 2487 static int 2488 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 2489 { 2490 struct map *map = act->ms.map; 2491 2492 if (!hists__has(browser->hists, dso) || map == NULL) 2493 return 0; 2494 2495 if (browser->hists->dso_filter) { 2496 pstack__remove(browser->pstack, &browser->hists->dso_filter); 2497 perf_hpp__set_elide(HISTC_DSO, false); 2498 browser->hists->dso_filter = NULL; 2499 ui_helpline__pop(); 2500 } else { 2501 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", 2502 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name); 2503 browser->hists->dso_filter = map->dso; 2504 perf_hpp__set_elide(HISTC_DSO, true); 2505 pstack__push(browser->pstack, &browser->hists->dso_filter); 2506 } 2507 2508 hists__filter_by_dso(browser->hists); 2509 hist_browser__reset(browser); 2510 return 0; 2511 } 2512 2513 static int 2514 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 2515 char **optstr, struct map *map) 2516 { 2517 if (!hists__has(browser->hists, dso) || map == NULL) 2518 return 0; 2519 2520 if (asprintf(optstr, "Zoom %s %s DSO", 2521 browser->hists->dso_filter ? "out of" : "into", 2522 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) 2523 return 0; 2524 2525 act->ms.map = map; 2526 act->fn = do_zoom_dso; 2527 return 1; 2528 } 2529 2530 static int 2531 do_browse_map(struct hist_browser *browser __maybe_unused, 2532 struct popup_action *act) 2533 { 2534 map__browse(act->ms.map); 2535 return 0; 2536 } 2537 2538 static int 2539 add_map_opt(struct hist_browser *browser, 2540 struct popup_action *act, char **optstr, struct map *map) 2541 { 2542 if (!hists__has(browser->hists, dso) || map == NULL) 2543 return 0; 2544 2545 if (asprintf(optstr, "Browse map details") < 0) 2546 return 0; 2547 2548 act->ms.map = map; 2549 act->fn = do_browse_map; 2550 return 1; 2551 } 2552 2553 static int 2554 do_run_script(struct hist_browser *browser __maybe_unused, 2555 struct popup_action *act) 2556 { 2557 char *script_opt; 2558 int len; 2559 int n = 0; 2560 2561 len = 100; 2562 if (act->thread) 2563 len += strlen(thread__comm_str(act->thread)); 2564 else if (act->ms.sym) 2565 len += strlen(act->ms.sym->name); 2566 script_opt = malloc(len); 2567 if (!script_opt) 2568 return -1; 2569 2570 script_opt[0] = 0; 2571 if (act->thread) { 2572 n = scnprintf(script_opt, len, " -c %s ", 2573 thread__comm_str(act->thread)); 2574 } else if (act->ms.sym) { 2575 n = scnprintf(script_opt, len, " -S %s ", 2576 act->ms.sym->name); 2577 } 2578 2579 if (act->time) { 2580 char start[32], end[32]; 2581 unsigned long starttime = act->time; 2582 unsigned long endtime = act->time + symbol_conf.time_quantum; 2583 2584 if (starttime == endtime) { /* Display 1ms as fallback */ 2585 starttime -= 1*NSEC_PER_MSEC; 2586 endtime += 1*NSEC_PER_MSEC; 2587 } 2588 timestamp__scnprintf_usec(starttime, start, sizeof start); 2589 timestamp__scnprintf_usec(endtime, end, sizeof end); 2590 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); 2591 } 2592 2593 script_browse(script_opt, act->evsel); 2594 free(script_opt); 2595 return 0; 2596 } 2597 2598 static int 2599 do_res_sample_script(struct hist_browser *browser __maybe_unused, 2600 struct popup_action *act) 2601 { 2602 struct hist_entry *he; 2603 2604 he = hist_browser__selected_entry(browser); 2605 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); 2606 return 0; 2607 } 2608 2609 static int 2610 add_script_opt_2(struct hist_browser *browser __maybe_unused, 2611 struct popup_action *act, char **optstr, 2612 struct thread *thread, struct symbol *sym, 2613 struct evsel *evsel, const char *tstr) 2614 { 2615 2616 if (thread) { 2617 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s", 2618 thread__comm_str(thread), tstr) < 0) 2619 return 0; 2620 } else if (sym) { 2621 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s", 2622 sym->name, tstr) < 0) 2623 return 0; 2624 } else { 2625 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0) 2626 return 0; 2627 } 2628 2629 act->thread = thread; 2630 act->ms.sym = sym; 2631 act->evsel = evsel; 2632 act->fn = do_run_script; 2633 return 1; 2634 } 2635 2636 static int 2637 add_script_opt(struct hist_browser *browser, 2638 struct popup_action *act, char **optstr, 2639 struct thread *thread, struct symbol *sym, 2640 struct evsel *evsel) 2641 { 2642 int n, j; 2643 struct hist_entry *he; 2644 2645 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); 2646 2647 he = hist_browser__selected_entry(browser); 2648 if (sort_order && strstr(sort_order, "time")) { 2649 char tstr[128]; 2650 2651 optstr++; 2652 act++; 2653 j = sprintf(tstr, " in "); 2654 j += timestamp__scnprintf_usec(he->time, tstr + j, 2655 sizeof tstr - j); 2656 j += sprintf(tstr + j, "-"); 2657 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, 2658 tstr + j, sizeof tstr - j); 2659 n += add_script_opt_2(browser, act, optstr, thread, sym, 2660 evsel, tstr); 2661 act->time = he->time; 2662 } 2663 return n; 2664 } 2665 2666 static int 2667 add_res_sample_opt(struct hist_browser *browser __maybe_unused, 2668 struct popup_action *act, char **optstr, 2669 struct res_sample *res_sample, 2670 struct evsel *evsel, 2671 enum rstype type) 2672 { 2673 if (!res_sample) 2674 return 0; 2675 2676 if (asprintf(optstr, "Show context for individual samples %s", 2677 type == A_ASM ? "with assembler" : 2678 type == A_SOURCE ? "with source" : "") < 0) 2679 return 0; 2680 2681 act->fn = do_res_sample_script; 2682 act->evsel = evsel; 2683 act->rstype = type; 2684 return 1; 2685 } 2686 2687 static int 2688 do_switch_data(struct hist_browser *browser __maybe_unused, 2689 struct popup_action *act __maybe_unused) 2690 { 2691 if (switch_data_file()) { 2692 ui__warning("Won't switch the data files due to\n" 2693 "no valid data file get selected!\n"); 2694 return 0; 2695 } 2696 2697 return K_SWITCH_INPUT_DATA; 2698 } 2699 2700 static int 2701 add_switch_opt(struct hist_browser *browser, 2702 struct popup_action *act, char **optstr) 2703 { 2704 if (!is_report_browser(browser->hbt)) 2705 return 0; 2706 2707 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 2708 return 0; 2709 2710 act->fn = do_switch_data; 2711 return 1; 2712 } 2713 2714 static int 2715 do_exit_browser(struct hist_browser *browser __maybe_unused, 2716 struct popup_action *act __maybe_unused) 2717 { 2718 return 0; 2719 } 2720 2721 static int 2722 add_exit_opt(struct hist_browser *browser __maybe_unused, 2723 struct popup_action *act, char **optstr) 2724 { 2725 if (asprintf(optstr, "Exit") < 0) 2726 return 0; 2727 2728 act->fn = do_exit_browser; 2729 return 1; 2730 } 2731 2732 static int 2733 do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 2734 { 2735 if (!hists__has(browser->hists, socket) || act->socket < 0) 2736 return 0; 2737 2738 if (browser->hists->socket_filter > -1) { 2739 pstack__remove(browser->pstack, &browser->hists->socket_filter); 2740 browser->hists->socket_filter = -1; 2741 perf_hpp__set_elide(HISTC_SOCKET, false); 2742 } else { 2743 browser->hists->socket_filter = act->socket; 2744 perf_hpp__set_elide(HISTC_SOCKET, true); 2745 pstack__push(browser->pstack, &browser->hists->socket_filter); 2746 } 2747 2748 hists__filter_by_socket(browser->hists); 2749 hist_browser__reset(browser); 2750 return 0; 2751 } 2752 2753 static int 2754 add_socket_opt(struct hist_browser *browser, struct popup_action *act, 2755 char **optstr, int socket_id) 2756 { 2757 if (!hists__has(browser->hists, socket) || socket_id < 0) 2758 return 0; 2759 2760 if (asprintf(optstr, "Zoom %s Processor Socket %d", 2761 (browser->hists->socket_filter > -1) ? "out of" : "into", 2762 socket_id) < 0) 2763 return 0; 2764 2765 act->socket = socket_id; 2766 act->fn = do_zoom_socket; 2767 return 1; 2768 } 2769 2770 static void hist_browser__update_nr_entries(struct hist_browser *hb) 2771 { 2772 u64 nr_entries = 0; 2773 struct rb_node *nd = rb_first_cached(&hb->hists->entries); 2774 2775 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { 2776 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 2777 return; 2778 } 2779 2780 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2781 nr_entries++; 2782 nd = rb_hierarchy_next(nd); 2783 } 2784 2785 hb->nr_non_filtered_entries = nr_entries; 2786 hb->nr_hierarchy_entries = nr_entries; 2787 } 2788 2789 static void hist_browser__update_percent_limit(struct hist_browser *hb, 2790 double percent) 2791 { 2792 struct hist_entry *he; 2793 struct rb_node *nd = rb_first_cached(&hb->hists->entries); 2794 u64 total = hists__total_period(hb->hists); 2795 u64 min_callchain_hits = total * (percent / 100); 2796 2797 hb->min_pcnt = callchain_param.min_percent = percent; 2798 2799 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2800 he = rb_entry(nd, struct hist_entry, rb_node); 2801 2802 if (he->has_no_entry) { 2803 he->has_no_entry = false; 2804 he->nr_rows = 0; 2805 } 2806 2807 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain) 2808 goto next; 2809 2810 if (callchain_param.mode == CHAIN_GRAPH_REL) { 2811 total = he->stat.period; 2812 2813 if (symbol_conf.cumulate_callchain) 2814 total = he->stat_acc->period; 2815 2816 min_callchain_hits = total * (percent / 100); 2817 } 2818 2819 callchain_param.sort(&he->sorted_chain, he->callchain, 2820 min_callchain_hits, &callchain_param); 2821 2822 next: 2823 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); 2824 2825 /* force to re-evaluate folding state of callchains */ 2826 he->init_have_children = false; 2827 hist_entry__set_folding(he, hb, false); 2828 } 2829 } 2830 2831 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, 2832 const char *helpline, 2833 bool left_exits, 2834 struct hist_browser_timer *hbt, 2835 float min_pcnt, 2836 struct perf_env *env, 2837 bool warn_lost_event, 2838 struct annotation_options *annotation_opts) 2839 { 2840 struct hists *hists = evsel__hists(evsel); 2841 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts); 2842 struct branch_info *bi = NULL; 2843 #define MAX_OPTIONS 16 2844 char *options[MAX_OPTIONS]; 2845 struct popup_action actions[MAX_OPTIONS]; 2846 int nr_options = 0; 2847 int key = -1; 2848 char buf[64]; 2849 int delay_secs = hbt ? hbt->refresh : 0; 2850 2851 #define HIST_BROWSER_HELP_COMMON \ 2852 "h/?/F1 Show this window\n" \ 2853 "UP/DOWN/PGUP\n" \ 2854 "PGDN/SPACE Navigate\n" \ 2855 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \ 2856 "For multiple event sessions:\n\n" \ 2857 "TAB/UNTAB Switch events\n\n" \ 2858 "For symbolic views (--sort has sym):\n\n" \ 2859 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ 2860 "ESC Zoom out\n" \ 2861 "a Annotate current symbol\n" \ 2862 "C Collapse all callchains\n" \ 2863 "d Zoom into current DSO\n" \ 2864 "E Expand all callchains\n" \ 2865 "F Toggle percentage of filtered entries\n" \ 2866 "H Display column headers\n" \ 2867 "L Change percent limit\n" \ 2868 "m Display context menu\n" \ 2869 "S Zoom into current Processor Socket\n" \ 2870 2871 /* help messages are sorted by lexical order of the hotkey */ 2872 static const char report_help[] = HIST_BROWSER_HELP_COMMON 2873 "i Show header information\n" 2874 "P Print histograms to perf.hist.N\n" 2875 "r Run available scripts\n" 2876 "s Switch to another data file in PWD\n" 2877 "t Zoom into current Thread\n" 2878 "V Verbose (DSO names in callchains, etc)\n" 2879 "/ Filter symbol by name"; 2880 static const char top_help[] = HIST_BROWSER_HELP_COMMON 2881 "P Print histograms to perf.hist.N\n" 2882 "t Zoom into current Thread\n" 2883 "V Verbose (DSO names in callchains, etc)\n" 2884 "z Toggle zeroing of samples\n" 2885 "f Enable/Disable events\n" 2886 "/ Filter symbol by name"; 2887 2888 if (browser == NULL) 2889 return -1; 2890 2891 /* reset abort key so that it can get Ctrl-C as a key */ 2892 SLang_reset_tty(); 2893 SLang_init_tty(0, 0, 0); 2894 2895 if (min_pcnt) 2896 browser->min_pcnt = min_pcnt; 2897 hist_browser__update_nr_entries(browser); 2898 2899 browser->pstack = pstack__new(3); 2900 if (browser->pstack == NULL) 2901 goto out; 2902 2903 ui_helpline__push(helpline); 2904 2905 memset(options, 0, sizeof(options)); 2906 memset(actions, 0, sizeof(actions)); 2907 2908 if (symbol_conf.col_width_list_str) 2909 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 2910 2911 if (!is_report_browser(hbt)) 2912 browser->b.no_samples_msg = "Collecting samples..."; 2913 2914 while (1) { 2915 struct thread *thread = NULL; 2916 struct map *map = NULL; 2917 int choice = 0; 2918 int socked_id = -1; 2919 2920 nr_options = 0; 2921 2922 key = hist_browser__run(browser, helpline, 2923 warn_lost_event); 2924 2925 if (browser->he_selection != NULL) { 2926 thread = hist_browser__selected_thread(browser); 2927 map = browser->selection->map; 2928 socked_id = browser->he_selection->socket; 2929 } 2930 switch (key) { 2931 case K_TAB: 2932 case K_UNTAB: 2933 if (nr_events == 1) 2934 continue; 2935 /* 2936 * Exit the browser, let hists__browser_tree 2937 * go to the next or previous 2938 */ 2939 goto out_free_stack; 2940 case 'a': 2941 if (!hists__has(hists, sym)) { 2942 ui_browser__warning(&browser->b, delay_secs * 2, 2943 "Annotation is only available for symbolic views, " 2944 "include \"sym*\" in --sort to use it."); 2945 continue; 2946 } 2947 2948 if (browser->selection == NULL || 2949 browser->selection->sym == NULL || 2950 browser->selection->map->dso->annotate_warned) 2951 continue; 2952 2953 actions->ms.map = browser->selection->map; 2954 actions->ms.sym = browser->selection->sym; 2955 do_annotate(browser, actions); 2956 continue; 2957 case 'P': 2958 hist_browser__dump(browser); 2959 continue; 2960 case 'd': 2961 actions->ms.map = map; 2962 do_zoom_dso(browser, actions); 2963 continue; 2964 case 'V': 2965 verbose = (verbose + 1) % 4; 2966 browser->show_dso = verbose > 0; 2967 ui_helpline__fpush("Verbosity level set to %d\n", 2968 verbose); 2969 continue; 2970 case 't': 2971 actions->thread = thread; 2972 do_zoom_thread(browser, actions); 2973 continue; 2974 case 'S': 2975 actions->socket = socked_id; 2976 do_zoom_socket(browser, actions); 2977 continue; 2978 case '/': 2979 if (ui_browser__input_window("Symbol to show", 2980 "Please enter the name of symbol you want to see.\n" 2981 "To remove the filter later, press / + ENTER.", 2982 buf, "ENTER: OK, ESC: Cancel", 2983 delay_secs * 2) == K_ENTER) { 2984 hists->symbol_filter_str = *buf ? buf : NULL; 2985 hists__filter_by_symbol(hists); 2986 hist_browser__reset(browser); 2987 } 2988 continue; 2989 case 'r': 2990 if (is_report_browser(hbt)) { 2991 actions->thread = NULL; 2992 actions->ms.sym = NULL; 2993 do_run_script(browser, actions); 2994 } 2995 continue; 2996 case 's': 2997 if (is_report_browser(hbt)) { 2998 key = do_switch_data(browser, actions); 2999 if (key == K_SWITCH_INPUT_DATA) 3000 goto out_free_stack; 3001 } 3002 continue; 3003 case 'i': 3004 /* env->arch is NULL for live-mode (i.e. perf top) */ 3005 if (env->arch) 3006 tui__header_window(env); 3007 continue; 3008 case 'F': 3009 symbol_conf.filter_relative ^= 1; 3010 continue; 3011 case 'z': 3012 if (!is_report_browser(hbt)) { 3013 struct perf_top *top = hbt->arg; 3014 3015 top->zero = !top->zero; 3016 } 3017 continue; 3018 case 'L': 3019 if (ui_browser__input_window("Percent Limit", 3020 "Please enter the value you want to hide entries under that percent.", 3021 buf, "ENTER: OK, ESC: Cancel", 3022 delay_secs * 2) == K_ENTER) { 3023 char *end; 3024 double new_percent = strtod(buf, &end); 3025 3026 if (new_percent < 0 || new_percent > 100) { 3027 ui_browser__warning(&browser->b, delay_secs * 2, 3028 "Invalid percent: %.2f", new_percent); 3029 continue; 3030 } 3031 3032 hist_browser__update_percent_limit(browser, new_percent); 3033 hist_browser__reset(browser); 3034 } 3035 continue; 3036 case K_F1: 3037 case 'h': 3038 case '?': 3039 ui_browser__help_window(&browser->b, 3040 is_report_browser(hbt) ? report_help : top_help); 3041 continue; 3042 case K_ENTER: 3043 case K_RIGHT: 3044 case 'm': 3045 /* menu */ 3046 break; 3047 case K_ESC: 3048 case K_LEFT: { 3049 const void *top; 3050 3051 if (pstack__empty(browser->pstack)) { 3052 /* 3053 * Go back to the perf_evsel_menu__run or other user 3054 */ 3055 if (left_exits) 3056 goto out_free_stack; 3057 3058 if (key == K_ESC && 3059 ui_browser__dialog_yesno(&browser->b, 3060 "Do you really want to exit?")) 3061 goto out_free_stack; 3062 3063 continue; 3064 } 3065 top = pstack__peek(browser->pstack); 3066 if (top == &browser->hists->dso_filter) { 3067 /* 3068 * No need to set actions->dso here since 3069 * it's just to remove the current filter. 3070 * Ditto for thread below. 3071 */ 3072 do_zoom_dso(browser, actions); 3073 } else if (top == &browser->hists->thread_filter) { 3074 do_zoom_thread(browser, actions); 3075 } else if (top == &browser->hists->socket_filter) { 3076 do_zoom_socket(browser, actions); 3077 } 3078 continue; 3079 } 3080 case 'q': 3081 case CTRL('c'): 3082 goto out_free_stack; 3083 case 'f': 3084 if (!is_report_browser(hbt)) { 3085 struct perf_top *top = hbt->arg; 3086 3087 perf_evlist__toggle_enable(top->evlist); 3088 /* 3089 * No need to refresh, resort/decay histogram 3090 * entries if we are not collecting samples: 3091 */ 3092 if (top->evlist->enabled) { 3093 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 3094 hbt->refresh = delay_secs; 3095 } else { 3096 helpline = "Press 'f' again to re-enable the events"; 3097 hbt->refresh = 0; 3098 } 3099 continue; 3100 } 3101 /* Fall thru */ 3102 default: 3103 helpline = "Press '?' for help on key bindings"; 3104 continue; 3105 } 3106 3107 if (!hists__has(hists, sym) || browser->selection == NULL) 3108 goto skip_annotation; 3109 3110 if (sort__mode == SORT_MODE__BRANCH) { 3111 3112 if (browser->he_selection) 3113 bi = browser->he_selection->branch_info; 3114 3115 if (bi == NULL) 3116 goto skip_annotation; 3117 3118 nr_options += add_annotate_opt(browser, 3119 &actions[nr_options], 3120 &options[nr_options], 3121 &bi->from.ms); 3122 if (bi->to.ms.sym != bi->from.ms.sym) 3123 nr_options += add_annotate_opt(browser, 3124 &actions[nr_options], 3125 &options[nr_options], 3126 &bi->to.ms); 3127 } else { 3128 nr_options += add_annotate_opt(browser, 3129 &actions[nr_options], 3130 &options[nr_options], 3131 browser->selection); 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 3452 static int block_hists_browser__title(struct hist_browser *browser, char *bf, 3453 size_t size) 3454 { 3455 struct hists *hists = evsel__hists(browser->block_evsel); 3456 const char *evname = perf_evsel__name(browser->block_evsel); 3457 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 3458 int ret; 3459 3460 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples); 3461 if (evname) 3462 scnprintf(bf + ret, size - ret, " of event '%s'", evname); 3463 3464 return 0; 3465 } 3466 3467 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, 3468 float min_percent, struct perf_env *env, 3469 struct annotation_options *annotation_opts) 3470 { 3471 struct hists *hists = &bh->block_hists; 3472 struct hist_browser *browser; 3473 int key = -1; 3474 struct popup_action action; 3475 static const char help[] = 3476 " q Quit \n"; 3477 3478 browser = hist_browser__new(hists); 3479 if (!browser) 3480 return -1; 3481 3482 browser->block_evsel = evsel; 3483 browser->title = block_hists_browser__title; 3484 browser->min_pcnt = min_percent; 3485 browser->env = env; 3486 browser->annotation_opts = annotation_opts; 3487 3488 /* reset abort key so that it can get Ctrl-C as a key */ 3489 SLang_reset_tty(); 3490 SLang_init_tty(0, 0, 0); 3491 3492 memset(&action, 0, sizeof(action)); 3493 3494 while (1) { 3495 key = hist_browser__run(browser, "? - help", true); 3496 3497 switch (key) { 3498 case 'q': 3499 goto out; 3500 case '?': 3501 ui_browser__help_window(&browser->b, help); 3502 break; 3503 case 'a': 3504 case K_ENTER: 3505 if (!browser->selection || 3506 !browser->selection->sym) { 3507 continue; 3508 } 3509 3510 action.ms.map = browser->selection->map; 3511 action.ms.sym = browser->selection->sym; 3512 do_annotate(browser, &action); 3513 continue; 3514 default: 3515 break; 3516 } 3517 } 3518 3519 out: 3520 hist_browser__delete(browser); 3521 return 0; 3522 } 3523