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 int hist_browser__show_callchain_flat(struct hist_browser *browser, 661 struct rb_root *root, 662 unsigned short row, u64 total, 663 print_callchain_entry_fn print, 664 struct callchain_print_arg *arg, 665 check_output_full_fn is_output_full) 666 { 667 struct rb_node *node; 668 int first_row = row, offset = LEVEL_OFFSET_STEP; 669 bool need_percent; 670 671 node = rb_first(root); 672 need_percent = node && rb_next(node); 673 674 while (node) { 675 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 676 struct rb_node *next = rb_next(node); 677 struct callchain_list *chain; 678 char folded_sign = ' '; 679 int first = true; 680 int extra_offset = 0; 681 682 list_for_each_entry(chain, &child->parent_val, list) { 683 bool was_first = first; 684 685 if (first) 686 first = false; 687 else if (need_percent) 688 extra_offset = LEVEL_OFFSET_STEP; 689 690 folded_sign = callchain_list__folded(chain); 691 692 row += hist_browser__show_callchain_list(browser, child, 693 chain, row, total, 694 was_first && need_percent, 695 offset + extra_offset, 696 print, arg); 697 698 if (is_output_full(browser, row)) 699 goto out; 700 701 if (folded_sign == '+') 702 goto next; 703 } 704 705 list_for_each_entry(chain, &child->val, list) { 706 bool was_first = first; 707 708 if (first) 709 first = false; 710 else if (need_percent) 711 extra_offset = LEVEL_OFFSET_STEP; 712 713 folded_sign = callchain_list__folded(chain); 714 715 row += hist_browser__show_callchain_list(browser, child, 716 chain, row, total, 717 was_first && need_percent, 718 offset + extra_offset, 719 print, arg); 720 721 if (is_output_full(browser, row)) 722 goto out; 723 724 if (folded_sign == '+') 725 break; 726 } 727 728 next: 729 if (is_output_full(browser, row)) 730 break; 731 node = next; 732 } 733 out: 734 return row - first_row; 735 } 736 737 static char *hist_browser__folded_callchain_str(struct hist_browser *browser, 738 struct callchain_list *chain, 739 char *value_str, char *old_str) 740 { 741 char bf[1024]; 742 const char *str; 743 char *new; 744 745 str = callchain_list__sym_name(chain, bf, sizeof(bf), 746 browser->show_dso); 747 if (old_str) { 748 if (asprintf(&new, "%s%s%s", old_str, 749 symbol_conf.field_sep ?: ";", str) < 0) 750 new = NULL; 751 } else { 752 if (value_str) { 753 if (asprintf(&new, "%s %s", value_str, str) < 0) 754 new = NULL; 755 } else { 756 if (asprintf(&new, "%s", str) < 0) 757 new = NULL; 758 } 759 } 760 return new; 761 } 762 763 static int hist_browser__show_callchain_folded(struct hist_browser *browser, 764 struct rb_root *root, 765 unsigned short row, u64 total, 766 print_callchain_entry_fn print, 767 struct callchain_print_arg *arg, 768 check_output_full_fn is_output_full) 769 { 770 struct rb_node *node; 771 int first_row = row, offset = LEVEL_OFFSET_STEP; 772 bool need_percent; 773 774 node = rb_first(root); 775 need_percent = node && rb_next(node); 776 777 while (node) { 778 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 779 struct rb_node *next = rb_next(node); 780 struct callchain_list *chain, *first_chain = NULL; 781 int first = true; 782 char *value_str = NULL, *value_str_alloc = NULL; 783 char *chain_str = NULL, *chain_str_alloc = NULL; 784 785 if (arg->row_offset != 0) { 786 arg->row_offset--; 787 goto next; 788 } 789 790 if (need_percent) { 791 char buf[64]; 792 793 callchain_node__scnprintf_value(child, buf, sizeof(buf), total); 794 if (asprintf(&value_str, "%s", buf) < 0) { 795 value_str = (char *)"<...>"; 796 goto do_print; 797 } 798 value_str_alloc = value_str; 799 } 800 801 list_for_each_entry(chain, &child->parent_val, list) { 802 chain_str = hist_browser__folded_callchain_str(browser, 803 chain, value_str, chain_str); 804 if (first) { 805 first = false; 806 first_chain = chain; 807 } 808 809 if (chain_str == NULL) { 810 chain_str = (char *)"Not enough memory!"; 811 goto do_print; 812 } 813 814 chain_str_alloc = chain_str; 815 } 816 817 list_for_each_entry(chain, &child->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 do_print: 834 print(browser, first_chain, chain_str, offset, row++, arg); 835 free(value_str_alloc); 836 free(chain_str_alloc); 837 838 next: 839 if (is_output_full(browser, row)) 840 break; 841 node = next; 842 } 843 844 return row - first_row; 845 } 846 847 static int hist_browser__show_callchain(struct hist_browser *browser, 848 struct rb_root *root, int level, 849 unsigned short row, u64 total, 850 print_callchain_entry_fn print, 851 struct callchain_print_arg *arg, 852 check_output_full_fn is_output_full) 853 { 854 struct rb_node *node; 855 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 856 u64 new_total; 857 bool need_percent; 858 859 node = rb_first(root); 860 need_percent = node && rb_next(node); 861 862 while (node) { 863 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 864 struct rb_node *next = rb_next(node); 865 struct callchain_list *chain; 866 char folded_sign = ' '; 867 int first = true; 868 int extra_offset = 0; 869 870 list_for_each_entry(chain, &child->val, list) { 871 bool was_first = first; 872 873 if (first) 874 first = false; 875 else if (need_percent) 876 extra_offset = LEVEL_OFFSET_STEP; 877 878 folded_sign = callchain_list__folded(chain); 879 880 row += hist_browser__show_callchain_list(browser, child, 881 chain, row, total, 882 was_first && need_percent, 883 offset + extra_offset, 884 print, arg); 885 886 if (is_output_full(browser, row)) 887 goto out; 888 889 if (folded_sign == '+') 890 break; 891 } 892 893 if (folded_sign == '-') { 894 const int new_level = level + (extra_offset ? 2 : 1); 895 896 if (callchain_param.mode == CHAIN_GRAPH_REL) 897 new_total = child->children_hit; 898 else 899 new_total = total; 900 901 row += hist_browser__show_callchain(browser, &child->rb_root, 902 new_level, row, new_total, 903 print, arg, is_output_full); 904 } 905 if (is_output_full(browser, row)) 906 break; 907 node = next; 908 } 909 out: 910 return row - first_row; 911 } 912 913 struct hpp_arg { 914 struct ui_browser *b; 915 char folded_sign; 916 bool current_entry; 917 }; 918 919 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 920 { 921 struct hpp_arg *arg = hpp->ptr; 922 int ret, len; 923 va_list args; 924 double percent; 925 926 va_start(args, fmt); 927 len = va_arg(args, int); 928 percent = va_arg(args, double); 929 va_end(args); 930 931 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 932 933 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 934 ui_browser__printf(arg->b, "%s", hpp->buf); 935 936 advance_hpp(hpp, ret); 937 return ret; 938 } 939 940 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 941 static u64 __hpp_get_##_field(struct hist_entry *he) \ 942 { \ 943 return he->stat._field; \ 944 } \ 945 \ 946 static int \ 947 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 948 struct perf_hpp *hpp, \ 949 struct hist_entry *he) \ 950 { \ 951 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 952 __hpp__slsmg_color_printf, true); \ 953 } 954 955 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 956 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 957 { \ 958 return he->stat_acc->_field; \ 959 } \ 960 \ 961 static int \ 962 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 963 struct perf_hpp *hpp, \ 964 struct hist_entry *he) \ 965 { \ 966 if (!symbol_conf.cumulate_callchain) { \ 967 struct hpp_arg *arg = hpp->ptr; \ 968 int len = fmt->user_len ?: fmt->len; \ 969 int ret = scnprintf(hpp->buf, hpp->size, \ 970 "%*s", len, "N/A"); \ 971 ui_browser__printf(arg->b, "%s", hpp->buf); \ 972 \ 973 return ret; \ 974 } \ 975 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ 976 " %*.2f%%", __hpp__slsmg_color_printf, true); \ 977 } 978 979 __HPP_COLOR_PERCENT_FN(overhead, period) 980 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 981 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 982 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 983 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 984 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 985 986 #undef __HPP_COLOR_PERCENT_FN 987 #undef __HPP_COLOR_ACC_PERCENT_FN 988 989 void hist_browser__init_hpp(void) 990 { 991 perf_hpp__format[PERF_HPP__OVERHEAD].color = 992 hist_browser__hpp_color_overhead; 993 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 994 hist_browser__hpp_color_overhead_sys; 995 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 996 hist_browser__hpp_color_overhead_us; 997 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 998 hist_browser__hpp_color_overhead_guest_sys; 999 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 1000 hist_browser__hpp_color_overhead_guest_us; 1001 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 1002 hist_browser__hpp_color_overhead_acc; 1003 } 1004 1005 static int hist_browser__show_entry(struct hist_browser *browser, 1006 struct hist_entry *entry, 1007 unsigned short row) 1008 { 1009 char s[256]; 1010 int printed = 0; 1011 int width = browser->b.width; 1012 char folded_sign = ' '; 1013 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 1014 off_t row_offset = entry->row_offset; 1015 bool first = true; 1016 struct perf_hpp_fmt *fmt; 1017 1018 if (current_entry) { 1019 browser->he_selection = entry; 1020 browser->selection = &entry->ms; 1021 } 1022 1023 if (symbol_conf.use_callchain) { 1024 hist_entry__init_have_children(entry); 1025 folded_sign = hist_entry__folded(entry); 1026 } 1027 1028 if (row_offset == 0) { 1029 struct hpp_arg arg = { 1030 .b = &browser->b, 1031 .folded_sign = folded_sign, 1032 .current_entry = current_entry, 1033 }; 1034 struct perf_hpp hpp = { 1035 .buf = s, 1036 .size = sizeof(s), 1037 .ptr = &arg, 1038 }; 1039 int column = 0; 1040 1041 hist_browser__gotorc(browser, row, 0); 1042 1043 perf_hpp__for_each_format(fmt) { 1044 if (perf_hpp__should_skip(fmt, entry->hists) || 1045 column++ < browser->b.horiz_scroll) 1046 continue; 1047 1048 if (current_entry && browser->b.navkeypressed) { 1049 ui_browser__set_color(&browser->b, 1050 HE_COLORSET_SELECTED); 1051 } else { 1052 ui_browser__set_color(&browser->b, 1053 HE_COLORSET_NORMAL); 1054 } 1055 1056 if (first) { 1057 if (symbol_conf.use_callchain) { 1058 ui_browser__printf(&browser->b, "%c ", folded_sign); 1059 width -= 2; 1060 } 1061 first = false; 1062 } else { 1063 ui_browser__printf(&browser->b, " "); 1064 width -= 2; 1065 } 1066 1067 if (fmt->color) { 1068 width -= fmt->color(fmt, &hpp, entry); 1069 } else { 1070 width -= fmt->entry(fmt, &hpp, entry); 1071 ui_browser__printf(&browser->b, "%s", s); 1072 } 1073 } 1074 1075 /* The scroll bar isn't being used */ 1076 if (!browser->b.navkeypressed) 1077 width += 1; 1078 1079 ui_browser__write_nstring(&browser->b, "", width); 1080 1081 ++row; 1082 ++printed; 1083 } else 1084 --row_offset; 1085 1086 if (folded_sign == '-' && row != browser->b.rows) { 1087 u64 total = hists__total_period(entry->hists); 1088 struct callchain_print_arg arg = { 1089 .row_offset = row_offset, 1090 .is_current_entry = current_entry, 1091 }; 1092 1093 if (callchain_param.mode == CHAIN_GRAPH_REL) { 1094 if (symbol_conf.cumulate_callchain) 1095 total = entry->stat_acc->period; 1096 else 1097 total = entry->stat.period; 1098 } 1099 1100 if (callchain_param.mode == CHAIN_FLAT) { 1101 printed += hist_browser__show_callchain_flat(browser, 1102 &entry->sorted_chain, row, total, 1103 hist_browser__show_callchain_entry, &arg, 1104 hist_browser__check_output_full); 1105 } else if (callchain_param.mode == CHAIN_FOLDED) { 1106 printed += hist_browser__show_callchain_folded(browser, 1107 &entry->sorted_chain, row, total, 1108 hist_browser__show_callchain_entry, &arg, 1109 hist_browser__check_output_full); 1110 } else { 1111 printed += hist_browser__show_callchain(browser, 1112 &entry->sorted_chain, 1, row, total, 1113 hist_browser__show_callchain_entry, &arg, 1114 hist_browser__check_output_full); 1115 } 1116 1117 if (arg.is_current_entry) 1118 browser->he_selection = entry; 1119 } 1120 1121 return printed; 1122 } 1123 1124 static int advance_hpp_check(struct perf_hpp *hpp, int inc) 1125 { 1126 advance_hpp(hpp, inc); 1127 return hpp->size <= 0; 1128 } 1129 1130 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size) 1131 { 1132 struct hists *hists = browser->hists; 1133 struct perf_hpp dummy_hpp = { 1134 .buf = buf, 1135 .size = size, 1136 }; 1137 struct perf_hpp_fmt *fmt; 1138 size_t ret = 0; 1139 int column = 0; 1140 1141 if (symbol_conf.use_callchain) { 1142 ret = scnprintf(buf, size, " "); 1143 if (advance_hpp_check(&dummy_hpp, ret)) 1144 return ret; 1145 } 1146 1147 perf_hpp__for_each_format(fmt) { 1148 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) 1149 continue; 1150 1151 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); 1152 if (advance_hpp_check(&dummy_hpp, ret)) 1153 break; 1154 1155 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 1156 if (advance_hpp_check(&dummy_hpp, ret)) 1157 break; 1158 } 1159 1160 return ret; 1161 } 1162 1163 static void hist_browser__show_headers(struct hist_browser *browser) 1164 { 1165 char headers[1024]; 1166 1167 hists_browser__scnprintf_headers(browser, headers, sizeof(headers)); 1168 ui_browser__gotorc(&browser->b, 0, 0); 1169 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1170 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1171 } 1172 1173 static void ui_browser__hists_init_top(struct ui_browser *browser) 1174 { 1175 if (browser->top == NULL) { 1176 struct hist_browser *hb; 1177 1178 hb = container_of(browser, struct hist_browser, b); 1179 browser->top = rb_first(&hb->hists->entries); 1180 } 1181 } 1182 1183 static unsigned int hist_browser__refresh(struct ui_browser *browser) 1184 { 1185 unsigned row = 0; 1186 u16 header_offset = 0; 1187 struct rb_node *nd; 1188 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 1189 1190 if (hb->show_headers) { 1191 hist_browser__show_headers(hb); 1192 header_offset = 1; 1193 } 1194 1195 ui_browser__hists_init_top(browser); 1196 hb->he_selection = NULL; 1197 hb->selection = NULL; 1198 1199 for (nd = browser->top; nd; nd = rb_next(nd)) { 1200 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1201 float percent; 1202 1203 if (h->filtered) 1204 continue; 1205 1206 percent = hist_entry__get_percent_limit(h); 1207 if (percent < hb->min_pcnt) 1208 continue; 1209 1210 row += hist_browser__show_entry(hb, h, row); 1211 if (row == browser->rows) 1212 break; 1213 } 1214 1215 return row + header_offset; 1216 } 1217 1218 static struct rb_node *hists__filter_entries(struct rb_node *nd, 1219 float min_pcnt) 1220 { 1221 while (nd != NULL) { 1222 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1223 float percent = hist_entry__get_percent_limit(h); 1224 1225 if (!h->filtered && percent >= min_pcnt) 1226 return nd; 1227 1228 nd = rb_next(nd); 1229 } 1230 1231 return NULL; 1232 } 1233 1234 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 1235 float min_pcnt) 1236 { 1237 while (nd != NULL) { 1238 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1239 float percent = hist_entry__get_percent_limit(h); 1240 1241 if (!h->filtered && percent >= min_pcnt) 1242 return nd; 1243 1244 nd = rb_prev(nd); 1245 } 1246 1247 return NULL; 1248 } 1249 1250 static void ui_browser__hists_seek(struct ui_browser *browser, 1251 off_t offset, int whence) 1252 { 1253 struct hist_entry *h; 1254 struct rb_node *nd; 1255 bool first = true; 1256 struct hist_browser *hb; 1257 1258 hb = container_of(browser, struct hist_browser, b); 1259 1260 if (browser->nr_entries == 0) 1261 return; 1262 1263 ui_browser__hists_init_top(browser); 1264 1265 switch (whence) { 1266 case SEEK_SET: 1267 nd = hists__filter_entries(rb_first(browser->entries), 1268 hb->min_pcnt); 1269 break; 1270 case SEEK_CUR: 1271 nd = browser->top; 1272 goto do_offset; 1273 case SEEK_END: 1274 nd = hists__filter_prev_entries(rb_last(browser->entries), 1275 hb->min_pcnt); 1276 first = false; 1277 break; 1278 default: 1279 return; 1280 } 1281 1282 /* 1283 * Moves not relative to the first visible entry invalidates its 1284 * row_offset: 1285 */ 1286 h = rb_entry(browser->top, struct hist_entry, rb_node); 1287 h->row_offset = 0; 1288 1289 /* 1290 * Here we have to check if nd is expanded (+), if it is we can't go 1291 * the next top level hist_entry, instead we must compute an offset of 1292 * what _not_ to show and not change the first visible entry. 1293 * 1294 * This offset increments when we are going from top to bottom and 1295 * decreases when we're going from bottom to top. 1296 * 1297 * As we don't have backpointers to the top level in the callchains 1298 * structure, we need to always print the whole hist_entry callchain, 1299 * skipping the first ones that are before the first visible entry 1300 * and stop when we printed enough lines to fill the screen. 1301 */ 1302 do_offset: 1303 if (!nd) 1304 return; 1305 1306 if (offset > 0) { 1307 do { 1308 h = rb_entry(nd, struct hist_entry, rb_node); 1309 if (h->unfolded) { 1310 u16 remaining = h->nr_rows - h->row_offset; 1311 if (offset > remaining) { 1312 offset -= remaining; 1313 h->row_offset = 0; 1314 } else { 1315 h->row_offset += offset; 1316 offset = 0; 1317 browser->top = nd; 1318 break; 1319 } 1320 } 1321 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt); 1322 if (nd == NULL) 1323 break; 1324 --offset; 1325 browser->top = nd; 1326 } while (offset != 0); 1327 } else if (offset < 0) { 1328 while (1) { 1329 h = rb_entry(nd, struct hist_entry, rb_node); 1330 if (h->unfolded) { 1331 if (first) { 1332 if (-offset > h->row_offset) { 1333 offset += h->row_offset; 1334 h->row_offset = 0; 1335 } else { 1336 h->row_offset += offset; 1337 offset = 0; 1338 browser->top = nd; 1339 break; 1340 } 1341 } else { 1342 if (-offset > h->nr_rows) { 1343 offset += h->nr_rows; 1344 h->row_offset = 0; 1345 } else { 1346 h->row_offset = h->nr_rows + offset; 1347 offset = 0; 1348 browser->top = nd; 1349 break; 1350 } 1351 } 1352 } 1353 1354 nd = hists__filter_prev_entries(rb_prev(nd), 1355 hb->min_pcnt); 1356 if (nd == NULL) 1357 break; 1358 ++offset; 1359 browser->top = nd; 1360 if (offset == 0) { 1361 /* 1362 * Last unfiltered hist_entry, check if it is 1363 * unfolded, if it is then we should have 1364 * row_offset at its last entry. 1365 */ 1366 h = rb_entry(nd, struct hist_entry, rb_node); 1367 if (h->unfolded) 1368 h->row_offset = h->nr_rows; 1369 break; 1370 } 1371 first = false; 1372 } 1373 } else { 1374 browser->top = nd; 1375 h = rb_entry(nd, struct hist_entry, rb_node); 1376 h->row_offset = 0; 1377 } 1378 } 1379 1380 static int hist_browser__fprintf_callchain(struct hist_browser *browser, 1381 struct hist_entry *he, FILE *fp) 1382 { 1383 u64 total = hists__total_period(he->hists); 1384 struct callchain_print_arg arg = { 1385 .fp = fp, 1386 }; 1387 1388 if (symbol_conf.cumulate_callchain) 1389 total = he->stat_acc->period; 1390 1391 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, 1392 hist_browser__fprintf_callchain_entry, &arg, 1393 hist_browser__check_dump_full); 1394 return arg.printed; 1395 } 1396 1397 static int hist_browser__fprintf_entry(struct hist_browser *browser, 1398 struct hist_entry *he, FILE *fp) 1399 { 1400 char s[8192]; 1401 int printed = 0; 1402 char folded_sign = ' '; 1403 struct perf_hpp hpp = { 1404 .buf = s, 1405 .size = sizeof(s), 1406 }; 1407 struct perf_hpp_fmt *fmt; 1408 bool first = true; 1409 int ret; 1410 1411 if (symbol_conf.use_callchain) 1412 folded_sign = hist_entry__folded(he); 1413 1414 if (symbol_conf.use_callchain) 1415 printed += fprintf(fp, "%c ", folded_sign); 1416 1417 perf_hpp__for_each_format(fmt) { 1418 if (perf_hpp__should_skip(fmt, he->hists)) 1419 continue; 1420 1421 if (!first) { 1422 ret = scnprintf(hpp.buf, hpp.size, " "); 1423 advance_hpp(&hpp, ret); 1424 } else 1425 first = false; 1426 1427 ret = fmt->entry(fmt, &hpp, he); 1428 advance_hpp(&hpp, ret); 1429 } 1430 printed += fprintf(fp, "%s\n", rtrim(s)); 1431 1432 if (folded_sign == '-') 1433 printed += hist_browser__fprintf_callchain(browser, he, fp); 1434 1435 return printed; 1436 } 1437 1438 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 1439 { 1440 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 1441 browser->min_pcnt); 1442 int printed = 0; 1443 1444 while (nd) { 1445 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1446 1447 printed += hist_browser__fprintf_entry(browser, h, fp); 1448 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); 1449 } 1450 1451 return printed; 1452 } 1453 1454 static int hist_browser__dump(struct hist_browser *browser) 1455 { 1456 char filename[64]; 1457 FILE *fp; 1458 1459 while (1) { 1460 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 1461 if (access(filename, F_OK)) 1462 break; 1463 /* 1464 * XXX: Just an arbitrary lazy upper limit 1465 */ 1466 if (++browser->print_seq == 8192) { 1467 ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 1468 return -1; 1469 } 1470 } 1471 1472 fp = fopen(filename, "w"); 1473 if (fp == NULL) { 1474 char bf[64]; 1475 const char *err = strerror_r(errno, bf, sizeof(bf)); 1476 ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 1477 return -1; 1478 } 1479 1480 ++browser->print_seq; 1481 hist_browser__fprintf(browser, fp); 1482 fclose(fp); 1483 ui_helpline__fpush("%s written!", filename); 1484 1485 return 0; 1486 } 1487 1488 static struct hist_browser *hist_browser__new(struct hists *hists, 1489 struct hist_browser_timer *hbt, 1490 struct perf_env *env) 1491 { 1492 struct hist_browser *browser = zalloc(sizeof(*browser)); 1493 1494 if (browser) { 1495 browser->hists = hists; 1496 browser->b.refresh = hist_browser__refresh; 1497 browser->b.refresh_dimensions = hist_browser__refresh_dimensions; 1498 browser->b.seek = ui_browser__hists_seek; 1499 browser->b.use_navkeypressed = true; 1500 browser->show_headers = symbol_conf.show_hist_headers; 1501 browser->hbt = hbt; 1502 browser->env = env; 1503 } 1504 1505 return browser; 1506 } 1507 1508 static void hist_browser__delete(struct hist_browser *browser) 1509 { 1510 free(browser); 1511 } 1512 1513 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 1514 { 1515 return browser->he_selection; 1516 } 1517 1518 static struct thread *hist_browser__selected_thread(struct hist_browser *browser) 1519 { 1520 return browser->he_selection->thread; 1521 } 1522 1523 /* Check whether the browser is for 'top' or 'report' */ 1524 static inline bool is_report_browser(void *timer) 1525 { 1526 return timer == NULL; 1527 } 1528 1529 static int hists__browser_title(struct hists *hists, 1530 struct hist_browser_timer *hbt, 1531 char *bf, size_t size) 1532 { 1533 char unit; 1534 int printed; 1535 const struct dso *dso = hists->dso_filter; 1536 const struct thread *thread = hists->thread_filter; 1537 int socket_id = hists->socket_filter; 1538 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1539 u64 nr_events = hists->stats.total_period; 1540 struct perf_evsel *evsel = hists_to_evsel(hists); 1541 const char *ev_name = perf_evsel__name(evsel); 1542 char buf[512]; 1543 size_t buflen = sizeof(buf); 1544 char ref[30] = " show reference callgraph, "; 1545 bool enable_ref = false; 1546 1547 if (symbol_conf.filter_relative) { 1548 nr_samples = hists->stats.nr_non_filtered_samples; 1549 nr_events = hists->stats.total_non_filtered_period; 1550 } 1551 1552 if (perf_evsel__is_group_event(evsel)) { 1553 struct perf_evsel *pos; 1554 1555 perf_evsel__group_desc(evsel, buf, buflen); 1556 ev_name = buf; 1557 1558 for_each_group_member(pos, evsel) { 1559 struct hists *pos_hists = evsel__hists(pos); 1560 1561 if (symbol_conf.filter_relative) { 1562 nr_samples += pos_hists->stats.nr_non_filtered_samples; 1563 nr_events += pos_hists->stats.total_non_filtered_period; 1564 } else { 1565 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1566 nr_events += pos_hists->stats.total_period; 1567 } 1568 } 1569 } 1570 1571 if (symbol_conf.show_ref_callgraph && 1572 strstr(ev_name, "call-graph=no")) 1573 enable_ref = true; 1574 nr_samples = convert_unit(nr_samples, &unit); 1575 printed = scnprintf(bf, size, 1576 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, 1577 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); 1578 1579 1580 if (hists->uid_filter_str) 1581 printed += snprintf(bf + printed, size - printed, 1582 ", UID: %s", hists->uid_filter_str); 1583 if (thread) 1584 printed += scnprintf(bf + printed, size - printed, 1585 ", Thread: %s(%d)", 1586 (thread->comm_set ? thread__comm_str(thread) : ""), 1587 thread->tid); 1588 if (dso) 1589 printed += scnprintf(bf + printed, size - printed, 1590 ", DSO: %s", dso->short_name); 1591 if (socket_id > -1) 1592 printed += scnprintf(bf + printed, size - printed, 1593 ", Processor Socket: %d", socket_id); 1594 if (!is_report_browser(hbt)) { 1595 struct perf_top *top = hbt->arg; 1596 1597 if (top->zero) 1598 printed += scnprintf(bf + printed, size - printed, " [z]"); 1599 } 1600 1601 return printed; 1602 } 1603 1604 static inline void free_popup_options(char **options, int n) 1605 { 1606 int i; 1607 1608 for (i = 0; i < n; ++i) 1609 zfree(&options[i]); 1610 } 1611 1612 /* 1613 * Only runtime switching of perf data file will make "input_name" point 1614 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 1615 * whether we need to call free() for current "input_name" during the switch. 1616 */ 1617 static bool is_input_name_malloced = false; 1618 1619 static int switch_data_file(void) 1620 { 1621 char *pwd, *options[32], *abs_path[32], *tmp; 1622 DIR *pwd_dir; 1623 int nr_options = 0, choice = -1, ret = -1; 1624 struct dirent *dent; 1625 1626 pwd = getenv("PWD"); 1627 if (!pwd) 1628 return ret; 1629 1630 pwd_dir = opendir(pwd); 1631 if (!pwd_dir) 1632 return ret; 1633 1634 memset(options, 0, sizeof(options)); 1635 memset(options, 0, sizeof(abs_path)); 1636 1637 while ((dent = readdir(pwd_dir))) { 1638 char path[PATH_MAX]; 1639 u64 magic; 1640 char *name = dent->d_name; 1641 FILE *file; 1642 1643 if (!(dent->d_type == DT_REG)) 1644 continue; 1645 1646 snprintf(path, sizeof(path), "%s/%s", pwd, name); 1647 1648 file = fopen(path, "r"); 1649 if (!file) 1650 continue; 1651 1652 if (fread(&magic, 1, 8, file) < 8) 1653 goto close_file_and_continue; 1654 1655 if (is_perf_magic(magic)) { 1656 options[nr_options] = strdup(name); 1657 if (!options[nr_options]) 1658 goto close_file_and_continue; 1659 1660 abs_path[nr_options] = strdup(path); 1661 if (!abs_path[nr_options]) { 1662 zfree(&options[nr_options]); 1663 ui__warning("Can't search all data files due to memory shortage.\n"); 1664 fclose(file); 1665 break; 1666 } 1667 1668 nr_options++; 1669 } 1670 1671 close_file_and_continue: 1672 fclose(file); 1673 if (nr_options >= 32) { 1674 ui__warning("Too many perf data files in PWD!\n" 1675 "Only the first 32 files will be listed.\n"); 1676 break; 1677 } 1678 } 1679 closedir(pwd_dir); 1680 1681 if (nr_options) { 1682 choice = ui__popup_menu(nr_options, options); 1683 if (choice < nr_options && choice >= 0) { 1684 tmp = strdup(abs_path[choice]); 1685 if (tmp) { 1686 if (is_input_name_malloced) 1687 free((void *)input_name); 1688 input_name = tmp; 1689 is_input_name_malloced = true; 1690 ret = 0; 1691 } else 1692 ui__warning("Data switch failed due to memory shortage!\n"); 1693 } 1694 } 1695 1696 free_popup_options(options, nr_options); 1697 free_popup_options(abs_path, nr_options); 1698 return ret; 1699 } 1700 1701 struct popup_action { 1702 struct thread *thread; 1703 struct map_symbol ms; 1704 int socket; 1705 1706 int (*fn)(struct hist_browser *browser, struct popup_action *act); 1707 }; 1708 1709 static int 1710 do_annotate(struct hist_browser *browser, struct popup_action *act) 1711 { 1712 struct perf_evsel *evsel; 1713 struct annotation *notes; 1714 struct hist_entry *he; 1715 int err; 1716 1717 if (!objdump_path && perf_env__lookup_objdump(browser->env)) 1718 return 0; 1719 1720 notes = symbol__annotation(act->ms.sym); 1721 if (!notes->src) 1722 return 0; 1723 1724 evsel = hists_to_evsel(browser->hists); 1725 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); 1726 he = hist_browser__selected_entry(browser); 1727 /* 1728 * offer option to annotate the other branch source or target 1729 * (if they exists) when returning from annotate 1730 */ 1731 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 1732 return 1; 1733 1734 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1735 if (err) 1736 ui_browser__handle_resize(&browser->b); 1737 return 0; 1738 } 1739 1740 static int 1741 add_annotate_opt(struct hist_browser *browser __maybe_unused, 1742 struct popup_action *act, char **optstr, 1743 struct map *map, struct symbol *sym) 1744 { 1745 if (sym == NULL || map->dso->annotate_warned) 1746 return 0; 1747 1748 if (asprintf(optstr, "Annotate %s", sym->name) < 0) 1749 return 0; 1750 1751 act->ms.map = map; 1752 act->ms.sym = sym; 1753 act->fn = do_annotate; 1754 return 1; 1755 } 1756 1757 static int 1758 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 1759 { 1760 struct thread *thread = act->thread; 1761 1762 if (browser->hists->thread_filter) { 1763 pstack__remove(browser->pstack, &browser->hists->thread_filter); 1764 perf_hpp__set_elide(HISTC_THREAD, false); 1765 thread__zput(browser->hists->thread_filter); 1766 ui_helpline__pop(); 1767 } else { 1768 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 1769 thread->comm_set ? thread__comm_str(thread) : "", 1770 thread->tid); 1771 browser->hists->thread_filter = thread__get(thread); 1772 perf_hpp__set_elide(HISTC_THREAD, false); 1773 pstack__push(browser->pstack, &browser->hists->thread_filter); 1774 } 1775 1776 hists__filter_by_thread(browser->hists); 1777 hist_browser__reset(browser); 1778 return 0; 1779 } 1780 1781 static int 1782 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 1783 char **optstr, struct thread *thread) 1784 { 1785 if (thread == NULL) 1786 return 0; 1787 1788 if (asprintf(optstr, "Zoom %s %s(%d) thread", 1789 browser->hists->thread_filter ? "out of" : "into", 1790 thread->comm_set ? thread__comm_str(thread) : "", 1791 thread->tid) < 0) 1792 return 0; 1793 1794 act->thread = thread; 1795 act->fn = do_zoom_thread; 1796 return 1; 1797 } 1798 1799 static int 1800 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 1801 { 1802 struct map *map = act->ms.map; 1803 1804 if (browser->hists->dso_filter) { 1805 pstack__remove(browser->pstack, &browser->hists->dso_filter); 1806 perf_hpp__set_elide(HISTC_DSO, false); 1807 browser->hists->dso_filter = NULL; 1808 ui_helpline__pop(); 1809 } else { 1810 if (map == NULL) 1811 return 0; 1812 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", 1813 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name); 1814 browser->hists->dso_filter = map->dso; 1815 perf_hpp__set_elide(HISTC_DSO, true); 1816 pstack__push(browser->pstack, &browser->hists->dso_filter); 1817 } 1818 1819 hists__filter_by_dso(browser->hists); 1820 hist_browser__reset(browser); 1821 return 0; 1822 } 1823 1824 static int 1825 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 1826 char **optstr, struct map *map) 1827 { 1828 if (map == NULL) 1829 return 0; 1830 1831 if (asprintf(optstr, "Zoom %s %s DSO", 1832 browser->hists->dso_filter ? "out of" : "into", 1833 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) 1834 return 0; 1835 1836 act->ms.map = map; 1837 act->fn = do_zoom_dso; 1838 return 1; 1839 } 1840 1841 static int 1842 do_browse_map(struct hist_browser *browser __maybe_unused, 1843 struct popup_action *act) 1844 { 1845 map__browse(act->ms.map); 1846 return 0; 1847 } 1848 1849 static int 1850 add_map_opt(struct hist_browser *browser __maybe_unused, 1851 struct popup_action *act, char **optstr, struct map *map) 1852 { 1853 if (map == NULL) 1854 return 0; 1855 1856 if (asprintf(optstr, "Browse map details") < 0) 1857 return 0; 1858 1859 act->ms.map = map; 1860 act->fn = do_browse_map; 1861 return 1; 1862 } 1863 1864 static int 1865 do_run_script(struct hist_browser *browser __maybe_unused, 1866 struct popup_action *act) 1867 { 1868 char script_opt[64]; 1869 memset(script_opt, 0, sizeof(script_opt)); 1870 1871 if (act->thread) { 1872 scnprintf(script_opt, sizeof(script_opt), " -c %s ", 1873 thread__comm_str(act->thread)); 1874 } else if (act->ms.sym) { 1875 scnprintf(script_opt, sizeof(script_opt), " -S %s ", 1876 act->ms.sym->name); 1877 } 1878 1879 script_browse(script_opt); 1880 return 0; 1881 } 1882 1883 static int 1884 add_script_opt(struct hist_browser *browser __maybe_unused, 1885 struct popup_action *act, char **optstr, 1886 struct thread *thread, struct symbol *sym) 1887 { 1888 if (thread) { 1889 if (asprintf(optstr, "Run scripts for samples of thread [%s]", 1890 thread__comm_str(thread)) < 0) 1891 return 0; 1892 } else if (sym) { 1893 if (asprintf(optstr, "Run scripts for samples of symbol [%s]", 1894 sym->name) < 0) 1895 return 0; 1896 } else { 1897 if (asprintf(optstr, "Run scripts for all samples") < 0) 1898 return 0; 1899 } 1900 1901 act->thread = thread; 1902 act->ms.sym = sym; 1903 act->fn = do_run_script; 1904 return 1; 1905 } 1906 1907 static int 1908 do_switch_data(struct hist_browser *browser __maybe_unused, 1909 struct popup_action *act __maybe_unused) 1910 { 1911 if (switch_data_file()) { 1912 ui__warning("Won't switch the data files due to\n" 1913 "no valid data file get selected!\n"); 1914 return 0; 1915 } 1916 1917 return K_SWITCH_INPUT_DATA; 1918 } 1919 1920 static int 1921 add_switch_opt(struct hist_browser *browser, 1922 struct popup_action *act, char **optstr) 1923 { 1924 if (!is_report_browser(browser->hbt)) 1925 return 0; 1926 1927 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 1928 return 0; 1929 1930 act->fn = do_switch_data; 1931 return 1; 1932 } 1933 1934 static int 1935 do_exit_browser(struct hist_browser *browser __maybe_unused, 1936 struct popup_action *act __maybe_unused) 1937 { 1938 return 0; 1939 } 1940 1941 static int 1942 add_exit_opt(struct hist_browser *browser __maybe_unused, 1943 struct popup_action *act, char **optstr) 1944 { 1945 if (asprintf(optstr, "Exit") < 0) 1946 return 0; 1947 1948 act->fn = do_exit_browser; 1949 return 1; 1950 } 1951 1952 static int 1953 do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 1954 { 1955 if (browser->hists->socket_filter > -1) { 1956 pstack__remove(browser->pstack, &browser->hists->socket_filter); 1957 browser->hists->socket_filter = -1; 1958 perf_hpp__set_elide(HISTC_SOCKET, false); 1959 } else { 1960 browser->hists->socket_filter = act->socket; 1961 perf_hpp__set_elide(HISTC_SOCKET, true); 1962 pstack__push(browser->pstack, &browser->hists->socket_filter); 1963 } 1964 1965 hists__filter_by_socket(browser->hists); 1966 hist_browser__reset(browser); 1967 return 0; 1968 } 1969 1970 static int 1971 add_socket_opt(struct hist_browser *browser, struct popup_action *act, 1972 char **optstr, int socket_id) 1973 { 1974 if (socket_id < 0) 1975 return 0; 1976 1977 if (asprintf(optstr, "Zoom %s Processor Socket %d", 1978 (browser->hists->socket_filter > -1) ? "out of" : "into", 1979 socket_id) < 0) 1980 return 0; 1981 1982 act->socket = socket_id; 1983 act->fn = do_zoom_socket; 1984 return 1; 1985 } 1986 1987 static void hist_browser__update_nr_entries(struct hist_browser *hb) 1988 { 1989 u64 nr_entries = 0; 1990 struct rb_node *nd = rb_first(&hb->hists->entries); 1991 1992 if (hb->min_pcnt == 0) { 1993 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 1994 return; 1995 } 1996 1997 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 1998 nr_entries++; 1999 nd = rb_next(nd); 2000 } 2001 2002 hb->nr_non_filtered_entries = nr_entries; 2003 } 2004 2005 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 2006 const char *helpline, 2007 bool left_exits, 2008 struct hist_browser_timer *hbt, 2009 float min_pcnt, 2010 struct perf_env *env) 2011 { 2012 struct hists *hists = evsel__hists(evsel); 2013 struct hist_browser *browser = hist_browser__new(hists, hbt, env); 2014 struct branch_info *bi; 2015 #define MAX_OPTIONS 16 2016 char *options[MAX_OPTIONS]; 2017 struct popup_action actions[MAX_OPTIONS]; 2018 int nr_options = 0; 2019 int key = -1; 2020 char buf[64]; 2021 int delay_secs = hbt ? hbt->refresh : 0; 2022 struct perf_hpp_fmt *fmt; 2023 2024 #define HIST_BROWSER_HELP_COMMON \ 2025 "h/?/F1 Show this window\n" \ 2026 "UP/DOWN/PGUP\n" \ 2027 "PGDN/SPACE Navigate\n" \ 2028 "q/ESC/CTRL+C Exit browser\n\n" \ 2029 "For multiple event sessions:\n\n" \ 2030 "TAB/UNTAB Switch events\n\n" \ 2031 "For symbolic views (--sort has sym):\n\n" \ 2032 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ 2033 "ESC Zoom out\n" \ 2034 "a Annotate current symbol\n" \ 2035 "C Collapse all callchains\n" \ 2036 "d Zoom into current DSO\n" \ 2037 "E Expand all callchains\n" \ 2038 "F Toggle percentage of filtered entries\n" \ 2039 "H Display column headers\n" \ 2040 "m Display context menu\n" \ 2041 "S Zoom into current Processor Socket\n" \ 2042 2043 /* help messages are sorted by lexical order of the hotkey */ 2044 const char report_help[] = HIST_BROWSER_HELP_COMMON 2045 "i Show header information\n" 2046 "P Print histograms to perf.hist.N\n" 2047 "r Run available scripts\n" 2048 "s Switch to another data file in PWD\n" 2049 "t Zoom into current Thread\n" 2050 "V Verbose (DSO names in callchains, etc)\n" 2051 "/ Filter symbol by name"; 2052 const char top_help[] = HIST_BROWSER_HELP_COMMON 2053 "P Print histograms to perf.hist.N\n" 2054 "t Zoom into current Thread\n" 2055 "V Verbose (DSO names in callchains, etc)\n" 2056 "z Toggle zeroing of samples\n" 2057 "f Enable/Disable events\n" 2058 "/ Filter symbol by name"; 2059 2060 if (browser == NULL) 2061 return -1; 2062 2063 /* reset abort key so that it can get Ctrl-C as a key */ 2064 SLang_reset_tty(); 2065 SLang_init_tty(0, 0, 0); 2066 2067 if (min_pcnt) 2068 browser->min_pcnt = min_pcnt; 2069 hist_browser__update_nr_entries(browser); 2070 2071 browser->pstack = pstack__new(3); 2072 if (browser->pstack == NULL) 2073 goto out; 2074 2075 ui_helpline__push(helpline); 2076 2077 memset(options, 0, sizeof(options)); 2078 memset(actions, 0, sizeof(actions)); 2079 2080 perf_hpp__for_each_format(fmt) { 2081 perf_hpp__reset_width(fmt, hists); 2082 /* 2083 * This is done just once, and activates the horizontal scrolling 2084 * code in the ui_browser code, it would be better to have a the 2085 * counter in the perf_hpp code, but I couldn't find doing it here 2086 * works, FIXME by setting this in hist_browser__new, for now, be 2087 * clever 8-) 2088 */ 2089 ++browser->b.columns; 2090 } 2091 2092 if (symbol_conf.col_width_list_str) 2093 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 2094 2095 while (1) { 2096 struct thread *thread = NULL; 2097 struct map *map = NULL; 2098 int choice = 0; 2099 int socked_id = -1; 2100 2101 nr_options = 0; 2102 2103 key = hist_browser__run(browser, helpline); 2104 2105 if (browser->he_selection != NULL) { 2106 thread = hist_browser__selected_thread(browser); 2107 map = browser->selection->map; 2108 socked_id = browser->he_selection->socket; 2109 } 2110 switch (key) { 2111 case K_TAB: 2112 case K_UNTAB: 2113 if (nr_events == 1) 2114 continue; 2115 /* 2116 * Exit the browser, let hists__browser_tree 2117 * go to the next or previous 2118 */ 2119 goto out_free_stack; 2120 case 'a': 2121 if (!sort__has_sym) { 2122 ui_browser__warning(&browser->b, delay_secs * 2, 2123 "Annotation is only available for symbolic views, " 2124 "include \"sym*\" in --sort to use it."); 2125 continue; 2126 } 2127 2128 if (browser->selection == NULL || 2129 browser->selection->sym == NULL || 2130 browser->selection->map->dso->annotate_warned) 2131 continue; 2132 2133 actions->ms.map = browser->selection->map; 2134 actions->ms.sym = browser->selection->sym; 2135 do_annotate(browser, actions); 2136 continue; 2137 case 'P': 2138 hist_browser__dump(browser); 2139 continue; 2140 case 'd': 2141 actions->ms.map = map; 2142 do_zoom_dso(browser, actions); 2143 continue; 2144 case 'V': 2145 browser->show_dso = !browser->show_dso; 2146 continue; 2147 case 't': 2148 actions->thread = thread; 2149 do_zoom_thread(browser, actions); 2150 continue; 2151 case 'S': 2152 actions->socket = socked_id; 2153 do_zoom_socket(browser, actions); 2154 continue; 2155 case '/': 2156 if (ui_browser__input_window("Symbol to show", 2157 "Please enter the name of symbol you want to see.\n" 2158 "To remove the filter later, press / + ENTER.", 2159 buf, "ENTER: OK, ESC: Cancel", 2160 delay_secs * 2) == K_ENTER) { 2161 hists->symbol_filter_str = *buf ? buf : NULL; 2162 hists__filter_by_symbol(hists); 2163 hist_browser__reset(browser); 2164 } 2165 continue; 2166 case 'r': 2167 if (is_report_browser(hbt)) { 2168 actions->thread = NULL; 2169 actions->ms.sym = NULL; 2170 do_run_script(browser, actions); 2171 } 2172 continue; 2173 case 's': 2174 if (is_report_browser(hbt)) { 2175 key = do_switch_data(browser, actions); 2176 if (key == K_SWITCH_INPUT_DATA) 2177 goto out_free_stack; 2178 } 2179 continue; 2180 case 'i': 2181 /* env->arch is NULL for live-mode (i.e. perf top) */ 2182 if (env->arch) 2183 tui__header_window(env); 2184 continue; 2185 case 'F': 2186 symbol_conf.filter_relative ^= 1; 2187 continue; 2188 case 'z': 2189 if (!is_report_browser(hbt)) { 2190 struct perf_top *top = hbt->arg; 2191 2192 top->zero = !top->zero; 2193 } 2194 continue; 2195 case K_F1: 2196 case 'h': 2197 case '?': 2198 ui_browser__help_window(&browser->b, 2199 is_report_browser(hbt) ? report_help : top_help); 2200 continue; 2201 case K_ENTER: 2202 case K_RIGHT: 2203 case 'm': 2204 /* menu */ 2205 break; 2206 case K_ESC: 2207 case K_LEFT: { 2208 const void *top; 2209 2210 if (pstack__empty(browser->pstack)) { 2211 /* 2212 * Go back to the perf_evsel_menu__run or other user 2213 */ 2214 if (left_exits) 2215 goto out_free_stack; 2216 2217 if (key == K_ESC && 2218 ui_browser__dialog_yesno(&browser->b, 2219 "Do you really want to exit?")) 2220 goto out_free_stack; 2221 2222 continue; 2223 } 2224 top = pstack__peek(browser->pstack); 2225 if (top == &browser->hists->dso_filter) { 2226 /* 2227 * No need to set actions->dso here since 2228 * it's just to remove the current filter. 2229 * Ditto for thread below. 2230 */ 2231 do_zoom_dso(browser, actions); 2232 } else if (top == &browser->hists->thread_filter) { 2233 do_zoom_thread(browser, actions); 2234 } else if (top == &browser->hists->socket_filter) { 2235 do_zoom_socket(browser, actions); 2236 } 2237 continue; 2238 } 2239 case 'q': 2240 case CTRL('c'): 2241 goto out_free_stack; 2242 case 'f': 2243 if (!is_report_browser(hbt)) { 2244 struct perf_top *top = hbt->arg; 2245 2246 perf_evlist__toggle_enable(top->evlist); 2247 /* 2248 * No need to refresh, resort/decay histogram 2249 * entries if we are not collecting samples: 2250 */ 2251 if (top->evlist->enabled) { 2252 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 2253 hbt->refresh = delay_secs; 2254 } else { 2255 helpline = "Press 'f' again to re-enable the events"; 2256 hbt->refresh = 0; 2257 } 2258 continue; 2259 } 2260 /* Fall thru */ 2261 default: 2262 helpline = "Press '?' for help on key bindings"; 2263 continue; 2264 } 2265 2266 if (!sort__has_sym) 2267 goto add_exit_option; 2268 2269 if (browser->selection == NULL) 2270 goto skip_annotation; 2271 2272 if (sort__mode == SORT_MODE__BRANCH) { 2273 bi = browser->he_selection->branch_info; 2274 2275 if (bi == NULL) 2276 goto skip_annotation; 2277 2278 nr_options += add_annotate_opt(browser, 2279 &actions[nr_options], 2280 &options[nr_options], 2281 bi->from.map, 2282 bi->from.sym); 2283 if (bi->to.sym != bi->from.sym) 2284 nr_options += add_annotate_opt(browser, 2285 &actions[nr_options], 2286 &options[nr_options], 2287 bi->to.map, 2288 bi->to.sym); 2289 } else { 2290 nr_options += add_annotate_opt(browser, 2291 &actions[nr_options], 2292 &options[nr_options], 2293 browser->selection->map, 2294 browser->selection->sym); 2295 } 2296 skip_annotation: 2297 nr_options += add_thread_opt(browser, &actions[nr_options], 2298 &options[nr_options], thread); 2299 nr_options += add_dso_opt(browser, &actions[nr_options], 2300 &options[nr_options], map); 2301 nr_options += add_map_opt(browser, &actions[nr_options], 2302 &options[nr_options], 2303 browser->selection ? 2304 browser->selection->map : NULL); 2305 nr_options += add_socket_opt(browser, &actions[nr_options], 2306 &options[nr_options], 2307 socked_id); 2308 /* perf script support */ 2309 if (browser->he_selection) { 2310 nr_options += add_script_opt(browser, 2311 &actions[nr_options], 2312 &options[nr_options], 2313 thread, NULL); 2314 /* 2315 * Note that browser->selection != NULL 2316 * when browser->he_selection is not NULL, 2317 * so we don't need to check browser->selection 2318 * before fetching browser->selection->sym like what 2319 * we do before fetching browser->selection->map. 2320 * 2321 * See hist_browser__show_entry. 2322 */ 2323 nr_options += add_script_opt(browser, 2324 &actions[nr_options], 2325 &options[nr_options], 2326 NULL, browser->selection->sym); 2327 } 2328 nr_options += add_script_opt(browser, &actions[nr_options], 2329 &options[nr_options], NULL, NULL); 2330 nr_options += add_switch_opt(browser, &actions[nr_options], 2331 &options[nr_options]); 2332 add_exit_option: 2333 nr_options += add_exit_opt(browser, &actions[nr_options], 2334 &options[nr_options]); 2335 2336 do { 2337 struct popup_action *act; 2338 2339 choice = ui__popup_menu(nr_options, options); 2340 if (choice == -1 || choice >= nr_options) 2341 break; 2342 2343 act = &actions[choice]; 2344 key = act->fn(browser, act); 2345 } while (key == 1); 2346 2347 if (key == K_SWITCH_INPUT_DATA) 2348 break; 2349 } 2350 out_free_stack: 2351 pstack__delete(browser->pstack); 2352 out: 2353 hist_browser__delete(browser); 2354 free_popup_options(options, MAX_OPTIONS); 2355 return key; 2356 } 2357 2358 struct perf_evsel_menu { 2359 struct ui_browser b; 2360 struct perf_evsel *selection; 2361 bool lost_events, lost_events_warned; 2362 float min_pcnt; 2363 struct perf_env *env; 2364 }; 2365 2366 static void perf_evsel_menu__write(struct ui_browser *browser, 2367 void *entry, int row) 2368 { 2369 struct perf_evsel_menu *menu = container_of(browser, 2370 struct perf_evsel_menu, b); 2371 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2372 struct hists *hists = evsel__hists(evsel); 2373 bool current_entry = ui_browser__is_current_entry(browser, row); 2374 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2375 const char *ev_name = perf_evsel__name(evsel); 2376 char bf[256], unit; 2377 const char *warn = " "; 2378 size_t printed; 2379 2380 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 2381 HE_COLORSET_NORMAL); 2382 2383 if (perf_evsel__is_group_event(evsel)) { 2384 struct perf_evsel *pos; 2385 2386 ev_name = perf_evsel__group_name(evsel); 2387 2388 for_each_group_member(pos, evsel) { 2389 struct hists *pos_hists = evsel__hists(pos); 2390 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2391 } 2392 } 2393 2394 nr_events = convert_unit(nr_events, &unit); 2395 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 2396 unit, unit == ' ' ? "" : " ", ev_name); 2397 ui_browser__printf(browser, "%s", bf); 2398 2399 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 2400 if (nr_events != 0) { 2401 menu->lost_events = true; 2402 if (!current_entry) 2403 ui_browser__set_color(browser, HE_COLORSET_TOP); 2404 nr_events = convert_unit(nr_events, &unit); 2405 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 2406 nr_events, unit, unit == ' ' ? "" : " "); 2407 warn = bf; 2408 } 2409 2410 ui_browser__write_nstring(browser, warn, browser->width - printed); 2411 2412 if (current_entry) 2413 menu->selection = evsel; 2414 } 2415 2416 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 2417 int nr_events, const char *help, 2418 struct hist_browser_timer *hbt) 2419 { 2420 struct perf_evlist *evlist = menu->b.priv; 2421 struct perf_evsel *pos; 2422 const char *title = "Available samples"; 2423 int delay_secs = hbt ? hbt->refresh : 0; 2424 int key; 2425 2426 if (ui_browser__show(&menu->b, title, 2427 "ESC: exit, ENTER|->: Browse histograms") < 0) 2428 return -1; 2429 2430 while (1) { 2431 key = ui_browser__run(&menu->b, delay_secs); 2432 2433 switch (key) { 2434 case K_TIMER: 2435 hbt->timer(hbt->arg); 2436 2437 if (!menu->lost_events_warned && menu->lost_events) { 2438 ui_browser__warn_lost_events(&menu->b); 2439 menu->lost_events_warned = true; 2440 } 2441 continue; 2442 case K_RIGHT: 2443 case K_ENTER: 2444 if (!menu->selection) 2445 continue; 2446 pos = menu->selection; 2447 browse_hists: 2448 perf_evlist__set_selected(evlist, pos); 2449 /* 2450 * Give the calling tool a chance to populate the non 2451 * default evsel resorted hists tree. 2452 */ 2453 if (hbt) 2454 hbt->timer(hbt->arg); 2455 key = perf_evsel__hists_browse(pos, nr_events, help, 2456 true, hbt, 2457 menu->min_pcnt, 2458 menu->env); 2459 ui_browser__show_title(&menu->b, title); 2460 switch (key) { 2461 case K_TAB: 2462 if (pos->node.next == &evlist->entries) 2463 pos = perf_evlist__first(evlist); 2464 else 2465 pos = perf_evsel__next(pos); 2466 goto browse_hists; 2467 case K_UNTAB: 2468 if (pos->node.prev == &evlist->entries) 2469 pos = perf_evlist__last(evlist); 2470 else 2471 pos = perf_evsel__prev(pos); 2472 goto browse_hists; 2473 case K_SWITCH_INPUT_DATA: 2474 case 'q': 2475 case CTRL('c'): 2476 goto out; 2477 case K_ESC: 2478 default: 2479 continue; 2480 } 2481 case K_LEFT: 2482 continue; 2483 case K_ESC: 2484 if (!ui_browser__dialog_yesno(&menu->b, 2485 "Do you really want to exit?")) 2486 continue; 2487 /* Fall thru */ 2488 case 'q': 2489 case CTRL('c'): 2490 goto out; 2491 default: 2492 continue; 2493 } 2494 } 2495 2496 out: 2497 ui_browser__hide(&menu->b); 2498 return key; 2499 } 2500 2501 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 2502 void *entry) 2503 { 2504 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2505 2506 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 2507 return true; 2508 2509 return false; 2510 } 2511 2512 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 2513 int nr_entries, const char *help, 2514 struct hist_browser_timer *hbt, 2515 float min_pcnt, 2516 struct perf_env *env) 2517 { 2518 struct perf_evsel *pos; 2519 struct perf_evsel_menu menu = { 2520 .b = { 2521 .entries = &evlist->entries, 2522 .refresh = ui_browser__list_head_refresh, 2523 .seek = ui_browser__list_head_seek, 2524 .write = perf_evsel_menu__write, 2525 .filter = filter_group_entries, 2526 .nr_entries = nr_entries, 2527 .priv = evlist, 2528 }, 2529 .min_pcnt = min_pcnt, 2530 .env = env, 2531 }; 2532 2533 ui_helpline__push("Press ESC to exit"); 2534 2535 evlist__for_each(evlist, pos) { 2536 const char *ev_name = perf_evsel__name(pos); 2537 size_t line_len = strlen(ev_name) + 7; 2538 2539 if (menu.b.width < line_len) 2540 menu.b.width = line_len; 2541 } 2542 2543 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 2544 } 2545 2546 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 2547 struct hist_browser_timer *hbt, 2548 float min_pcnt, 2549 struct perf_env *env) 2550 { 2551 int nr_entries = evlist->nr_entries; 2552 2553 single_entry: 2554 if (nr_entries == 1) { 2555 struct perf_evsel *first = perf_evlist__first(evlist); 2556 2557 return perf_evsel__hists_browse(first, nr_entries, help, 2558 false, hbt, min_pcnt, 2559 env); 2560 } 2561 2562 if (symbol_conf.event_group) { 2563 struct perf_evsel *pos; 2564 2565 nr_entries = 0; 2566 evlist__for_each(evlist, pos) { 2567 if (perf_evsel__is_group_leader(pos)) 2568 nr_entries++; 2569 } 2570 2571 if (nr_entries == 1) 2572 goto single_entry; 2573 } 2574 2575 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 2576 hbt, min_pcnt, env); 2577 } 2578