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