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