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