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