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