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