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