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