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