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