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