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