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