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