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