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 if (need_percent) 935 callchain_list_counts__printf_value(node, chain, NULL, 936 buf, sizeof(buf)); 937 else 938 callchain_list_counts__printf_value(NULL, chain, NULL, 939 buf, sizeof(buf)); 940 941 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0) 942 str = "Not enough memory!"; 943 else 944 str = alloc_str2; 945 } 946 947 if (need_percent) { 948 callchain_node__scnprintf_value(node, buf, sizeof(buf), 949 total); 950 951 if (asprintf(&alloc_str, "%s %s", buf, str) < 0) 952 str = "Not enough memory!"; 953 else 954 str = alloc_str; 955 } 956 957 print(browser, chain, str, offset, row, arg); 958 free(alloc_str); 959 free(alloc_str2); 960 961 if (symbol_conf.inline_name) { 962 inline_rows = show_inline_list(browser, chain->ms.map, 963 chain->ip, row + 1, offset); 964 } 965 966 return ret + inline_rows; 967 } 968 969 static bool check_percent_display(struct rb_node *node, u64 parent_total) 970 { 971 struct callchain_node *child; 972 973 if (node == NULL) 974 return false; 975 976 if (rb_next(node)) 977 return true; 978 979 child = rb_entry(node, struct callchain_node, rb_node); 980 return callchain_cumul_hits(child) != parent_total; 981 } 982 983 static int hist_browser__show_callchain_flat(struct hist_browser *browser, 984 struct rb_root *root, 985 unsigned short row, u64 total, 986 u64 parent_total, 987 print_callchain_entry_fn print, 988 struct callchain_print_arg *arg, 989 check_output_full_fn is_output_full) 990 { 991 struct rb_node *node; 992 int first_row = row, offset = LEVEL_OFFSET_STEP; 993 bool need_percent; 994 995 node = rb_first(root); 996 need_percent = check_percent_display(node, parent_total); 997 998 while (node) { 999 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 1000 struct rb_node *next = rb_next(node); 1001 struct callchain_list *chain; 1002 char folded_sign = ' '; 1003 int first = true; 1004 int extra_offset = 0; 1005 1006 list_for_each_entry(chain, &child->parent_val, list) { 1007 bool was_first = first; 1008 1009 if (first) 1010 first = false; 1011 else if (need_percent) 1012 extra_offset = LEVEL_OFFSET_STEP; 1013 1014 folded_sign = callchain_list__folded(chain); 1015 1016 row += hist_browser__show_callchain_list(browser, child, 1017 chain, row, total, 1018 was_first && need_percent, 1019 offset + extra_offset, 1020 print, arg); 1021 1022 if (is_output_full(browser, row)) 1023 goto out; 1024 1025 if (folded_sign == '+') 1026 goto next; 1027 } 1028 1029 list_for_each_entry(chain, &child->val, list) { 1030 bool was_first = first; 1031 1032 if (first) 1033 first = false; 1034 else if (need_percent) 1035 extra_offset = LEVEL_OFFSET_STEP; 1036 1037 folded_sign = callchain_list__folded(chain); 1038 1039 row += hist_browser__show_callchain_list(browser, child, 1040 chain, row, total, 1041 was_first && need_percent, 1042 offset + extra_offset, 1043 print, arg); 1044 1045 if (is_output_full(browser, row)) 1046 goto out; 1047 1048 if (folded_sign == '+') 1049 break; 1050 } 1051 1052 next: 1053 if (is_output_full(browser, row)) 1054 break; 1055 node = next; 1056 } 1057 out: 1058 return row - first_row; 1059 } 1060 1061 static char *hist_browser__folded_callchain_str(struct hist_browser *browser, 1062 struct callchain_list *chain, 1063 char *value_str, char *old_str) 1064 { 1065 char bf[1024]; 1066 const char *str; 1067 char *new; 1068 1069 str = callchain_list__sym_name(chain, bf, sizeof(bf), 1070 browser->show_dso); 1071 if (old_str) { 1072 if (asprintf(&new, "%s%s%s", old_str, 1073 symbol_conf.field_sep ?: ";", str) < 0) 1074 new = NULL; 1075 } else { 1076 if (value_str) { 1077 if (asprintf(&new, "%s %s", value_str, str) < 0) 1078 new = NULL; 1079 } else { 1080 if (asprintf(&new, "%s", str) < 0) 1081 new = NULL; 1082 } 1083 } 1084 return new; 1085 } 1086 1087 static int hist_browser__show_callchain_folded(struct hist_browser *browser, 1088 struct rb_root *root, 1089 unsigned short row, u64 total, 1090 u64 parent_total, 1091 print_callchain_entry_fn print, 1092 struct callchain_print_arg *arg, 1093 check_output_full_fn is_output_full) 1094 { 1095 struct rb_node *node; 1096 int first_row = row, offset = LEVEL_OFFSET_STEP; 1097 bool need_percent; 1098 1099 node = rb_first(root); 1100 need_percent = check_percent_display(node, parent_total); 1101 1102 while (node) { 1103 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 1104 struct rb_node *next = rb_next(node); 1105 struct callchain_list *chain, *first_chain = NULL; 1106 int first = true; 1107 char *value_str = NULL, *value_str_alloc = NULL; 1108 char *chain_str = NULL, *chain_str_alloc = NULL; 1109 1110 if (arg->row_offset != 0) { 1111 arg->row_offset--; 1112 goto next; 1113 } 1114 1115 if (need_percent) { 1116 char buf[64]; 1117 1118 callchain_node__scnprintf_value(child, buf, sizeof(buf), total); 1119 if (asprintf(&value_str, "%s", buf) < 0) { 1120 value_str = (char *)"<...>"; 1121 goto do_print; 1122 } 1123 value_str_alloc = value_str; 1124 } 1125 1126 list_for_each_entry(chain, &child->parent_val, list) { 1127 chain_str = hist_browser__folded_callchain_str(browser, 1128 chain, value_str, chain_str); 1129 if (first) { 1130 first = false; 1131 first_chain = chain; 1132 } 1133 1134 if (chain_str == NULL) { 1135 chain_str = (char *)"Not enough memory!"; 1136 goto do_print; 1137 } 1138 1139 chain_str_alloc = chain_str; 1140 } 1141 1142 list_for_each_entry(chain, &child->val, list) { 1143 chain_str = hist_browser__folded_callchain_str(browser, 1144 chain, value_str, chain_str); 1145 if (first) { 1146 first = false; 1147 first_chain = chain; 1148 } 1149 1150 if (chain_str == NULL) { 1151 chain_str = (char *)"Not enough memory!"; 1152 goto do_print; 1153 } 1154 1155 chain_str_alloc = chain_str; 1156 } 1157 1158 do_print: 1159 print(browser, first_chain, chain_str, offset, row++, arg); 1160 free(value_str_alloc); 1161 free(chain_str_alloc); 1162 1163 next: 1164 if (is_output_full(browser, row)) 1165 break; 1166 node = next; 1167 } 1168 1169 return row - first_row; 1170 } 1171 1172 static int hist_browser__show_callchain_graph(struct hist_browser *browser, 1173 struct rb_root *root, int level, 1174 unsigned short row, u64 total, 1175 u64 parent_total, 1176 print_callchain_entry_fn print, 1177 struct callchain_print_arg *arg, 1178 check_output_full_fn is_output_full) 1179 { 1180 struct rb_node *node; 1181 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 1182 bool need_percent; 1183 u64 percent_total = total; 1184 1185 if (callchain_param.mode == CHAIN_GRAPH_REL) 1186 percent_total = parent_total; 1187 1188 node = rb_first(root); 1189 need_percent = check_percent_display(node, parent_total); 1190 1191 while (node) { 1192 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 1193 struct rb_node *next = rb_next(node); 1194 struct callchain_list *chain; 1195 char folded_sign = ' '; 1196 int first = true; 1197 int extra_offset = 0; 1198 1199 list_for_each_entry(chain, &child->val, list) { 1200 bool was_first = first; 1201 1202 if (first) 1203 first = false; 1204 else if (need_percent) 1205 extra_offset = LEVEL_OFFSET_STEP; 1206 1207 folded_sign = callchain_list__folded(chain); 1208 1209 row += hist_browser__show_callchain_list(browser, child, 1210 chain, row, percent_total, 1211 was_first && need_percent, 1212 offset + extra_offset, 1213 print, arg); 1214 1215 if (is_output_full(browser, row)) 1216 goto out; 1217 1218 if (folded_sign == '+') 1219 break; 1220 } 1221 1222 if (folded_sign == '-') { 1223 const int new_level = level + (extra_offset ? 2 : 1); 1224 1225 row += hist_browser__show_callchain_graph(browser, &child->rb_root, 1226 new_level, row, total, 1227 child->children_hit, 1228 print, arg, is_output_full); 1229 } 1230 if (is_output_full(browser, row)) 1231 break; 1232 node = next; 1233 } 1234 out: 1235 return row - first_row; 1236 } 1237 1238 static int hist_browser__show_callchain(struct hist_browser *browser, 1239 struct hist_entry *entry, int level, 1240 unsigned short row, 1241 print_callchain_entry_fn print, 1242 struct callchain_print_arg *arg, 1243 check_output_full_fn is_output_full) 1244 { 1245 u64 total = hists__total_period(entry->hists); 1246 u64 parent_total; 1247 int printed; 1248 1249 if (symbol_conf.cumulate_callchain) 1250 parent_total = entry->stat_acc->period; 1251 else 1252 parent_total = entry->stat.period; 1253 1254 if (callchain_param.mode == CHAIN_FLAT) { 1255 printed = hist_browser__show_callchain_flat(browser, 1256 &entry->sorted_chain, row, 1257 total, parent_total, print, arg, 1258 is_output_full); 1259 } else if (callchain_param.mode == CHAIN_FOLDED) { 1260 printed = hist_browser__show_callchain_folded(browser, 1261 &entry->sorted_chain, row, 1262 total, parent_total, print, arg, 1263 is_output_full); 1264 } else { 1265 printed = hist_browser__show_callchain_graph(browser, 1266 &entry->sorted_chain, level, row, 1267 total, parent_total, print, arg, 1268 is_output_full); 1269 } 1270 1271 if (arg->is_current_entry) 1272 browser->he_selection = entry; 1273 1274 return printed; 1275 } 1276 1277 struct hpp_arg { 1278 struct ui_browser *b; 1279 char folded_sign; 1280 bool current_entry; 1281 }; 1282 1283 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 1284 { 1285 struct hpp_arg *arg = hpp->ptr; 1286 int ret, len; 1287 va_list args; 1288 double percent; 1289 1290 va_start(args, fmt); 1291 len = va_arg(args, int); 1292 percent = va_arg(args, double); 1293 va_end(args); 1294 1295 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 1296 1297 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 1298 ui_browser__printf(arg->b, "%s", hpp->buf); 1299 1300 return ret; 1301 } 1302 1303 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 1304 static u64 __hpp_get_##_field(struct hist_entry *he) \ 1305 { \ 1306 return he->stat._field; \ 1307 } \ 1308 \ 1309 static int \ 1310 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 1311 struct perf_hpp *hpp, \ 1312 struct hist_entry *he) \ 1313 { \ 1314 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 1315 __hpp__slsmg_color_printf, true); \ 1316 } 1317 1318 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 1319 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 1320 { \ 1321 return he->stat_acc->_field; \ 1322 } \ 1323 \ 1324 static int \ 1325 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 1326 struct perf_hpp *hpp, \ 1327 struct hist_entry *he) \ 1328 { \ 1329 if (!symbol_conf.cumulate_callchain) { \ 1330 struct hpp_arg *arg = hpp->ptr; \ 1331 int len = fmt->user_len ?: fmt->len; \ 1332 int ret = scnprintf(hpp->buf, hpp->size, \ 1333 "%*s", len, "N/A"); \ 1334 ui_browser__printf(arg->b, "%s", hpp->buf); \ 1335 \ 1336 return ret; \ 1337 } \ 1338 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ 1339 " %*.2f%%", __hpp__slsmg_color_printf, true); \ 1340 } 1341 1342 __HPP_COLOR_PERCENT_FN(overhead, period) 1343 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 1344 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 1345 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 1346 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 1347 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 1348 1349 #undef __HPP_COLOR_PERCENT_FN 1350 #undef __HPP_COLOR_ACC_PERCENT_FN 1351 1352 void hist_browser__init_hpp(void) 1353 { 1354 perf_hpp__format[PERF_HPP__OVERHEAD].color = 1355 hist_browser__hpp_color_overhead; 1356 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 1357 hist_browser__hpp_color_overhead_sys; 1358 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 1359 hist_browser__hpp_color_overhead_us; 1360 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 1361 hist_browser__hpp_color_overhead_guest_sys; 1362 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 1363 hist_browser__hpp_color_overhead_guest_us; 1364 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 1365 hist_browser__hpp_color_overhead_acc; 1366 } 1367 1368 static int hist_browser__show_entry(struct hist_browser *browser, 1369 struct hist_entry *entry, 1370 unsigned short row) 1371 { 1372 int printed = 0; 1373 int width = browser->b.width; 1374 char folded_sign = ' '; 1375 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1376 off_t row_offset = entry->row_offset; 1377 bool first = true; 1378 struct perf_hpp_fmt *fmt; 1379 1380 if (current_entry) { 1381 browser->he_selection = entry; 1382 browser->selection = &entry->ms; 1383 } 1384 1385 if (symbol_conf.use_callchain) { 1386 hist_entry__init_have_children(entry); 1387 folded_sign = hist_entry__folded(entry); 1388 } 1389 1390 if (symbol_conf.inline_name && 1391 (!entry->has_children)) { 1392 hist_entry_init_inline_node(entry); 1393 folded_sign = hist_entry__folded(entry); 1394 } 1395 1396 if (row_offset == 0) { 1397 struct hpp_arg arg = { 1398 .b = &browser->b, 1399 .folded_sign = folded_sign, 1400 .current_entry = current_entry, 1401 }; 1402 int column = 0; 1403 1404 hist_browser__gotorc(browser, row, 0); 1405 1406 hists__for_each_format(browser->hists, fmt) { 1407 char s[2048]; 1408 struct perf_hpp hpp = { 1409 .buf = s, 1410 .size = sizeof(s), 1411 .ptr = &arg, 1412 }; 1413 1414 if (perf_hpp__should_skip(fmt, entry->hists) || 1415 column++ < browser->b.horiz_scroll) 1416 continue; 1417 1418 if (current_entry && browser->b.navkeypressed) { 1419 ui_browser__set_color(&browser->b, 1420 HE_COLORSET_SELECTED); 1421 } else { 1422 ui_browser__set_color(&browser->b, 1423 HE_COLORSET_NORMAL); 1424 } 1425 1426 if (first) { 1427 if (symbol_conf.use_callchain || 1428 symbol_conf.inline_name) { 1429 ui_browser__printf(&browser->b, "%c ", folded_sign); 1430 width -= 2; 1431 } 1432 first = false; 1433 } else { 1434 ui_browser__printf(&browser->b, " "); 1435 width -= 2; 1436 } 1437 1438 if (fmt->color) { 1439 int ret = fmt->color(fmt, &hpp, entry); 1440 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1441 /* 1442 * fmt->color() already used ui_browser to 1443 * print the non alignment bits, skip it (+ret): 1444 */ 1445 ui_browser__printf(&browser->b, "%s", s + ret); 1446 } else { 1447 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry)); 1448 ui_browser__printf(&browser->b, "%s", s); 1449 } 1450 width -= hpp.buf - s; 1451 } 1452 1453 /* The scroll bar isn't being used */ 1454 if (!browser->b.navkeypressed) 1455 width += 1; 1456 1457 ui_browser__write_nstring(&browser->b, "", width); 1458 1459 ++row; 1460 ++printed; 1461 } else 1462 --row_offset; 1463 1464 if (folded_sign == '-' && row != browser->b.rows) { 1465 struct callchain_print_arg arg = { 1466 .row_offset = row_offset, 1467 .is_current_entry = current_entry, 1468 }; 1469 1470 if (entry->inline_node) 1471 printed += hist_browser__show_inline(browser, 1472 entry->inline_node, row, 0); 1473 else 1474 printed += hist_browser__show_callchain(browser, 1475 entry, 1, row, 1476 hist_browser__show_callchain_entry, 1477 &arg, 1478 hist_browser__check_output_full); 1479 } 1480 1481 return printed; 1482 } 1483 1484 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, 1485 struct hist_entry *entry, 1486 unsigned short row, 1487 int level) 1488 { 1489 int printed = 0; 1490 int width = browser->b.width; 1491 char folded_sign = ' '; 1492 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1493 off_t row_offset = entry->row_offset; 1494 bool first = true; 1495 struct perf_hpp_fmt *fmt; 1496 struct perf_hpp_list_node *fmt_node; 1497 struct hpp_arg arg = { 1498 .b = &browser->b, 1499 .current_entry = current_entry, 1500 }; 1501 int column = 0; 1502 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; 1503 1504 if (current_entry) { 1505 browser->he_selection = entry; 1506 browser->selection = &entry->ms; 1507 } 1508 1509 hist_entry__init_have_children(entry); 1510 folded_sign = hist_entry__folded(entry); 1511 arg.folded_sign = folded_sign; 1512 1513 if (entry->leaf && row_offset) { 1514 row_offset--; 1515 goto show_callchain; 1516 } 1517 1518 hist_browser__gotorc(browser, row, 0); 1519 1520 if (current_entry && browser->b.navkeypressed) 1521 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); 1522 else 1523 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); 1524 1525 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); 1526 width -= level * HIERARCHY_INDENT; 1527 1528 /* the first hpp_list_node is for overhead columns */ 1529 fmt_node = list_first_entry(&entry->hists->hpp_formats, 1530 struct perf_hpp_list_node, list); 1531 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 1532 char s[2048]; 1533 struct perf_hpp hpp = { 1534 .buf = s, 1535 .size = sizeof(s), 1536 .ptr = &arg, 1537 }; 1538 1539 if (perf_hpp__should_skip(fmt, entry->hists) || 1540 column++ < browser->b.horiz_scroll) 1541 continue; 1542 1543 if (current_entry && browser->b.navkeypressed) { 1544 ui_browser__set_color(&browser->b, 1545 HE_COLORSET_SELECTED); 1546 } else { 1547 ui_browser__set_color(&browser->b, 1548 HE_COLORSET_NORMAL); 1549 } 1550 1551 if (first) { 1552 ui_browser__printf(&browser->b, "%c ", folded_sign); 1553 width -= 2; 1554 first = false; 1555 } else { 1556 ui_browser__printf(&browser->b, " "); 1557 width -= 2; 1558 } 1559 1560 if (fmt->color) { 1561 int ret = fmt->color(fmt, &hpp, entry); 1562 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1563 /* 1564 * fmt->color() already used ui_browser to 1565 * print the non alignment bits, skip it (+ret): 1566 */ 1567 ui_browser__printf(&browser->b, "%s", s + ret); 1568 } else { 1569 int ret = fmt->entry(fmt, &hpp, entry); 1570 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 1571 ui_browser__printf(&browser->b, "%s", s); 1572 } 1573 width -= hpp.buf - s; 1574 } 1575 1576 if (!first) { 1577 ui_browser__write_nstring(&browser->b, "", hierarchy_indent); 1578 width -= hierarchy_indent; 1579 } 1580 1581 if (column >= browser->b.horiz_scroll) { 1582 char s[2048]; 1583 struct perf_hpp hpp = { 1584 .buf = s, 1585 .size = sizeof(s), 1586 .ptr = &arg, 1587 }; 1588 1589 if (current_entry && browser->b.navkeypressed) { 1590 ui_browser__set_color(&browser->b, 1591 HE_COLORSET_SELECTED); 1592 } else { 1593 ui_browser__set_color(&browser->b, 1594 HE_COLORSET_NORMAL); 1595 } 1596 1597 perf_hpp_list__for_each_format(entry->hpp_list, fmt) { 1598 if (first) { 1599 ui_browser__printf(&browser->b, "%c ", folded_sign); 1600 first = false; 1601 } else { 1602 ui_browser__write_nstring(&browser->b, "", 2); 1603 } 1604 1605 width -= 2; 1606 1607 /* 1608 * No need to call hist_entry__snprintf_alignment() 1609 * since this fmt is always the last column in the 1610 * hierarchy mode. 1611 */ 1612 if (fmt->color) { 1613 width -= fmt->color(fmt, &hpp, entry); 1614 } else { 1615 int i = 0; 1616 1617 width -= fmt->entry(fmt, &hpp, entry); 1618 ui_browser__printf(&browser->b, "%s", ltrim(s)); 1619 1620 while (isspace(s[i++])) 1621 width++; 1622 } 1623 } 1624 } 1625 1626 /* The scroll bar isn't being used */ 1627 if (!browser->b.navkeypressed) 1628 width += 1; 1629 1630 ui_browser__write_nstring(&browser->b, "", width); 1631 1632 ++row; 1633 ++printed; 1634 1635 show_callchain: 1636 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) { 1637 struct callchain_print_arg carg = { 1638 .row_offset = row_offset, 1639 }; 1640 1641 printed += hist_browser__show_callchain(browser, entry, 1642 level + 1, row, 1643 hist_browser__show_callchain_entry, &carg, 1644 hist_browser__check_output_full); 1645 } 1646 1647 return printed; 1648 } 1649 1650 static int hist_browser__show_no_entry(struct hist_browser *browser, 1651 unsigned short row, int level) 1652 { 1653 int width = browser->b.width; 1654 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1655 bool first = true; 1656 int column = 0; 1657 int ret; 1658 struct perf_hpp_fmt *fmt; 1659 struct perf_hpp_list_node *fmt_node; 1660 int indent = browser->hists->nr_hpp_node - 2; 1661 1662 if (current_entry) { 1663 browser->he_selection = NULL; 1664 browser->selection = NULL; 1665 } 1666 1667 hist_browser__gotorc(browser, row, 0); 1668 1669 if (current_entry && browser->b.navkeypressed) 1670 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); 1671 else 1672 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); 1673 1674 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); 1675 width -= level * HIERARCHY_INDENT; 1676 1677 /* the first hpp_list_node is for overhead columns */ 1678 fmt_node = list_first_entry(&browser->hists->hpp_formats, 1679 struct perf_hpp_list_node, list); 1680 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 1681 if (perf_hpp__should_skip(fmt, browser->hists) || 1682 column++ < browser->b.horiz_scroll) 1683 continue; 1684 1685 ret = fmt->width(fmt, NULL, browser->hists); 1686 1687 if (first) { 1688 /* for folded sign */ 1689 first = false; 1690 ret++; 1691 } else { 1692 /* space between columns */ 1693 ret += 2; 1694 } 1695 1696 ui_browser__write_nstring(&browser->b, "", ret); 1697 width -= ret; 1698 } 1699 1700 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT); 1701 width -= indent * HIERARCHY_INDENT; 1702 1703 if (column >= browser->b.horiz_scroll) { 1704 char buf[32]; 1705 1706 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt); 1707 ui_browser__printf(&browser->b, " %s", buf); 1708 width -= ret + 2; 1709 } 1710 1711 /* The scroll bar isn't being used */ 1712 if (!browser->b.navkeypressed) 1713 width += 1; 1714 1715 ui_browser__write_nstring(&browser->b, "", width); 1716 return 1; 1717 } 1718 1719 static int advance_hpp_check(struct perf_hpp *hpp, int inc) 1720 { 1721 advance_hpp(hpp, inc); 1722 return hpp->size <= 0; 1723 } 1724 1725 static int 1726 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, 1727 size_t size, int line) 1728 { 1729 struct hists *hists = browser->hists; 1730 struct perf_hpp dummy_hpp = { 1731 .buf = buf, 1732 .size = size, 1733 }; 1734 struct perf_hpp_fmt *fmt; 1735 size_t ret = 0; 1736 int column = 0; 1737 int span = 0; 1738 1739 if (symbol_conf.use_callchain) { 1740 ret = scnprintf(buf, size, " "); 1741 if (advance_hpp_check(&dummy_hpp, ret)) 1742 return ret; 1743 } 1744 1745 hists__for_each_format(browser->hists, fmt) { 1746 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) 1747 continue; 1748 1749 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span); 1750 if (advance_hpp_check(&dummy_hpp, ret)) 1751 break; 1752 1753 if (span) 1754 continue; 1755 1756 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 1757 if (advance_hpp_check(&dummy_hpp, ret)) 1758 break; 1759 } 1760 1761 return ret; 1762 } 1763 1764 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) 1765 { 1766 struct hists *hists = browser->hists; 1767 struct perf_hpp dummy_hpp = { 1768 .buf = buf, 1769 .size = size, 1770 }; 1771 struct perf_hpp_fmt *fmt; 1772 struct perf_hpp_list_node *fmt_node; 1773 size_t ret = 0; 1774 int column = 0; 1775 int indent = hists->nr_hpp_node - 2; 1776 bool first_node, first_col; 1777 1778 ret = scnprintf(buf, size, " "); 1779 if (advance_hpp_check(&dummy_hpp, ret)) 1780 return ret; 1781 1782 first_node = true; 1783 /* the first hpp_list_node is for overhead columns */ 1784 fmt_node = list_first_entry(&hists->hpp_formats, 1785 struct perf_hpp_list_node, list); 1786 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 1787 if (column++ < browser->b.horiz_scroll) 1788 continue; 1789 1790 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 1791 if (advance_hpp_check(&dummy_hpp, ret)) 1792 break; 1793 1794 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 1795 if (advance_hpp_check(&dummy_hpp, ret)) 1796 break; 1797 1798 first_node = false; 1799 } 1800 1801 if (!first_node) { 1802 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", 1803 indent * HIERARCHY_INDENT, ""); 1804 if (advance_hpp_check(&dummy_hpp, ret)) 1805 return ret; 1806 } 1807 1808 first_node = true; 1809 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 1810 if (!first_node) { 1811 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / "); 1812 if (advance_hpp_check(&dummy_hpp, ret)) 1813 break; 1814 } 1815 first_node = false; 1816 1817 first_col = true; 1818 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 1819 char *start; 1820 1821 if (perf_hpp__should_skip(fmt, hists)) 1822 continue; 1823 1824 if (!first_col) { 1825 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+"); 1826 if (advance_hpp_check(&dummy_hpp, ret)) 1827 break; 1828 } 1829 first_col = false; 1830 1831 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 1832 dummy_hpp.buf[ret] = '\0'; 1833 1834 start = trim(dummy_hpp.buf); 1835 ret = strlen(start); 1836 1837 if (start != dummy_hpp.buf) 1838 memmove(dummy_hpp.buf, start, ret + 1); 1839 1840 if (advance_hpp_check(&dummy_hpp, ret)) 1841 break; 1842 } 1843 } 1844 1845 return ret; 1846 } 1847 1848 static void hists_browser__hierarchy_headers(struct hist_browser *browser) 1849 { 1850 char headers[1024]; 1851 1852 hists_browser__scnprintf_hierarchy_headers(browser, headers, 1853 sizeof(headers)); 1854 1855 ui_browser__gotorc(&browser->b, 0, 0); 1856 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1857 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1858 } 1859 1860 static void hists_browser__headers(struct hist_browser *browser) 1861 { 1862 struct hists *hists = browser->hists; 1863 struct perf_hpp_list *hpp_list = hists->hpp_list; 1864 1865 int line; 1866 1867 for (line = 0; line < hpp_list->nr_header_lines; line++) { 1868 char headers[1024]; 1869 1870 hists_browser__scnprintf_headers(browser, headers, 1871 sizeof(headers), line); 1872 1873 ui_browser__gotorc(&browser->b, line, 0); 1874 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1875 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1876 } 1877 } 1878 1879 static void hist_browser__show_headers(struct hist_browser *browser) 1880 { 1881 if (symbol_conf.report_hierarchy) 1882 hists_browser__hierarchy_headers(browser); 1883 else 1884 hists_browser__headers(browser); 1885 } 1886 1887 static void ui_browser__hists_init_top(struct ui_browser *browser) 1888 { 1889 if (browser->top == NULL) { 1890 struct hist_browser *hb; 1891 1892 hb = container_of(browser, struct hist_browser, b); 1893 browser->top = rb_first(&hb->hists->entries); 1894 } 1895 } 1896 1897 static unsigned int hist_browser__refresh(struct ui_browser *browser) 1898 { 1899 unsigned row = 0; 1900 u16 header_offset = 0; 1901 struct rb_node *nd; 1902 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 1903 struct hists *hists = hb->hists; 1904 1905 if (hb->show_headers) { 1906 struct perf_hpp_list *hpp_list = hists->hpp_list; 1907 1908 hist_browser__show_headers(hb); 1909 header_offset = hpp_list->nr_header_lines; 1910 } 1911 1912 ui_browser__hists_init_top(browser); 1913 hb->he_selection = NULL; 1914 hb->selection = NULL; 1915 1916 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) { 1917 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1918 float percent; 1919 1920 if (h->filtered) { 1921 /* let it move to sibling */ 1922 h->unfolded = false; 1923 continue; 1924 } 1925 1926 percent = hist_entry__get_percent_limit(h); 1927 if (percent < hb->min_pcnt) 1928 continue; 1929 1930 if (symbol_conf.report_hierarchy) { 1931 row += hist_browser__show_hierarchy_entry(hb, h, row, 1932 h->depth); 1933 if (row == browser->rows) 1934 break; 1935 1936 if (h->has_no_entry) { 1937 hist_browser__show_no_entry(hb, row, h->depth + 1); 1938 row++; 1939 } 1940 } else { 1941 row += hist_browser__show_entry(hb, h, row); 1942 } 1943 1944 if (row == browser->rows) 1945 break; 1946 } 1947 1948 return row + header_offset; 1949 } 1950 1951 static struct rb_node *hists__filter_entries(struct rb_node *nd, 1952 float min_pcnt) 1953 { 1954 while (nd != NULL) { 1955 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1956 float percent = hist_entry__get_percent_limit(h); 1957 1958 if (!h->filtered && percent >= min_pcnt) 1959 return nd; 1960 1961 /* 1962 * If it's filtered, its all children also were filtered. 1963 * So move to sibling node. 1964 */ 1965 if (rb_next(nd)) 1966 nd = rb_next(nd); 1967 else 1968 nd = rb_hierarchy_next(nd); 1969 } 1970 1971 return NULL; 1972 } 1973 1974 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 1975 float min_pcnt) 1976 { 1977 while (nd != NULL) { 1978 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1979 float percent = hist_entry__get_percent_limit(h); 1980 1981 if (!h->filtered && percent >= min_pcnt) 1982 return nd; 1983 1984 nd = rb_hierarchy_prev(nd); 1985 } 1986 1987 return NULL; 1988 } 1989 1990 static void ui_browser__hists_seek(struct ui_browser *browser, 1991 off_t offset, int whence) 1992 { 1993 struct hist_entry *h; 1994 struct rb_node *nd; 1995 bool first = true; 1996 struct hist_browser *hb; 1997 1998 hb = container_of(browser, struct hist_browser, b); 1999 2000 if (browser->nr_entries == 0) 2001 return; 2002 2003 ui_browser__hists_init_top(browser); 2004 2005 switch (whence) { 2006 case SEEK_SET: 2007 nd = hists__filter_entries(rb_first(browser->entries), 2008 hb->min_pcnt); 2009 break; 2010 case SEEK_CUR: 2011 nd = browser->top; 2012 goto do_offset; 2013 case SEEK_END: 2014 nd = rb_hierarchy_last(rb_last(browser->entries)); 2015 nd = hists__filter_prev_entries(nd, hb->min_pcnt); 2016 first = false; 2017 break; 2018 default: 2019 return; 2020 } 2021 2022 /* 2023 * Moves not relative to the first visible entry invalidates its 2024 * row_offset: 2025 */ 2026 h = rb_entry(browser->top, struct hist_entry, rb_node); 2027 h->row_offset = 0; 2028 2029 /* 2030 * Here we have to check if nd is expanded (+), if it is we can't go 2031 * the next top level hist_entry, instead we must compute an offset of 2032 * what _not_ to show and not change the first visible entry. 2033 * 2034 * This offset increments when we are going from top to bottom and 2035 * decreases when we're going from bottom to top. 2036 * 2037 * As we don't have backpointers to the top level in the callchains 2038 * structure, we need to always print the whole hist_entry callchain, 2039 * skipping the first ones that are before the first visible entry 2040 * and stop when we printed enough lines to fill the screen. 2041 */ 2042 do_offset: 2043 if (!nd) 2044 return; 2045 2046 if (offset > 0) { 2047 do { 2048 h = rb_entry(nd, struct hist_entry, rb_node); 2049 if (h->unfolded && h->leaf) { 2050 u16 remaining = h->nr_rows - h->row_offset; 2051 if (offset > remaining) { 2052 offset -= remaining; 2053 h->row_offset = 0; 2054 } else { 2055 h->row_offset += offset; 2056 offset = 0; 2057 browser->top = nd; 2058 break; 2059 } 2060 } 2061 nd = hists__filter_entries(rb_hierarchy_next(nd), 2062 hb->min_pcnt); 2063 if (nd == NULL) 2064 break; 2065 --offset; 2066 browser->top = nd; 2067 } while (offset != 0); 2068 } else if (offset < 0) { 2069 while (1) { 2070 h = rb_entry(nd, struct hist_entry, rb_node); 2071 if (h->unfolded && h->leaf) { 2072 if (first) { 2073 if (-offset > h->row_offset) { 2074 offset += h->row_offset; 2075 h->row_offset = 0; 2076 } else { 2077 h->row_offset += offset; 2078 offset = 0; 2079 browser->top = nd; 2080 break; 2081 } 2082 } else { 2083 if (-offset > h->nr_rows) { 2084 offset += h->nr_rows; 2085 h->row_offset = 0; 2086 } else { 2087 h->row_offset = h->nr_rows + offset; 2088 offset = 0; 2089 browser->top = nd; 2090 break; 2091 } 2092 } 2093 } 2094 2095 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd), 2096 hb->min_pcnt); 2097 if (nd == NULL) 2098 break; 2099 ++offset; 2100 browser->top = nd; 2101 if (offset == 0) { 2102 /* 2103 * Last unfiltered hist_entry, check if it is 2104 * unfolded, if it is then we should have 2105 * row_offset at its last entry. 2106 */ 2107 h = rb_entry(nd, struct hist_entry, rb_node); 2108 if (h->unfolded && h->leaf) 2109 h->row_offset = h->nr_rows; 2110 break; 2111 } 2112 first = false; 2113 } 2114 } else { 2115 browser->top = nd; 2116 h = rb_entry(nd, struct hist_entry, rb_node); 2117 h->row_offset = 0; 2118 } 2119 } 2120 2121 static int hist_browser__fprintf_callchain(struct hist_browser *browser, 2122 struct hist_entry *he, FILE *fp, 2123 int level) 2124 { 2125 struct callchain_print_arg arg = { 2126 .fp = fp, 2127 }; 2128 2129 hist_browser__show_callchain(browser, he, level, 0, 2130 hist_browser__fprintf_callchain_entry, &arg, 2131 hist_browser__check_dump_full); 2132 return arg.printed; 2133 } 2134 2135 static int hist_browser__fprintf_entry(struct hist_browser *browser, 2136 struct hist_entry *he, FILE *fp) 2137 { 2138 char s[8192]; 2139 int printed = 0; 2140 char folded_sign = ' '; 2141 struct perf_hpp hpp = { 2142 .buf = s, 2143 .size = sizeof(s), 2144 }; 2145 struct perf_hpp_fmt *fmt; 2146 bool first = true; 2147 int ret; 2148 2149 if (symbol_conf.use_callchain) { 2150 folded_sign = hist_entry__folded(he); 2151 printed += fprintf(fp, "%c ", folded_sign); 2152 } 2153 2154 hists__for_each_format(browser->hists, fmt) { 2155 if (perf_hpp__should_skip(fmt, he->hists)) 2156 continue; 2157 2158 if (!first) { 2159 ret = scnprintf(hpp.buf, hpp.size, " "); 2160 advance_hpp(&hpp, ret); 2161 } else 2162 first = false; 2163 2164 ret = fmt->entry(fmt, &hpp, he); 2165 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret); 2166 advance_hpp(&hpp, ret); 2167 } 2168 printed += fprintf(fp, "%s\n", s); 2169 2170 if (folded_sign == '-') 2171 printed += hist_browser__fprintf_callchain(browser, he, fp, 1); 2172 2173 return printed; 2174 } 2175 2176 2177 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, 2178 struct hist_entry *he, 2179 FILE *fp, int level) 2180 { 2181 char s[8192]; 2182 int printed = 0; 2183 char folded_sign = ' '; 2184 struct perf_hpp hpp = { 2185 .buf = s, 2186 .size = sizeof(s), 2187 }; 2188 struct perf_hpp_fmt *fmt; 2189 struct perf_hpp_list_node *fmt_node; 2190 bool first = true; 2191 int ret; 2192 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; 2193 2194 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, ""); 2195 2196 folded_sign = hist_entry__folded(he); 2197 printed += fprintf(fp, "%c", folded_sign); 2198 2199 /* the first hpp_list_node is for overhead columns */ 2200 fmt_node = list_first_entry(&he->hists->hpp_formats, 2201 struct perf_hpp_list_node, list); 2202 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 2203 if (!first) { 2204 ret = scnprintf(hpp.buf, hpp.size, " "); 2205 advance_hpp(&hpp, ret); 2206 } else 2207 first = false; 2208 2209 ret = fmt->entry(fmt, &hpp, he); 2210 advance_hpp(&hpp, ret); 2211 } 2212 2213 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, ""); 2214 advance_hpp(&hpp, ret); 2215 2216 perf_hpp_list__for_each_format(he->hpp_list, fmt) { 2217 ret = scnprintf(hpp.buf, hpp.size, " "); 2218 advance_hpp(&hpp, ret); 2219 2220 ret = fmt->entry(fmt, &hpp, he); 2221 advance_hpp(&hpp, ret); 2222 } 2223 2224 printed += fprintf(fp, "%s\n", rtrim(s)); 2225 2226 if (he->leaf && folded_sign == '-') { 2227 printed += hist_browser__fprintf_callchain(browser, he, fp, 2228 he->depth + 1); 2229 } 2230 2231 return printed; 2232 } 2233 2234 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 2235 { 2236 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 2237 browser->min_pcnt); 2238 int printed = 0; 2239 2240 while (nd) { 2241 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 2242 2243 if (symbol_conf.report_hierarchy) { 2244 printed += hist_browser__fprintf_hierarchy_entry(browser, 2245 h, fp, 2246 h->depth); 2247 } else { 2248 printed += hist_browser__fprintf_entry(browser, h, fp); 2249 } 2250 2251 nd = hists__filter_entries(rb_hierarchy_next(nd), 2252 browser->min_pcnt); 2253 } 2254 2255 return printed; 2256 } 2257 2258 static int hist_browser__dump(struct hist_browser *browser) 2259 { 2260 char filename[64]; 2261 FILE *fp; 2262 2263 while (1) { 2264 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 2265 if (access(filename, F_OK)) 2266 break; 2267 /* 2268 * XXX: Just an arbitrary lazy upper limit 2269 */ 2270 if (++browser->print_seq == 8192) { 2271 ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 2272 return -1; 2273 } 2274 } 2275 2276 fp = fopen(filename, "w"); 2277 if (fp == NULL) { 2278 char bf[64]; 2279 const char *err = str_error_r(errno, bf, sizeof(bf)); 2280 ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 2281 return -1; 2282 } 2283 2284 ++browser->print_seq; 2285 hist_browser__fprintf(browser, fp); 2286 fclose(fp); 2287 ui_helpline__fpush("%s written!", filename); 2288 2289 return 0; 2290 } 2291 2292 void hist_browser__init(struct hist_browser *browser, 2293 struct hists *hists) 2294 { 2295 struct perf_hpp_fmt *fmt; 2296 2297 browser->hists = hists; 2298 browser->b.refresh = hist_browser__refresh; 2299 browser->b.refresh_dimensions = hist_browser__refresh_dimensions; 2300 browser->b.seek = ui_browser__hists_seek; 2301 browser->b.use_navkeypressed = true; 2302 browser->show_headers = symbol_conf.show_hist_headers; 2303 2304 if (symbol_conf.report_hierarchy) { 2305 struct perf_hpp_list_node *fmt_node; 2306 2307 /* count overhead columns (in the first node) */ 2308 fmt_node = list_first_entry(&hists->hpp_formats, 2309 struct perf_hpp_list_node, list); 2310 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) 2311 ++browser->b.columns; 2312 2313 /* add a single column for whole hierarchy sort keys*/ 2314 ++browser->b.columns; 2315 } else { 2316 hists__for_each_format(hists, fmt) 2317 ++browser->b.columns; 2318 } 2319 2320 hists__reset_column_width(hists); 2321 } 2322 2323 struct hist_browser *hist_browser__new(struct hists *hists) 2324 { 2325 struct hist_browser *browser = zalloc(sizeof(*browser)); 2326 2327 if (browser) 2328 hist_browser__init(browser, hists); 2329 2330 return browser; 2331 } 2332 2333 static struct hist_browser * 2334 perf_evsel_browser__new(struct perf_evsel *evsel, 2335 struct hist_browser_timer *hbt, 2336 struct perf_env *env) 2337 { 2338 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel)); 2339 2340 if (browser) { 2341 browser->hbt = hbt; 2342 browser->env = env; 2343 browser->title = perf_evsel_browser_title; 2344 } 2345 return browser; 2346 } 2347 2348 void hist_browser__delete(struct hist_browser *browser) 2349 { 2350 free(browser); 2351 } 2352 2353 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 2354 { 2355 return browser->he_selection; 2356 } 2357 2358 static struct thread *hist_browser__selected_thread(struct hist_browser *browser) 2359 { 2360 return browser->he_selection->thread; 2361 } 2362 2363 /* Check whether the browser is for 'top' or 'report' */ 2364 static inline bool is_report_browser(void *timer) 2365 { 2366 return timer == NULL; 2367 } 2368 2369 static int perf_evsel_browser_title(struct hist_browser *browser, 2370 char *bf, size_t size) 2371 { 2372 struct hist_browser_timer *hbt = browser->hbt; 2373 struct hists *hists = browser->hists; 2374 char unit; 2375 int printed; 2376 const struct dso *dso = hists->dso_filter; 2377 const struct thread *thread = hists->thread_filter; 2378 int socket_id = hists->socket_filter; 2379 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2380 u64 nr_events = hists->stats.total_period; 2381 struct perf_evsel *evsel = hists_to_evsel(hists); 2382 const char *ev_name = perf_evsel__name(evsel); 2383 char buf[512]; 2384 size_t buflen = sizeof(buf); 2385 char ref[30] = " show reference callgraph, "; 2386 bool enable_ref = false; 2387 2388 if (symbol_conf.filter_relative) { 2389 nr_samples = hists->stats.nr_non_filtered_samples; 2390 nr_events = hists->stats.total_non_filtered_period; 2391 } 2392 2393 if (perf_evsel__is_group_event(evsel)) { 2394 struct perf_evsel *pos; 2395 2396 perf_evsel__group_desc(evsel, buf, buflen); 2397 ev_name = buf; 2398 2399 for_each_group_member(pos, evsel) { 2400 struct hists *pos_hists = evsel__hists(pos); 2401 2402 if (symbol_conf.filter_relative) { 2403 nr_samples += pos_hists->stats.nr_non_filtered_samples; 2404 nr_events += pos_hists->stats.total_non_filtered_period; 2405 } else { 2406 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2407 nr_events += pos_hists->stats.total_period; 2408 } 2409 } 2410 } 2411 2412 if (symbol_conf.show_ref_callgraph && 2413 strstr(ev_name, "call-graph=no")) 2414 enable_ref = true; 2415 nr_samples = convert_unit(nr_samples, &unit); 2416 printed = scnprintf(bf, size, 2417 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, 2418 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); 2419 2420 2421 if (hists->uid_filter_str) 2422 printed += snprintf(bf + printed, size - printed, 2423 ", UID: %s", hists->uid_filter_str); 2424 if (thread) { 2425 if (hists__has(hists, thread)) { 2426 printed += scnprintf(bf + printed, size - printed, 2427 ", Thread: %s(%d)", 2428 (thread->comm_set ? thread__comm_str(thread) : ""), 2429 thread->tid); 2430 } else { 2431 printed += scnprintf(bf + printed, size - printed, 2432 ", Thread: %s", 2433 (thread->comm_set ? thread__comm_str(thread) : "")); 2434 } 2435 } 2436 if (dso) 2437 printed += scnprintf(bf + printed, size - printed, 2438 ", DSO: %s", dso->short_name); 2439 if (socket_id > -1) 2440 printed += scnprintf(bf + printed, size - printed, 2441 ", Processor Socket: %d", socket_id); 2442 if (!is_report_browser(hbt)) { 2443 struct perf_top *top = hbt->arg; 2444 2445 if (top->zero) 2446 printed += scnprintf(bf + printed, size - printed, " [z]"); 2447 } 2448 2449 return printed; 2450 } 2451 2452 static inline void free_popup_options(char **options, int n) 2453 { 2454 int i; 2455 2456 for (i = 0; i < n; ++i) 2457 zfree(&options[i]); 2458 } 2459 2460 /* 2461 * Only runtime switching of perf data file will make "input_name" point 2462 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 2463 * whether we need to call free() for current "input_name" during the switch. 2464 */ 2465 static bool is_input_name_malloced = false; 2466 2467 static int switch_data_file(void) 2468 { 2469 char *pwd, *options[32], *abs_path[32], *tmp; 2470 DIR *pwd_dir; 2471 int nr_options = 0, choice = -1, ret = -1; 2472 struct dirent *dent; 2473 2474 pwd = getenv("PWD"); 2475 if (!pwd) 2476 return ret; 2477 2478 pwd_dir = opendir(pwd); 2479 if (!pwd_dir) 2480 return ret; 2481 2482 memset(options, 0, sizeof(options)); 2483 memset(abs_path, 0, sizeof(abs_path)); 2484 2485 while ((dent = readdir(pwd_dir))) { 2486 char path[PATH_MAX]; 2487 u64 magic; 2488 char *name = dent->d_name; 2489 FILE *file; 2490 2491 if (!(dent->d_type == DT_REG)) 2492 continue; 2493 2494 snprintf(path, sizeof(path), "%s/%s", pwd, name); 2495 2496 file = fopen(path, "r"); 2497 if (!file) 2498 continue; 2499 2500 if (fread(&magic, 1, 8, file) < 8) 2501 goto close_file_and_continue; 2502 2503 if (is_perf_magic(magic)) { 2504 options[nr_options] = strdup(name); 2505 if (!options[nr_options]) 2506 goto close_file_and_continue; 2507 2508 abs_path[nr_options] = strdup(path); 2509 if (!abs_path[nr_options]) { 2510 zfree(&options[nr_options]); 2511 ui__warning("Can't search all data files due to memory shortage.\n"); 2512 fclose(file); 2513 break; 2514 } 2515 2516 nr_options++; 2517 } 2518 2519 close_file_and_continue: 2520 fclose(file); 2521 if (nr_options >= 32) { 2522 ui__warning("Too many perf data files in PWD!\n" 2523 "Only the first 32 files will be listed.\n"); 2524 break; 2525 } 2526 } 2527 closedir(pwd_dir); 2528 2529 if (nr_options) { 2530 choice = ui__popup_menu(nr_options, options); 2531 if (choice < nr_options && choice >= 0) { 2532 tmp = strdup(abs_path[choice]); 2533 if (tmp) { 2534 if (is_input_name_malloced) 2535 free((void *)input_name); 2536 input_name = tmp; 2537 is_input_name_malloced = true; 2538 ret = 0; 2539 } else 2540 ui__warning("Data switch failed due to memory shortage!\n"); 2541 } 2542 } 2543 2544 free_popup_options(options, nr_options); 2545 free_popup_options(abs_path, nr_options); 2546 return ret; 2547 } 2548 2549 struct popup_action { 2550 struct thread *thread; 2551 struct map_symbol ms; 2552 int socket; 2553 2554 int (*fn)(struct hist_browser *browser, struct popup_action *act); 2555 }; 2556 2557 static int 2558 do_annotate(struct hist_browser *browser, struct popup_action *act) 2559 { 2560 struct perf_evsel *evsel; 2561 struct annotation *notes; 2562 struct hist_entry *he; 2563 int err; 2564 2565 if (!objdump_path && perf_env__lookup_objdump(browser->env)) 2566 return 0; 2567 2568 notes = symbol__annotation(act->ms.sym); 2569 if (!notes->src) 2570 return 0; 2571 2572 evsel = hists_to_evsel(browser->hists); 2573 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); 2574 he = hist_browser__selected_entry(browser); 2575 /* 2576 * offer option to annotate the other branch source or target 2577 * (if they exists) when returning from annotate 2578 */ 2579 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 2580 return 1; 2581 2582 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 2583 if (err) 2584 ui_browser__handle_resize(&browser->b); 2585 return 0; 2586 } 2587 2588 static int 2589 add_annotate_opt(struct hist_browser *browser __maybe_unused, 2590 struct popup_action *act, char **optstr, 2591 struct map *map, struct symbol *sym) 2592 { 2593 if (sym == NULL || map->dso->annotate_warned) 2594 return 0; 2595 2596 if (asprintf(optstr, "Annotate %s", sym->name) < 0) 2597 return 0; 2598 2599 act->ms.map = map; 2600 act->ms.sym = sym; 2601 act->fn = do_annotate; 2602 return 1; 2603 } 2604 2605 static int 2606 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 2607 { 2608 struct thread *thread = act->thread; 2609 2610 if ((!hists__has(browser->hists, thread) && 2611 !hists__has(browser->hists, comm)) || thread == NULL) 2612 return 0; 2613 2614 if (browser->hists->thread_filter) { 2615 pstack__remove(browser->pstack, &browser->hists->thread_filter); 2616 perf_hpp__set_elide(HISTC_THREAD, false); 2617 thread__zput(browser->hists->thread_filter); 2618 ui_helpline__pop(); 2619 } else { 2620 if (hists__has(browser->hists, thread)) { 2621 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 2622 thread->comm_set ? thread__comm_str(thread) : "", 2623 thread->tid); 2624 } else { 2625 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"", 2626 thread->comm_set ? thread__comm_str(thread) : ""); 2627 } 2628 2629 browser->hists->thread_filter = thread__get(thread); 2630 perf_hpp__set_elide(HISTC_THREAD, false); 2631 pstack__push(browser->pstack, &browser->hists->thread_filter); 2632 } 2633 2634 hists__filter_by_thread(browser->hists); 2635 hist_browser__reset(browser); 2636 return 0; 2637 } 2638 2639 static int 2640 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 2641 char **optstr, struct thread *thread) 2642 { 2643 int ret; 2644 2645 if ((!hists__has(browser->hists, thread) && 2646 !hists__has(browser->hists, comm)) || thread == NULL) 2647 return 0; 2648 2649 if (hists__has(browser->hists, thread)) { 2650 ret = asprintf(optstr, "Zoom %s %s(%d) thread", 2651 browser->hists->thread_filter ? "out of" : "into", 2652 thread->comm_set ? thread__comm_str(thread) : "", 2653 thread->tid); 2654 } else { 2655 ret = asprintf(optstr, "Zoom %s %s thread", 2656 browser->hists->thread_filter ? "out of" : "into", 2657 thread->comm_set ? thread__comm_str(thread) : ""); 2658 } 2659 if (ret < 0) 2660 return 0; 2661 2662 act->thread = thread; 2663 act->fn = do_zoom_thread; 2664 return 1; 2665 } 2666 2667 static int 2668 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 2669 { 2670 struct map *map = act->ms.map; 2671 2672 if (!hists__has(browser->hists, dso) || map == NULL) 2673 return 0; 2674 2675 if (browser->hists->dso_filter) { 2676 pstack__remove(browser->pstack, &browser->hists->dso_filter); 2677 perf_hpp__set_elide(HISTC_DSO, false); 2678 browser->hists->dso_filter = NULL; 2679 ui_helpline__pop(); 2680 } else { 2681 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", 2682 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name); 2683 browser->hists->dso_filter = map->dso; 2684 perf_hpp__set_elide(HISTC_DSO, true); 2685 pstack__push(browser->pstack, &browser->hists->dso_filter); 2686 } 2687 2688 hists__filter_by_dso(browser->hists); 2689 hist_browser__reset(browser); 2690 return 0; 2691 } 2692 2693 static int 2694 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 2695 char **optstr, struct map *map) 2696 { 2697 if (!hists__has(browser->hists, dso) || map == NULL) 2698 return 0; 2699 2700 if (asprintf(optstr, "Zoom %s %s DSO", 2701 browser->hists->dso_filter ? "out of" : "into", 2702 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) 2703 return 0; 2704 2705 act->ms.map = map; 2706 act->fn = do_zoom_dso; 2707 return 1; 2708 } 2709 2710 static int 2711 do_browse_map(struct hist_browser *browser __maybe_unused, 2712 struct popup_action *act) 2713 { 2714 map__browse(act->ms.map); 2715 return 0; 2716 } 2717 2718 static int 2719 add_map_opt(struct hist_browser *browser, 2720 struct popup_action *act, char **optstr, struct map *map) 2721 { 2722 if (!hists__has(browser->hists, dso) || map == NULL) 2723 return 0; 2724 2725 if (asprintf(optstr, "Browse map details") < 0) 2726 return 0; 2727 2728 act->ms.map = map; 2729 act->fn = do_browse_map; 2730 return 1; 2731 } 2732 2733 static int 2734 do_run_script(struct hist_browser *browser __maybe_unused, 2735 struct popup_action *act) 2736 { 2737 char script_opt[64]; 2738 memset(script_opt, 0, sizeof(script_opt)); 2739 2740 if (act->thread) { 2741 scnprintf(script_opt, sizeof(script_opt), " -c %s ", 2742 thread__comm_str(act->thread)); 2743 } else if (act->ms.sym) { 2744 scnprintf(script_opt, sizeof(script_opt), " -S %s ", 2745 act->ms.sym->name); 2746 } 2747 2748 script_browse(script_opt); 2749 return 0; 2750 } 2751 2752 static int 2753 add_script_opt(struct hist_browser *browser __maybe_unused, 2754 struct popup_action *act, char **optstr, 2755 struct thread *thread, struct symbol *sym) 2756 { 2757 if (thread) { 2758 if (asprintf(optstr, "Run scripts for samples of thread [%s]", 2759 thread__comm_str(thread)) < 0) 2760 return 0; 2761 } else if (sym) { 2762 if (asprintf(optstr, "Run scripts for samples of symbol [%s]", 2763 sym->name) < 0) 2764 return 0; 2765 } else { 2766 if (asprintf(optstr, "Run scripts for all samples") < 0) 2767 return 0; 2768 } 2769 2770 act->thread = thread; 2771 act->ms.sym = sym; 2772 act->fn = do_run_script; 2773 return 1; 2774 } 2775 2776 static int 2777 do_switch_data(struct hist_browser *browser __maybe_unused, 2778 struct popup_action *act __maybe_unused) 2779 { 2780 if (switch_data_file()) { 2781 ui__warning("Won't switch the data files due to\n" 2782 "no valid data file get selected!\n"); 2783 return 0; 2784 } 2785 2786 return K_SWITCH_INPUT_DATA; 2787 } 2788 2789 static int 2790 add_switch_opt(struct hist_browser *browser, 2791 struct popup_action *act, char **optstr) 2792 { 2793 if (!is_report_browser(browser->hbt)) 2794 return 0; 2795 2796 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 2797 return 0; 2798 2799 act->fn = do_switch_data; 2800 return 1; 2801 } 2802 2803 static int 2804 do_exit_browser(struct hist_browser *browser __maybe_unused, 2805 struct popup_action *act __maybe_unused) 2806 { 2807 return 0; 2808 } 2809 2810 static int 2811 add_exit_opt(struct hist_browser *browser __maybe_unused, 2812 struct popup_action *act, char **optstr) 2813 { 2814 if (asprintf(optstr, "Exit") < 0) 2815 return 0; 2816 2817 act->fn = do_exit_browser; 2818 return 1; 2819 } 2820 2821 static int 2822 do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 2823 { 2824 if (!hists__has(browser->hists, socket) || act->socket < 0) 2825 return 0; 2826 2827 if (browser->hists->socket_filter > -1) { 2828 pstack__remove(browser->pstack, &browser->hists->socket_filter); 2829 browser->hists->socket_filter = -1; 2830 perf_hpp__set_elide(HISTC_SOCKET, false); 2831 } else { 2832 browser->hists->socket_filter = act->socket; 2833 perf_hpp__set_elide(HISTC_SOCKET, true); 2834 pstack__push(browser->pstack, &browser->hists->socket_filter); 2835 } 2836 2837 hists__filter_by_socket(browser->hists); 2838 hist_browser__reset(browser); 2839 return 0; 2840 } 2841 2842 static int 2843 add_socket_opt(struct hist_browser *browser, struct popup_action *act, 2844 char **optstr, int socket_id) 2845 { 2846 if (!hists__has(browser->hists, socket) || socket_id < 0) 2847 return 0; 2848 2849 if (asprintf(optstr, "Zoom %s Processor Socket %d", 2850 (browser->hists->socket_filter > -1) ? "out of" : "into", 2851 socket_id) < 0) 2852 return 0; 2853 2854 act->socket = socket_id; 2855 act->fn = do_zoom_socket; 2856 return 1; 2857 } 2858 2859 static void hist_browser__update_nr_entries(struct hist_browser *hb) 2860 { 2861 u64 nr_entries = 0; 2862 struct rb_node *nd = rb_first(&hb->hists->entries); 2863 2864 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { 2865 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 2866 return; 2867 } 2868 2869 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2870 nr_entries++; 2871 nd = rb_hierarchy_next(nd); 2872 } 2873 2874 hb->nr_non_filtered_entries = nr_entries; 2875 hb->nr_hierarchy_entries = nr_entries; 2876 } 2877 2878 static void hist_browser__update_percent_limit(struct hist_browser *hb, 2879 double percent) 2880 { 2881 struct hist_entry *he; 2882 struct rb_node *nd = rb_first(&hb->hists->entries); 2883 u64 total = hists__total_period(hb->hists); 2884 u64 min_callchain_hits = total * (percent / 100); 2885 2886 hb->min_pcnt = callchain_param.min_percent = percent; 2887 2888 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 2889 he = rb_entry(nd, struct hist_entry, rb_node); 2890 2891 if (he->has_no_entry) { 2892 he->has_no_entry = false; 2893 he->nr_rows = 0; 2894 } 2895 2896 if (!he->leaf || !symbol_conf.use_callchain) 2897 goto next; 2898 2899 if (callchain_param.mode == CHAIN_GRAPH_REL) { 2900 total = he->stat.period; 2901 2902 if (symbol_conf.cumulate_callchain) 2903 total = he->stat_acc->period; 2904 2905 min_callchain_hits = total * (percent / 100); 2906 } 2907 2908 callchain_param.sort(&he->sorted_chain, he->callchain, 2909 min_callchain_hits, &callchain_param); 2910 2911 next: 2912 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); 2913 2914 /* force to re-evaluate folding state of callchains */ 2915 he->init_have_children = false; 2916 hist_entry__set_folding(he, hb, false); 2917 } 2918 } 2919 2920 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 2921 const char *helpline, 2922 bool left_exits, 2923 struct hist_browser_timer *hbt, 2924 float min_pcnt, 2925 struct perf_env *env) 2926 { 2927 struct hists *hists = evsel__hists(evsel); 2928 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env); 2929 struct branch_info *bi; 2930 #define MAX_OPTIONS 16 2931 char *options[MAX_OPTIONS]; 2932 struct popup_action actions[MAX_OPTIONS]; 2933 int nr_options = 0; 2934 int key = -1; 2935 char buf[64]; 2936 int delay_secs = hbt ? hbt->refresh : 0; 2937 2938 #define HIST_BROWSER_HELP_COMMON \ 2939 "h/?/F1 Show this window\n" \ 2940 "UP/DOWN/PGUP\n" \ 2941 "PGDN/SPACE Navigate\n" \ 2942 "q/ESC/CTRL+C Exit browser\n\n" \ 2943 "For multiple event sessions:\n\n" \ 2944 "TAB/UNTAB Switch events\n\n" \ 2945 "For symbolic views (--sort has sym):\n\n" \ 2946 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ 2947 "ESC Zoom out\n" \ 2948 "a Annotate current symbol\n" \ 2949 "C Collapse all callchains\n" \ 2950 "d Zoom into current DSO\n" \ 2951 "E Expand all callchains\n" \ 2952 "F Toggle percentage of filtered entries\n" \ 2953 "H Display column headers\n" \ 2954 "L Change percent limit\n" \ 2955 "m Display context menu\n" \ 2956 "S Zoom into current Processor Socket\n" \ 2957 2958 /* help messages are sorted by lexical order of the hotkey */ 2959 const char report_help[] = HIST_BROWSER_HELP_COMMON 2960 "i Show header information\n" 2961 "P Print histograms to perf.hist.N\n" 2962 "r Run available scripts\n" 2963 "s Switch to another data file in PWD\n" 2964 "t Zoom into current Thread\n" 2965 "V Verbose (DSO names in callchains, etc)\n" 2966 "/ Filter symbol by name"; 2967 const char top_help[] = HIST_BROWSER_HELP_COMMON 2968 "P Print histograms to perf.hist.N\n" 2969 "t Zoom into current Thread\n" 2970 "V Verbose (DSO names in callchains, etc)\n" 2971 "z Toggle zeroing of samples\n" 2972 "f Enable/Disable events\n" 2973 "/ Filter symbol by name"; 2974 2975 if (browser == NULL) 2976 return -1; 2977 2978 /* reset abort key so that it can get Ctrl-C as a key */ 2979 SLang_reset_tty(); 2980 SLang_init_tty(0, 0, 0); 2981 2982 if (min_pcnt) 2983 browser->min_pcnt = min_pcnt; 2984 hist_browser__update_nr_entries(browser); 2985 2986 browser->pstack = pstack__new(3); 2987 if (browser->pstack == NULL) 2988 goto out; 2989 2990 ui_helpline__push(helpline); 2991 2992 memset(options, 0, sizeof(options)); 2993 memset(actions, 0, sizeof(actions)); 2994 2995 if (symbol_conf.col_width_list_str) 2996 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 2997 2998 while (1) { 2999 struct thread *thread = NULL; 3000 struct map *map = NULL; 3001 int choice = 0; 3002 int socked_id = -1; 3003 3004 nr_options = 0; 3005 3006 key = hist_browser__run(browser, helpline); 3007 3008 if (browser->he_selection != NULL) { 3009 thread = hist_browser__selected_thread(browser); 3010 map = browser->selection->map; 3011 socked_id = browser->he_selection->socket; 3012 } 3013 switch (key) { 3014 case K_TAB: 3015 case K_UNTAB: 3016 if (nr_events == 1) 3017 continue; 3018 /* 3019 * Exit the browser, let hists__browser_tree 3020 * go to the next or previous 3021 */ 3022 goto out_free_stack; 3023 case 'a': 3024 if (!hists__has(hists, sym)) { 3025 ui_browser__warning(&browser->b, delay_secs * 2, 3026 "Annotation is only available for symbolic views, " 3027 "include \"sym*\" in --sort to use it."); 3028 continue; 3029 } 3030 3031 if (browser->selection == NULL || 3032 browser->selection->sym == NULL || 3033 browser->selection->map->dso->annotate_warned) 3034 continue; 3035 3036 actions->ms.map = browser->selection->map; 3037 actions->ms.sym = browser->selection->sym; 3038 do_annotate(browser, actions); 3039 continue; 3040 case 'P': 3041 hist_browser__dump(browser); 3042 continue; 3043 case 'd': 3044 actions->ms.map = map; 3045 do_zoom_dso(browser, actions); 3046 continue; 3047 case 'V': 3048 verbose = (verbose + 1) % 4; 3049 browser->show_dso = verbose > 0; 3050 ui_helpline__fpush("Verbosity level set to %d\n", 3051 verbose); 3052 continue; 3053 case 't': 3054 actions->thread = thread; 3055 do_zoom_thread(browser, actions); 3056 continue; 3057 case 'S': 3058 actions->socket = socked_id; 3059 do_zoom_socket(browser, actions); 3060 continue; 3061 case '/': 3062 if (ui_browser__input_window("Symbol to show", 3063 "Please enter the name of symbol you want to see.\n" 3064 "To remove the filter later, press / + ENTER.", 3065 buf, "ENTER: OK, ESC: Cancel", 3066 delay_secs * 2) == K_ENTER) { 3067 hists->symbol_filter_str = *buf ? buf : NULL; 3068 hists__filter_by_symbol(hists); 3069 hist_browser__reset(browser); 3070 } 3071 continue; 3072 case 'r': 3073 if (is_report_browser(hbt)) { 3074 actions->thread = NULL; 3075 actions->ms.sym = NULL; 3076 do_run_script(browser, actions); 3077 } 3078 continue; 3079 case 's': 3080 if (is_report_browser(hbt)) { 3081 key = do_switch_data(browser, actions); 3082 if (key == K_SWITCH_INPUT_DATA) 3083 goto out_free_stack; 3084 } 3085 continue; 3086 case 'i': 3087 /* env->arch is NULL for live-mode (i.e. perf top) */ 3088 if (env->arch) 3089 tui__header_window(env); 3090 continue; 3091 case 'F': 3092 symbol_conf.filter_relative ^= 1; 3093 continue; 3094 case 'z': 3095 if (!is_report_browser(hbt)) { 3096 struct perf_top *top = hbt->arg; 3097 3098 top->zero = !top->zero; 3099 } 3100 continue; 3101 case 'L': 3102 if (ui_browser__input_window("Percent Limit", 3103 "Please enter the value you want to hide entries under that percent.", 3104 buf, "ENTER: OK, ESC: Cancel", 3105 delay_secs * 2) == K_ENTER) { 3106 char *end; 3107 double new_percent = strtod(buf, &end); 3108 3109 if (new_percent < 0 || new_percent > 100) { 3110 ui_browser__warning(&browser->b, delay_secs * 2, 3111 "Invalid percent: %.2f", new_percent); 3112 continue; 3113 } 3114 3115 hist_browser__update_percent_limit(browser, new_percent); 3116 hist_browser__reset(browser); 3117 } 3118 continue; 3119 case K_F1: 3120 case 'h': 3121 case '?': 3122 ui_browser__help_window(&browser->b, 3123 is_report_browser(hbt) ? report_help : top_help); 3124 continue; 3125 case K_ENTER: 3126 case K_RIGHT: 3127 case 'm': 3128 /* menu */ 3129 break; 3130 case K_ESC: 3131 case K_LEFT: { 3132 const void *top; 3133 3134 if (pstack__empty(browser->pstack)) { 3135 /* 3136 * Go back to the perf_evsel_menu__run or other user 3137 */ 3138 if (left_exits) 3139 goto out_free_stack; 3140 3141 if (key == K_ESC && 3142 ui_browser__dialog_yesno(&browser->b, 3143 "Do you really want to exit?")) 3144 goto out_free_stack; 3145 3146 continue; 3147 } 3148 top = pstack__peek(browser->pstack); 3149 if (top == &browser->hists->dso_filter) { 3150 /* 3151 * No need to set actions->dso here since 3152 * it's just to remove the current filter. 3153 * Ditto for thread below. 3154 */ 3155 do_zoom_dso(browser, actions); 3156 } else if (top == &browser->hists->thread_filter) { 3157 do_zoom_thread(browser, actions); 3158 } else if (top == &browser->hists->socket_filter) { 3159 do_zoom_socket(browser, actions); 3160 } 3161 continue; 3162 } 3163 case 'q': 3164 case CTRL('c'): 3165 goto out_free_stack; 3166 case 'f': 3167 if (!is_report_browser(hbt)) { 3168 struct perf_top *top = hbt->arg; 3169 3170 perf_evlist__toggle_enable(top->evlist); 3171 /* 3172 * No need to refresh, resort/decay histogram 3173 * entries if we are not collecting samples: 3174 */ 3175 if (top->evlist->enabled) { 3176 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 3177 hbt->refresh = delay_secs; 3178 } else { 3179 helpline = "Press 'f' again to re-enable the events"; 3180 hbt->refresh = 0; 3181 } 3182 continue; 3183 } 3184 /* Fall thru */ 3185 default: 3186 helpline = "Press '?' for help on key bindings"; 3187 continue; 3188 } 3189 3190 if (!hists__has(hists, sym) || browser->selection == NULL) 3191 goto skip_annotation; 3192 3193 if (sort__mode == SORT_MODE__BRANCH) { 3194 bi = browser->he_selection->branch_info; 3195 3196 if (bi == NULL) 3197 goto skip_annotation; 3198 3199 nr_options += add_annotate_opt(browser, 3200 &actions[nr_options], 3201 &options[nr_options], 3202 bi->from.map, 3203 bi->from.sym); 3204 if (bi->to.sym != bi->from.sym) 3205 nr_options += add_annotate_opt(browser, 3206 &actions[nr_options], 3207 &options[nr_options], 3208 bi->to.map, 3209 bi->to.sym); 3210 } else { 3211 nr_options += add_annotate_opt(browser, 3212 &actions[nr_options], 3213 &options[nr_options], 3214 browser->selection->map, 3215 browser->selection->sym); 3216 } 3217 skip_annotation: 3218 nr_options += add_thread_opt(browser, &actions[nr_options], 3219 &options[nr_options], thread); 3220 nr_options += add_dso_opt(browser, &actions[nr_options], 3221 &options[nr_options], map); 3222 nr_options += add_map_opt(browser, &actions[nr_options], 3223 &options[nr_options], 3224 browser->selection ? 3225 browser->selection->map : NULL); 3226 nr_options += add_socket_opt(browser, &actions[nr_options], 3227 &options[nr_options], 3228 socked_id); 3229 /* perf script support */ 3230 if (!is_report_browser(hbt)) 3231 goto skip_scripting; 3232 3233 if (browser->he_selection) { 3234 if (hists__has(hists, thread) && thread) { 3235 nr_options += add_script_opt(browser, 3236 &actions[nr_options], 3237 &options[nr_options], 3238 thread, NULL); 3239 } 3240 /* 3241 * Note that browser->selection != NULL 3242 * when browser->he_selection is not NULL, 3243 * so we don't need to check browser->selection 3244 * before fetching browser->selection->sym like what 3245 * we do before fetching browser->selection->map. 3246 * 3247 * See hist_browser__show_entry. 3248 */ 3249 if (hists__has(hists, sym) && browser->selection->sym) { 3250 nr_options += add_script_opt(browser, 3251 &actions[nr_options], 3252 &options[nr_options], 3253 NULL, browser->selection->sym); 3254 } 3255 } 3256 nr_options += add_script_opt(browser, &actions[nr_options], 3257 &options[nr_options], NULL, NULL); 3258 nr_options += add_switch_opt(browser, &actions[nr_options], 3259 &options[nr_options]); 3260 skip_scripting: 3261 nr_options += add_exit_opt(browser, &actions[nr_options], 3262 &options[nr_options]); 3263 3264 do { 3265 struct popup_action *act; 3266 3267 choice = ui__popup_menu(nr_options, options); 3268 if (choice == -1 || choice >= nr_options) 3269 break; 3270 3271 act = &actions[choice]; 3272 key = act->fn(browser, act); 3273 } while (key == 1); 3274 3275 if (key == K_SWITCH_INPUT_DATA) 3276 break; 3277 } 3278 out_free_stack: 3279 pstack__delete(browser->pstack); 3280 out: 3281 hist_browser__delete(browser); 3282 free_popup_options(options, MAX_OPTIONS); 3283 return key; 3284 } 3285 3286 struct perf_evsel_menu { 3287 struct ui_browser b; 3288 struct perf_evsel *selection; 3289 bool lost_events, lost_events_warned; 3290 float min_pcnt; 3291 struct perf_env *env; 3292 }; 3293 3294 static void perf_evsel_menu__write(struct ui_browser *browser, 3295 void *entry, int row) 3296 { 3297 struct perf_evsel_menu *menu = container_of(browser, 3298 struct perf_evsel_menu, b); 3299 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 3300 struct hists *hists = evsel__hists(evsel); 3301 bool current_entry = ui_browser__is_current_entry(browser, row); 3302 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 3303 const char *ev_name = perf_evsel__name(evsel); 3304 char bf[256], unit; 3305 const char *warn = " "; 3306 size_t printed; 3307 3308 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 3309 HE_COLORSET_NORMAL); 3310 3311 if (perf_evsel__is_group_event(evsel)) { 3312 struct perf_evsel *pos; 3313 3314 ev_name = perf_evsel__group_name(evsel); 3315 3316 for_each_group_member(pos, evsel) { 3317 struct hists *pos_hists = evsel__hists(pos); 3318 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 3319 } 3320 } 3321 3322 nr_events = convert_unit(nr_events, &unit); 3323 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 3324 unit, unit == ' ' ? "" : " ", ev_name); 3325 ui_browser__printf(browser, "%s", bf); 3326 3327 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 3328 if (nr_events != 0) { 3329 menu->lost_events = true; 3330 if (!current_entry) 3331 ui_browser__set_color(browser, HE_COLORSET_TOP); 3332 nr_events = convert_unit(nr_events, &unit); 3333 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 3334 nr_events, unit, unit == ' ' ? "" : " "); 3335 warn = bf; 3336 } 3337 3338 ui_browser__write_nstring(browser, warn, browser->width - printed); 3339 3340 if (current_entry) 3341 menu->selection = evsel; 3342 } 3343 3344 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 3345 int nr_events, const char *help, 3346 struct hist_browser_timer *hbt) 3347 { 3348 struct perf_evlist *evlist = menu->b.priv; 3349 struct perf_evsel *pos; 3350 const char *title = "Available samples"; 3351 int delay_secs = hbt ? hbt->refresh : 0; 3352 int key; 3353 3354 if (ui_browser__show(&menu->b, title, 3355 "ESC: exit, ENTER|->: Browse histograms") < 0) 3356 return -1; 3357 3358 while (1) { 3359 key = ui_browser__run(&menu->b, delay_secs); 3360 3361 switch (key) { 3362 case K_TIMER: 3363 hbt->timer(hbt->arg); 3364 3365 if (!menu->lost_events_warned && menu->lost_events) { 3366 ui_browser__warn_lost_events(&menu->b); 3367 menu->lost_events_warned = true; 3368 } 3369 continue; 3370 case K_RIGHT: 3371 case K_ENTER: 3372 if (!menu->selection) 3373 continue; 3374 pos = menu->selection; 3375 browse_hists: 3376 perf_evlist__set_selected(evlist, pos); 3377 /* 3378 * Give the calling tool a chance to populate the non 3379 * default evsel resorted hists tree. 3380 */ 3381 if (hbt) 3382 hbt->timer(hbt->arg); 3383 key = perf_evsel__hists_browse(pos, nr_events, help, 3384 true, hbt, 3385 menu->min_pcnt, 3386 menu->env); 3387 ui_browser__show_title(&menu->b, title); 3388 switch (key) { 3389 case K_TAB: 3390 if (pos->node.next == &evlist->entries) 3391 pos = perf_evlist__first(evlist); 3392 else 3393 pos = perf_evsel__next(pos); 3394 goto browse_hists; 3395 case K_UNTAB: 3396 if (pos->node.prev == &evlist->entries) 3397 pos = perf_evlist__last(evlist); 3398 else 3399 pos = perf_evsel__prev(pos); 3400 goto browse_hists; 3401 case K_SWITCH_INPUT_DATA: 3402 case 'q': 3403 case CTRL('c'): 3404 goto out; 3405 case K_ESC: 3406 default: 3407 continue; 3408 } 3409 case K_LEFT: 3410 continue; 3411 case K_ESC: 3412 if (!ui_browser__dialog_yesno(&menu->b, 3413 "Do you really want to exit?")) 3414 continue; 3415 /* Fall thru */ 3416 case 'q': 3417 case CTRL('c'): 3418 goto out; 3419 default: 3420 continue; 3421 } 3422 } 3423 3424 out: 3425 ui_browser__hide(&menu->b); 3426 return key; 3427 } 3428 3429 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 3430 void *entry) 3431 { 3432 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 3433 3434 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 3435 return true; 3436 3437 return false; 3438 } 3439 3440 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 3441 int nr_entries, const char *help, 3442 struct hist_browser_timer *hbt, 3443 float min_pcnt, 3444 struct perf_env *env) 3445 { 3446 struct perf_evsel *pos; 3447 struct perf_evsel_menu menu = { 3448 .b = { 3449 .entries = &evlist->entries, 3450 .refresh = ui_browser__list_head_refresh, 3451 .seek = ui_browser__list_head_seek, 3452 .write = perf_evsel_menu__write, 3453 .filter = filter_group_entries, 3454 .nr_entries = nr_entries, 3455 .priv = evlist, 3456 }, 3457 .min_pcnt = min_pcnt, 3458 .env = env, 3459 }; 3460 3461 ui_helpline__push("Press ESC to exit"); 3462 3463 evlist__for_each_entry(evlist, pos) { 3464 const char *ev_name = perf_evsel__name(pos); 3465 size_t line_len = strlen(ev_name) + 7; 3466 3467 if (menu.b.width < line_len) 3468 menu.b.width = line_len; 3469 } 3470 3471 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 3472 } 3473 3474 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 3475 struct hist_browser_timer *hbt, 3476 float min_pcnt, 3477 struct perf_env *env) 3478 { 3479 int nr_entries = evlist->nr_entries; 3480 3481 single_entry: 3482 if (nr_entries == 1) { 3483 struct perf_evsel *first = perf_evlist__first(evlist); 3484 3485 return perf_evsel__hists_browse(first, nr_entries, help, 3486 false, hbt, min_pcnt, 3487 env); 3488 } 3489 3490 if (symbol_conf.event_group) { 3491 struct perf_evsel *pos; 3492 3493 nr_entries = 0; 3494 evlist__for_each_entry(evlist, pos) { 3495 if (perf_evsel__is_group_leader(pos)) 3496 nr_entries++; 3497 } 3498 3499 if (nr_entries == 1) 3500 goto single_entry; 3501 } 3502 3503 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 3504 hbt, min_pcnt, env); 3505 } 3506