1 #include <stdio.h> 2 #include "../libslang.h" 3 #include <stdlib.h> 4 #include <string.h> 5 #include <linux/rbtree.h> 6 7 #include "../../util/evsel.h" 8 #include "../../util/evlist.h" 9 #include "../../util/hist.h" 10 #include "../../util/pstack.h" 11 #include "../../util/sort.h" 12 #include "../../util/util.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 21 struct hist_browser { 22 struct ui_browser b; 23 struct hists *hists; 24 struct hist_entry *he_selection; 25 struct map_symbol *selection; 26 int print_seq; 27 bool show_dso; 28 float min_pcnt; 29 u64 nr_non_filtered_entries; 30 u64 nr_callchain_rows; 31 }; 32 33 extern void hist_browser__init_hpp(void); 34 35 static int hists__browser_title(struct hists *hists, char *bf, size_t size, 36 const char *ev_name); 37 static void hist_browser__update_nr_entries(struct hist_browser *hb); 38 39 static struct rb_node *hists__filter_entries(struct rb_node *nd, 40 struct hists *hists, 41 float min_pcnt); 42 43 static bool hist_browser__has_filter(struct hist_browser *hb) 44 { 45 return hists__has_filter(hb->hists) || hb->min_pcnt; 46 } 47 48 static u32 hist_browser__nr_entries(struct hist_browser *hb) 49 { 50 u32 nr_entries; 51 52 if (hist_browser__has_filter(hb)) 53 nr_entries = hb->nr_non_filtered_entries; 54 else 55 nr_entries = hb->hists->nr_entries; 56 57 return nr_entries + hb->nr_callchain_rows; 58 } 59 60 static void hist_browser__refresh_dimensions(struct hist_browser *browser) 61 { 62 /* 3 == +/- toggle symbol before actual hist_entry rendering */ 63 browser->b.width = 3 + (hists__sort_list_width(browser->hists) + 64 sizeof("[k]")); 65 } 66 67 static void hist_browser__reset(struct hist_browser *browser) 68 { 69 /* 70 * The hists__remove_entry_filter() already folds non-filtered 71 * entries so we can assume it has 0 callchain rows. 72 */ 73 browser->nr_callchain_rows = 0; 74 75 hist_browser__update_nr_entries(browser); 76 browser->b.nr_entries = hist_browser__nr_entries(browser); 77 hist_browser__refresh_dimensions(browser); 78 ui_browser__reset_index(&browser->b); 79 } 80 81 static char tree__folded_sign(bool unfolded) 82 { 83 return unfolded ? '-' : '+'; 84 } 85 86 static char map_symbol__folded(const struct map_symbol *ms) 87 { 88 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' '; 89 } 90 91 static char hist_entry__folded(const struct hist_entry *he) 92 { 93 return map_symbol__folded(&he->ms); 94 } 95 96 static char callchain_list__folded(const struct callchain_list *cl) 97 { 98 return map_symbol__folded(&cl->ms); 99 } 100 101 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold) 102 { 103 ms->unfolded = unfold ? ms->has_children : false; 104 } 105 106 static int callchain_node__count_rows_rb_tree(struct callchain_node *node) 107 { 108 int n = 0; 109 struct rb_node *nd; 110 111 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 112 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 113 struct callchain_list *chain; 114 char folded_sign = ' '; /* No children */ 115 116 list_for_each_entry(chain, &child->val, list) { 117 ++n; 118 /* We need this because we may not have children */ 119 folded_sign = callchain_list__folded(chain); 120 if (folded_sign == '+') 121 break; 122 } 123 124 if (folded_sign == '-') /* Have children and they're unfolded */ 125 n += callchain_node__count_rows_rb_tree(child); 126 } 127 128 return n; 129 } 130 131 static int callchain_node__count_rows(struct callchain_node *node) 132 { 133 struct callchain_list *chain; 134 bool unfolded = false; 135 int n = 0; 136 137 list_for_each_entry(chain, &node->val, list) { 138 ++n; 139 unfolded = chain->ms.unfolded; 140 } 141 142 if (unfolded) 143 n += callchain_node__count_rows_rb_tree(node); 144 145 return n; 146 } 147 148 static int callchain__count_rows(struct rb_root *chain) 149 { 150 struct rb_node *nd; 151 int n = 0; 152 153 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 154 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 155 n += callchain_node__count_rows(node); 156 } 157 158 return n; 159 } 160 161 static bool map_symbol__toggle_fold(struct map_symbol *ms) 162 { 163 if (!ms) 164 return false; 165 166 if (!ms->has_children) 167 return false; 168 169 ms->unfolded = !ms->unfolded; 170 return true; 171 } 172 173 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node) 174 { 175 struct rb_node *nd = rb_first(&node->rb_root); 176 177 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 178 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 179 struct callchain_list *chain; 180 bool first = true; 181 182 list_for_each_entry(chain, &child->val, list) { 183 if (first) { 184 first = false; 185 chain->ms.has_children = chain->list.next != &child->val || 186 !RB_EMPTY_ROOT(&child->rb_root); 187 } else 188 chain->ms.has_children = chain->list.next == &child->val && 189 !RB_EMPTY_ROOT(&child->rb_root); 190 } 191 192 callchain_node__init_have_children_rb_tree(child); 193 } 194 } 195 196 static void callchain_node__init_have_children(struct callchain_node *node) 197 { 198 struct callchain_list *chain; 199 200 list_for_each_entry(chain, &node->val, list) 201 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); 202 203 callchain_node__init_have_children_rb_tree(node); 204 } 205 206 static void callchain__init_have_children(struct rb_root *root) 207 { 208 struct rb_node *nd; 209 210 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 211 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 212 callchain_node__init_have_children(node); 213 } 214 } 215 216 static void hist_entry__init_have_children(struct hist_entry *he) 217 { 218 if (!he->init_have_children) { 219 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 220 callchain__init_have_children(&he->sorted_chain); 221 he->init_have_children = true; 222 } 223 } 224 225 static bool hist_browser__toggle_fold(struct hist_browser *browser) 226 { 227 if (map_symbol__toggle_fold(browser->selection)) { 228 struct hist_entry *he = browser->he_selection; 229 230 hist_entry__init_have_children(he); 231 browser->b.nr_entries -= he->nr_rows; 232 browser->nr_callchain_rows -= he->nr_rows; 233 234 if (he->ms.unfolded) 235 he->nr_rows = callchain__count_rows(&he->sorted_chain); 236 else 237 he->nr_rows = 0; 238 239 browser->b.nr_entries += he->nr_rows; 240 browser->nr_callchain_rows += he->nr_rows; 241 242 return true; 243 } 244 245 /* If it doesn't have children, no toggling performed */ 246 return false; 247 } 248 249 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold) 250 { 251 int n = 0; 252 struct rb_node *nd; 253 254 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 255 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 256 struct callchain_list *chain; 257 bool has_children = false; 258 259 list_for_each_entry(chain, &child->val, list) { 260 ++n; 261 map_symbol__set_folding(&chain->ms, unfold); 262 has_children = chain->ms.has_children; 263 } 264 265 if (has_children) 266 n += callchain_node__set_folding_rb_tree(child, unfold); 267 } 268 269 return n; 270 } 271 272 static int callchain_node__set_folding(struct callchain_node *node, bool unfold) 273 { 274 struct callchain_list *chain; 275 bool has_children = false; 276 int n = 0; 277 278 list_for_each_entry(chain, &node->val, list) { 279 ++n; 280 map_symbol__set_folding(&chain->ms, unfold); 281 has_children = chain->ms.has_children; 282 } 283 284 if (has_children) 285 n += callchain_node__set_folding_rb_tree(node, unfold); 286 287 return n; 288 } 289 290 static int callchain__set_folding(struct rb_root *chain, bool unfold) 291 { 292 struct rb_node *nd; 293 int n = 0; 294 295 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 296 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 297 n += callchain_node__set_folding(node, unfold); 298 } 299 300 return n; 301 } 302 303 static void hist_entry__set_folding(struct hist_entry *he, bool unfold) 304 { 305 hist_entry__init_have_children(he); 306 map_symbol__set_folding(&he->ms, unfold); 307 308 if (he->ms.has_children) { 309 int n = callchain__set_folding(&he->sorted_chain, unfold); 310 he->nr_rows = unfold ? n : 0; 311 } else 312 he->nr_rows = 0; 313 } 314 315 static void 316 __hist_browser__set_folding(struct hist_browser *browser, bool unfold) 317 { 318 struct rb_node *nd; 319 struct hists *hists = browser->hists; 320 321 for (nd = rb_first(&hists->entries); 322 (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL; 323 nd = rb_next(nd)) { 324 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 325 hist_entry__set_folding(he, unfold); 326 browser->nr_callchain_rows += he->nr_rows; 327 } 328 } 329 330 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 331 { 332 browser->nr_callchain_rows = 0; 333 __hist_browser__set_folding(browser, unfold); 334 335 browser->b.nr_entries = hist_browser__nr_entries(browser); 336 /* Go to the start, we may be way after valid entries after a collapse */ 337 ui_browser__reset_index(&browser->b); 338 } 339 340 static void ui_browser__warn_lost_events(struct ui_browser *browser) 341 { 342 ui_browser__warning(browser, 4, 343 "Events are being lost, check IO/CPU overload!\n\n" 344 "You may want to run 'perf' using a RT scheduler policy:\n\n" 345 " perf top -r 80\n\n" 346 "Or reduce the sampling frequency."); 347 } 348 349 static int hist_browser__run(struct hist_browser *browser, const char *ev_name, 350 struct hist_browser_timer *hbt) 351 { 352 int key; 353 char title[160]; 354 int delay_secs = hbt ? hbt->refresh : 0; 355 356 browser->b.entries = &browser->hists->entries; 357 browser->b.nr_entries = hist_browser__nr_entries(browser); 358 359 hist_browser__refresh_dimensions(browser); 360 hists__browser_title(browser->hists, title, sizeof(title), ev_name); 361 362 if (ui_browser__show(&browser->b, title, 363 "Press '?' for help on key bindings") < 0) 364 return -1; 365 366 while (1) { 367 key = ui_browser__run(&browser->b, delay_secs); 368 369 switch (key) { 370 case K_TIMER: { 371 u64 nr_entries; 372 hbt->timer(hbt->arg); 373 374 if (hist_browser__has_filter(browser)) 375 hist_browser__update_nr_entries(browser); 376 377 nr_entries = hist_browser__nr_entries(browser); 378 ui_browser__update_nr_entries(&browser->b, nr_entries); 379 380 if (browser->hists->stats.nr_lost_warned != 381 browser->hists->stats.nr_events[PERF_RECORD_LOST]) { 382 browser->hists->stats.nr_lost_warned = 383 browser->hists->stats.nr_events[PERF_RECORD_LOST]; 384 ui_browser__warn_lost_events(&browser->b); 385 } 386 387 hists__browser_title(browser->hists, title, sizeof(title), ev_name); 388 ui_browser__show_title(&browser->b, title); 389 continue; 390 } 391 case 'D': { /* Debug */ 392 static int seq; 393 struct hist_entry *h = rb_entry(browser->b.top, 394 struct hist_entry, rb_node); 395 ui_helpline__pop(); 396 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 397 seq++, browser->b.nr_entries, 398 browser->hists->nr_entries, 399 browser->b.height, 400 browser->b.index, 401 browser->b.top_idx, 402 h->row_offset, h->nr_rows); 403 } 404 break; 405 case 'C': 406 /* Collapse the whole world. */ 407 hist_browser__set_folding(browser, false); 408 break; 409 case 'E': 410 /* Expand the whole world. */ 411 hist_browser__set_folding(browser, true); 412 break; 413 case K_ENTER: 414 if (hist_browser__toggle_fold(browser)) 415 break; 416 /* fall thru */ 417 default: 418 goto out; 419 } 420 } 421 out: 422 ui_browser__hide(&browser->b); 423 return key; 424 } 425 426 static char *callchain_list__sym_name(struct callchain_list *cl, 427 char *bf, size_t bfsize, bool show_dso) 428 { 429 int printed; 430 431 if (cl->ms.sym) 432 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); 433 else 434 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); 435 436 if (show_dso) 437 scnprintf(bf + printed, bfsize - printed, " %s", 438 cl->ms.map ? cl->ms.map->dso->short_name : "unknown"); 439 440 return bf; 441 } 442 443 #define LEVEL_OFFSET_STEP 3 444 445 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser, 446 struct callchain_node *chain_node, 447 u64 total, int level, 448 unsigned short row, 449 off_t *row_offset, 450 bool *is_current_entry) 451 { 452 struct rb_node *node; 453 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; 454 u64 new_total, remaining; 455 456 if (callchain_param.mode == CHAIN_GRAPH_REL) 457 new_total = chain_node->children_hit; 458 else 459 new_total = total; 460 461 remaining = new_total; 462 node = rb_first(&chain_node->rb_root); 463 while (node) { 464 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 465 struct rb_node *next = rb_next(node); 466 u64 cumul = callchain_cumul_hits(child); 467 struct callchain_list *chain; 468 char folded_sign = ' '; 469 int first = true; 470 int extra_offset = 0; 471 472 remaining -= cumul; 473 474 list_for_each_entry(chain, &child->val, list) { 475 char bf[1024], *alloc_str; 476 const char *str; 477 int color; 478 bool was_first = first; 479 480 if (first) 481 first = false; 482 else 483 extra_offset = LEVEL_OFFSET_STEP; 484 485 folded_sign = callchain_list__folded(chain); 486 if (*row_offset != 0) { 487 --*row_offset; 488 goto do_next; 489 } 490 491 alloc_str = NULL; 492 str = callchain_list__sym_name(chain, bf, sizeof(bf), 493 browser->show_dso); 494 if (was_first) { 495 double percent = cumul * 100.0 / new_total; 496 497 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) 498 str = "Not enough memory!"; 499 else 500 str = alloc_str; 501 } 502 503 color = HE_COLORSET_NORMAL; 504 width = browser->b.width - (offset + extra_offset + 2); 505 if (ui_browser__is_current_entry(&browser->b, row)) { 506 browser->selection = &chain->ms; 507 color = HE_COLORSET_SELECTED; 508 *is_current_entry = true; 509 } 510 511 ui_browser__set_color(&browser->b, color); 512 ui_browser__gotorc(&browser->b, row, 0); 513 slsmg_write_nstring(" ", offset + extra_offset); 514 slsmg_printf("%c ", folded_sign); 515 slsmg_write_nstring(str, width); 516 free(alloc_str); 517 518 if (++row == browser->b.height) 519 goto out; 520 do_next: 521 if (folded_sign == '+') 522 break; 523 } 524 525 if (folded_sign == '-') { 526 const int new_level = level + (extra_offset ? 2 : 1); 527 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total, 528 new_level, row, row_offset, 529 is_current_entry); 530 } 531 if (row == browser->b.height) 532 goto out; 533 node = next; 534 } 535 out: 536 return row - first_row; 537 } 538 539 static int hist_browser__show_callchain_node(struct hist_browser *browser, 540 struct callchain_node *node, 541 int level, unsigned short row, 542 off_t *row_offset, 543 bool *is_current_entry) 544 { 545 struct callchain_list *chain; 546 int first_row = row, 547 offset = level * LEVEL_OFFSET_STEP, 548 width = browser->b.width - offset; 549 char folded_sign = ' '; 550 551 list_for_each_entry(chain, &node->val, list) { 552 char bf[1024], *s; 553 int color; 554 555 folded_sign = callchain_list__folded(chain); 556 557 if (*row_offset != 0) { 558 --*row_offset; 559 continue; 560 } 561 562 color = HE_COLORSET_NORMAL; 563 if (ui_browser__is_current_entry(&browser->b, row)) { 564 browser->selection = &chain->ms; 565 color = HE_COLORSET_SELECTED; 566 *is_current_entry = true; 567 } 568 569 s = callchain_list__sym_name(chain, bf, sizeof(bf), 570 browser->show_dso); 571 ui_browser__gotorc(&browser->b, row, 0); 572 ui_browser__set_color(&browser->b, color); 573 slsmg_write_nstring(" ", offset); 574 slsmg_printf("%c ", folded_sign); 575 slsmg_write_nstring(s, width - 2); 576 577 if (++row == browser->b.height) 578 goto out; 579 } 580 581 if (folded_sign == '-') 582 row += hist_browser__show_callchain_node_rb_tree(browser, node, 583 browser->hists->stats.total_period, 584 level + 1, row, 585 row_offset, 586 is_current_entry); 587 out: 588 return row - first_row; 589 } 590 591 static int hist_browser__show_callchain(struct hist_browser *browser, 592 struct rb_root *chain, 593 int level, unsigned short row, 594 off_t *row_offset, 595 bool *is_current_entry) 596 { 597 struct rb_node *nd; 598 int first_row = row; 599 600 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 601 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 602 603 row += hist_browser__show_callchain_node(browser, node, level, 604 row, row_offset, 605 is_current_entry); 606 if (row == browser->b.height) 607 break; 608 } 609 610 return row - first_row; 611 } 612 613 struct hpp_arg { 614 struct ui_browser *b; 615 char folded_sign; 616 bool current_entry; 617 }; 618 619 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 620 { 621 struct hpp_arg *arg = hpp->ptr; 622 int ret; 623 va_list args; 624 double percent; 625 626 va_start(args, fmt); 627 percent = va_arg(args, double); 628 va_end(args); 629 630 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 631 632 ret = scnprintf(hpp->buf, hpp->size, fmt, percent); 633 slsmg_printf("%s", hpp->buf); 634 635 advance_hpp(hpp, ret); 636 return ret; 637 } 638 639 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 640 static u64 __hpp_get_##_field(struct hist_entry *he) \ 641 { \ 642 return he->stat._field; \ 643 } \ 644 \ 645 static int \ 646 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ 647 struct perf_hpp *hpp, \ 648 struct hist_entry *he) \ 649 { \ 650 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ 651 __hpp__slsmg_color_printf, true); \ 652 } 653 654 __HPP_COLOR_PERCENT_FN(overhead, period) 655 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 656 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 657 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 658 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 659 660 #undef __HPP_COLOR_PERCENT_FN 661 662 void hist_browser__init_hpp(void) 663 { 664 perf_hpp__format[PERF_HPP__OVERHEAD].color = 665 hist_browser__hpp_color_overhead; 666 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 667 hist_browser__hpp_color_overhead_sys; 668 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 669 hist_browser__hpp_color_overhead_us; 670 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 671 hist_browser__hpp_color_overhead_guest_sys; 672 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 673 hist_browser__hpp_color_overhead_guest_us; 674 } 675 676 static int hist_browser__show_entry(struct hist_browser *browser, 677 struct hist_entry *entry, 678 unsigned short row) 679 { 680 char s[256]; 681 int printed = 0; 682 int width = browser->b.width; 683 char folded_sign = ' '; 684 bool current_entry = ui_browser__is_current_entry(&browser->b, row); 685 off_t row_offset = entry->row_offset; 686 bool first = true; 687 struct perf_hpp_fmt *fmt; 688 689 if (current_entry) { 690 browser->he_selection = entry; 691 browser->selection = &entry->ms; 692 } 693 694 if (symbol_conf.use_callchain) { 695 hist_entry__init_have_children(entry); 696 folded_sign = hist_entry__folded(entry); 697 } 698 699 if (row_offset == 0) { 700 struct hpp_arg arg = { 701 .b = &browser->b, 702 .folded_sign = folded_sign, 703 .current_entry = current_entry, 704 }; 705 struct perf_hpp hpp = { 706 .buf = s, 707 .size = sizeof(s), 708 .ptr = &arg, 709 }; 710 711 ui_browser__gotorc(&browser->b, row, 0); 712 713 perf_hpp__for_each_format(fmt) { 714 if (perf_hpp__should_skip(fmt)) 715 continue; 716 717 if (current_entry && browser->b.navkeypressed) { 718 ui_browser__set_color(&browser->b, 719 HE_COLORSET_SELECTED); 720 } else { 721 ui_browser__set_color(&browser->b, 722 HE_COLORSET_NORMAL); 723 } 724 725 if (first) { 726 if (symbol_conf.use_callchain) { 727 slsmg_printf("%c ", folded_sign); 728 width -= 2; 729 } 730 first = false; 731 } else { 732 slsmg_printf(" "); 733 width -= 2; 734 } 735 736 if (fmt->color) { 737 width -= fmt->color(fmt, &hpp, entry); 738 } else { 739 width -= fmt->entry(fmt, &hpp, entry); 740 slsmg_printf("%s", s); 741 } 742 } 743 744 /* The scroll bar isn't being used */ 745 if (!browser->b.navkeypressed) 746 width += 1; 747 748 slsmg_write_nstring("", width); 749 750 ++row; 751 ++printed; 752 } else 753 --row_offset; 754 755 if (folded_sign == '-' && row != browser->b.height) { 756 printed += hist_browser__show_callchain(browser, &entry->sorted_chain, 757 1, row, &row_offset, 758 ¤t_entry); 759 if (current_entry) 760 browser->he_selection = entry; 761 } 762 763 return printed; 764 } 765 766 static void ui_browser__hists_init_top(struct ui_browser *browser) 767 { 768 if (browser->top == NULL) { 769 struct hist_browser *hb; 770 771 hb = container_of(browser, struct hist_browser, b); 772 browser->top = rb_first(&hb->hists->entries); 773 } 774 } 775 776 static unsigned int hist_browser__refresh(struct ui_browser *browser) 777 { 778 unsigned row = 0; 779 struct rb_node *nd; 780 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 781 782 ui_browser__hists_init_top(browser); 783 784 for (nd = browser->top; nd; nd = rb_next(nd)) { 785 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 786 u64 total = hists__total_period(h->hists); 787 float percent = 0.0; 788 789 if (h->filtered) 790 continue; 791 792 if (total) 793 percent = h->stat.period * 100.0 / total; 794 795 if (percent < hb->min_pcnt) 796 continue; 797 798 row += hist_browser__show_entry(hb, h, row); 799 if (row == browser->height) 800 break; 801 } 802 803 return row; 804 } 805 806 static struct rb_node *hists__filter_entries(struct rb_node *nd, 807 struct hists *hists, 808 float min_pcnt) 809 { 810 while (nd != NULL) { 811 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 812 u64 total = hists__total_period(hists); 813 float percent = 0.0; 814 815 if (total) 816 percent = h->stat.period * 100.0 / total; 817 818 if (!h->filtered && percent >= min_pcnt) 819 return nd; 820 821 nd = rb_next(nd); 822 } 823 824 return NULL; 825 } 826 827 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 828 struct hists *hists, 829 float min_pcnt) 830 { 831 while (nd != NULL) { 832 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 833 u64 total = hists__total_period(hists); 834 float percent = 0.0; 835 836 if (total) 837 percent = h->stat.period * 100.0 / total; 838 839 if (!h->filtered && percent >= min_pcnt) 840 return nd; 841 842 nd = rb_prev(nd); 843 } 844 845 return NULL; 846 } 847 848 static void ui_browser__hists_seek(struct ui_browser *browser, 849 off_t offset, int whence) 850 { 851 struct hist_entry *h; 852 struct rb_node *nd; 853 bool first = true; 854 struct hist_browser *hb; 855 856 hb = container_of(browser, struct hist_browser, b); 857 858 if (browser->nr_entries == 0) 859 return; 860 861 ui_browser__hists_init_top(browser); 862 863 switch (whence) { 864 case SEEK_SET: 865 nd = hists__filter_entries(rb_first(browser->entries), 866 hb->hists, hb->min_pcnt); 867 break; 868 case SEEK_CUR: 869 nd = browser->top; 870 goto do_offset; 871 case SEEK_END: 872 nd = hists__filter_prev_entries(rb_last(browser->entries), 873 hb->hists, hb->min_pcnt); 874 first = false; 875 break; 876 default: 877 return; 878 } 879 880 /* 881 * Moves not relative to the first visible entry invalidates its 882 * row_offset: 883 */ 884 h = rb_entry(browser->top, struct hist_entry, rb_node); 885 h->row_offset = 0; 886 887 /* 888 * Here we have to check if nd is expanded (+), if it is we can't go 889 * the next top level hist_entry, instead we must compute an offset of 890 * what _not_ to show and not change the first visible entry. 891 * 892 * This offset increments when we are going from top to bottom and 893 * decreases when we're going from bottom to top. 894 * 895 * As we don't have backpointers to the top level in the callchains 896 * structure, we need to always print the whole hist_entry callchain, 897 * skipping the first ones that are before the first visible entry 898 * and stop when we printed enough lines to fill the screen. 899 */ 900 do_offset: 901 if (offset > 0) { 902 do { 903 h = rb_entry(nd, struct hist_entry, rb_node); 904 if (h->ms.unfolded) { 905 u16 remaining = h->nr_rows - h->row_offset; 906 if (offset > remaining) { 907 offset -= remaining; 908 h->row_offset = 0; 909 } else { 910 h->row_offset += offset; 911 offset = 0; 912 browser->top = nd; 913 break; 914 } 915 } 916 nd = hists__filter_entries(rb_next(nd), hb->hists, 917 hb->min_pcnt); 918 if (nd == NULL) 919 break; 920 --offset; 921 browser->top = nd; 922 } while (offset != 0); 923 } else if (offset < 0) { 924 while (1) { 925 h = rb_entry(nd, struct hist_entry, rb_node); 926 if (h->ms.unfolded) { 927 if (first) { 928 if (-offset > h->row_offset) { 929 offset += h->row_offset; 930 h->row_offset = 0; 931 } else { 932 h->row_offset += offset; 933 offset = 0; 934 browser->top = nd; 935 break; 936 } 937 } else { 938 if (-offset > h->nr_rows) { 939 offset += h->nr_rows; 940 h->row_offset = 0; 941 } else { 942 h->row_offset = h->nr_rows + offset; 943 offset = 0; 944 browser->top = nd; 945 break; 946 } 947 } 948 } 949 950 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists, 951 hb->min_pcnt); 952 if (nd == NULL) 953 break; 954 ++offset; 955 browser->top = nd; 956 if (offset == 0) { 957 /* 958 * Last unfiltered hist_entry, check if it is 959 * unfolded, if it is then we should have 960 * row_offset at its last entry. 961 */ 962 h = rb_entry(nd, struct hist_entry, rb_node); 963 if (h->ms.unfolded) 964 h->row_offset = h->nr_rows; 965 break; 966 } 967 first = false; 968 } 969 } else { 970 browser->top = nd; 971 h = rb_entry(nd, struct hist_entry, rb_node); 972 h->row_offset = 0; 973 } 974 } 975 976 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, 977 struct callchain_node *chain_node, 978 u64 total, int level, 979 FILE *fp) 980 { 981 struct rb_node *node; 982 int offset = level * LEVEL_OFFSET_STEP; 983 u64 new_total, remaining; 984 int printed = 0; 985 986 if (callchain_param.mode == CHAIN_GRAPH_REL) 987 new_total = chain_node->children_hit; 988 else 989 new_total = total; 990 991 remaining = new_total; 992 node = rb_first(&chain_node->rb_root); 993 while (node) { 994 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 995 struct rb_node *next = rb_next(node); 996 u64 cumul = callchain_cumul_hits(child); 997 struct callchain_list *chain; 998 char folded_sign = ' '; 999 int first = true; 1000 int extra_offset = 0; 1001 1002 remaining -= cumul; 1003 1004 list_for_each_entry(chain, &child->val, list) { 1005 char bf[1024], *alloc_str; 1006 const char *str; 1007 bool was_first = first; 1008 1009 if (first) 1010 first = false; 1011 else 1012 extra_offset = LEVEL_OFFSET_STEP; 1013 1014 folded_sign = callchain_list__folded(chain); 1015 1016 alloc_str = NULL; 1017 str = callchain_list__sym_name(chain, bf, sizeof(bf), 1018 browser->show_dso); 1019 if (was_first) { 1020 double percent = cumul * 100.0 / new_total; 1021 1022 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) 1023 str = "Not enough memory!"; 1024 else 1025 str = alloc_str; 1026 } 1027 1028 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); 1029 free(alloc_str); 1030 if (folded_sign == '+') 1031 break; 1032 } 1033 1034 if (folded_sign == '-') { 1035 const int new_level = level + (extra_offset ? 2 : 1); 1036 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, 1037 new_level, fp); 1038 } 1039 1040 node = next; 1041 } 1042 1043 return printed; 1044 } 1045 1046 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, 1047 struct callchain_node *node, 1048 int level, FILE *fp) 1049 { 1050 struct callchain_list *chain; 1051 int offset = level * LEVEL_OFFSET_STEP; 1052 char folded_sign = ' '; 1053 int printed = 0; 1054 1055 list_for_each_entry(chain, &node->val, list) { 1056 char bf[1024], *s; 1057 1058 folded_sign = callchain_list__folded(chain); 1059 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso); 1060 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); 1061 } 1062 1063 if (folded_sign == '-') 1064 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, 1065 browser->hists->stats.total_period, 1066 level + 1, fp); 1067 return printed; 1068 } 1069 1070 static int hist_browser__fprintf_callchain(struct hist_browser *browser, 1071 struct rb_root *chain, int level, FILE *fp) 1072 { 1073 struct rb_node *nd; 1074 int printed = 0; 1075 1076 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 1077 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 1078 1079 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); 1080 } 1081 1082 return printed; 1083 } 1084 1085 static int hist_browser__fprintf_entry(struct hist_browser *browser, 1086 struct hist_entry *he, FILE *fp) 1087 { 1088 char s[8192]; 1089 int printed = 0; 1090 char folded_sign = ' '; 1091 struct perf_hpp hpp = { 1092 .buf = s, 1093 .size = sizeof(s), 1094 }; 1095 struct perf_hpp_fmt *fmt; 1096 bool first = true; 1097 int ret; 1098 1099 if (symbol_conf.use_callchain) 1100 folded_sign = hist_entry__folded(he); 1101 1102 if (symbol_conf.use_callchain) 1103 printed += fprintf(fp, "%c ", folded_sign); 1104 1105 perf_hpp__for_each_format(fmt) { 1106 if (perf_hpp__should_skip(fmt)) 1107 continue; 1108 1109 if (!first) { 1110 ret = scnprintf(hpp.buf, hpp.size, " "); 1111 advance_hpp(&hpp, ret); 1112 } else 1113 first = false; 1114 1115 ret = fmt->entry(fmt, &hpp, he); 1116 advance_hpp(&hpp, ret); 1117 } 1118 printed += fprintf(fp, "%s\n", rtrim(s)); 1119 1120 if (folded_sign == '-') 1121 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); 1122 1123 return printed; 1124 } 1125 1126 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 1127 { 1128 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 1129 browser->hists, 1130 browser->min_pcnt); 1131 int printed = 0; 1132 1133 while (nd) { 1134 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1135 1136 printed += hist_browser__fprintf_entry(browser, h, fp); 1137 nd = hists__filter_entries(rb_next(nd), browser->hists, 1138 browser->min_pcnt); 1139 } 1140 1141 return printed; 1142 } 1143 1144 static int hist_browser__dump(struct hist_browser *browser) 1145 { 1146 char filename[64]; 1147 FILE *fp; 1148 1149 while (1) { 1150 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 1151 if (access(filename, F_OK)) 1152 break; 1153 /* 1154 * XXX: Just an arbitrary lazy upper limit 1155 */ 1156 if (++browser->print_seq == 8192) { 1157 ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 1158 return -1; 1159 } 1160 } 1161 1162 fp = fopen(filename, "w"); 1163 if (fp == NULL) { 1164 char bf[64]; 1165 const char *err = strerror_r(errno, bf, sizeof(bf)); 1166 ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 1167 return -1; 1168 } 1169 1170 ++browser->print_seq; 1171 hist_browser__fprintf(browser, fp); 1172 fclose(fp); 1173 ui_helpline__fpush("%s written!", filename); 1174 1175 return 0; 1176 } 1177 1178 static struct hist_browser *hist_browser__new(struct hists *hists) 1179 { 1180 struct hist_browser *browser = zalloc(sizeof(*browser)); 1181 1182 if (browser) { 1183 browser->hists = hists; 1184 browser->b.refresh = hist_browser__refresh; 1185 browser->b.seek = ui_browser__hists_seek; 1186 browser->b.use_navkeypressed = true; 1187 } 1188 1189 return browser; 1190 } 1191 1192 static void hist_browser__delete(struct hist_browser *browser) 1193 { 1194 free(browser); 1195 } 1196 1197 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 1198 { 1199 return browser->he_selection; 1200 } 1201 1202 static struct thread *hist_browser__selected_thread(struct hist_browser *browser) 1203 { 1204 return browser->he_selection->thread; 1205 } 1206 1207 static int hists__browser_title(struct hists *hists, char *bf, size_t size, 1208 const char *ev_name) 1209 { 1210 char unit; 1211 int printed; 1212 const struct dso *dso = hists->dso_filter; 1213 const struct thread *thread = hists->thread_filter; 1214 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1215 u64 nr_events = hists->stats.total_period; 1216 struct perf_evsel *evsel = hists_to_evsel(hists); 1217 char buf[512]; 1218 size_t buflen = sizeof(buf); 1219 1220 if (symbol_conf.filter_relative) { 1221 nr_samples = hists->stats.nr_non_filtered_samples; 1222 nr_events = hists->stats.total_non_filtered_period; 1223 } 1224 1225 if (perf_evsel__is_group_event(evsel)) { 1226 struct perf_evsel *pos; 1227 1228 perf_evsel__group_desc(evsel, buf, buflen); 1229 ev_name = buf; 1230 1231 for_each_group_member(pos, evsel) { 1232 if (symbol_conf.filter_relative) { 1233 nr_samples += pos->hists.stats.nr_non_filtered_samples; 1234 nr_events += pos->hists.stats.total_non_filtered_period; 1235 } else { 1236 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1237 nr_events += pos->hists.stats.total_period; 1238 } 1239 } 1240 } 1241 1242 nr_samples = convert_unit(nr_samples, &unit); 1243 printed = scnprintf(bf, size, 1244 "Samples: %lu%c of event '%s', Event count (approx.): %lu", 1245 nr_samples, unit, ev_name, nr_events); 1246 1247 1248 if (hists->uid_filter_str) 1249 printed += snprintf(bf + printed, size - printed, 1250 ", UID: %s", hists->uid_filter_str); 1251 if (thread) 1252 printed += scnprintf(bf + printed, size - printed, 1253 ", Thread: %s(%d)", 1254 (thread->comm_set ? thread__comm_str(thread) : ""), 1255 thread->tid); 1256 if (dso) 1257 printed += scnprintf(bf + printed, size - printed, 1258 ", DSO: %s", dso->short_name); 1259 return printed; 1260 } 1261 1262 static inline void free_popup_options(char **options, int n) 1263 { 1264 int i; 1265 1266 for (i = 0; i < n; ++i) 1267 zfree(&options[i]); 1268 } 1269 1270 /* Check whether the browser is for 'top' or 'report' */ 1271 static inline bool is_report_browser(void *timer) 1272 { 1273 return timer == NULL; 1274 } 1275 1276 /* 1277 * Only runtime switching of perf data file will make "input_name" point 1278 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 1279 * whether we need to call free() for current "input_name" during the switch. 1280 */ 1281 static bool is_input_name_malloced = false; 1282 1283 static int switch_data_file(void) 1284 { 1285 char *pwd, *options[32], *abs_path[32], *tmp; 1286 DIR *pwd_dir; 1287 int nr_options = 0, choice = -1, ret = -1; 1288 struct dirent *dent; 1289 1290 pwd = getenv("PWD"); 1291 if (!pwd) 1292 return ret; 1293 1294 pwd_dir = opendir(pwd); 1295 if (!pwd_dir) 1296 return ret; 1297 1298 memset(options, 0, sizeof(options)); 1299 memset(options, 0, sizeof(abs_path)); 1300 1301 while ((dent = readdir(pwd_dir))) { 1302 char path[PATH_MAX]; 1303 u64 magic; 1304 char *name = dent->d_name; 1305 FILE *file; 1306 1307 if (!(dent->d_type == DT_REG)) 1308 continue; 1309 1310 snprintf(path, sizeof(path), "%s/%s", pwd, name); 1311 1312 file = fopen(path, "r"); 1313 if (!file) 1314 continue; 1315 1316 if (fread(&magic, 1, 8, file) < 8) 1317 goto close_file_and_continue; 1318 1319 if (is_perf_magic(magic)) { 1320 options[nr_options] = strdup(name); 1321 if (!options[nr_options]) 1322 goto close_file_and_continue; 1323 1324 abs_path[nr_options] = strdup(path); 1325 if (!abs_path[nr_options]) { 1326 zfree(&options[nr_options]); 1327 ui__warning("Can't search all data files due to memory shortage.\n"); 1328 fclose(file); 1329 break; 1330 } 1331 1332 nr_options++; 1333 } 1334 1335 close_file_and_continue: 1336 fclose(file); 1337 if (nr_options >= 32) { 1338 ui__warning("Too many perf data files in PWD!\n" 1339 "Only the first 32 files will be listed.\n"); 1340 break; 1341 } 1342 } 1343 closedir(pwd_dir); 1344 1345 if (nr_options) { 1346 choice = ui__popup_menu(nr_options, options); 1347 if (choice < nr_options && choice >= 0) { 1348 tmp = strdup(abs_path[choice]); 1349 if (tmp) { 1350 if (is_input_name_malloced) 1351 free((void *)input_name); 1352 input_name = tmp; 1353 is_input_name_malloced = true; 1354 ret = 0; 1355 } else 1356 ui__warning("Data switch failed due to memory shortage!\n"); 1357 } 1358 } 1359 1360 free_popup_options(options, nr_options); 1361 free_popup_options(abs_path, nr_options); 1362 return ret; 1363 } 1364 1365 static void hist_browser__update_nr_entries(struct hist_browser *hb) 1366 { 1367 u64 nr_entries = 0; 1368 struct rb_node *nd = rb_first(&hb->hists->entries); 1369 1370 if (hb->min_pcnt == 0) { 1371 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 1372 return; 1373 } 1374 1375 while ((nd = hists__filter_entries(nd, hb->hists, 1376 hb->min_pcnt)) != NULL) { 1377 nr_entries++; 1378 nd = rb_next(nd); 1379 } 1380 1381 hb->nr_non_filtered_entries = nr_entries; 1382 } 1383 1384 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1385 const char *helpline, const char *ev_name, 1386 bool left_exits, 1387 struct hist_browser_timer *hbt, 1388 float min_pcnt, 1389 struct perf_session_env *env) 1390 { 1391 struct hists *hists = &evsel->hists; 1392 struct hist_browser *browser = hist_browser__new(hists); 1393 struct branch_info *bi; 1394 struct pstack *fstack; 1395 char *options[16]; 1396 int nr_options = 0; 1397 int key = -1; 1398 char buf[64]; 1399 char script_opt[64]; 1400 int delay_secs = hbt ? hbt->refresh : 0; 1401 1402 #define HIST_BROWSER_HELP_COMMON \ 1403 "h/?/F1 Show this window\n" \ 1404 "UP/DOWN/PGUP\n" \ 1405 "PGDN/SPACE Navigate\n" \ 1406 "q/ESC/CTRL+C Exit browser\n\n" \ 1407 "For multiple event sessions:\n\n" \ 1408 "TAB/UNTAB Switch events\n\n" \ 1409 "For symbolic views (--sort has sym):\n\n" \ 1410 "-> Zoom into DSO/Threads & Annotate current symbol\n" \ 1411 "<- Zoom out\n" \ 1412 "a Annotate current symbol\n" \ 1413 "C Collapse all callchains\n" \ 1414 "d Zoom into current DSO\n" \ 1415 "E Expand all callchains\n" \ 1416 "F Toggle percentage of filtered entries\n" \ 1417 1418 /* help messages are sorted by lexical order of the hotkey */ 1419 const char report_help[] = HIST_BROWSER_HELP_COMMON 1420 "i Show header information\n" 1421 "P Print histograms to perf.hist.N\n" 1422 "r Run available scripts\n" 1423 "s Switch to another data file in PWD\n" 1424 "t Zoom into current Thread\n" 1425 "V Verbose (DSO names in callchains, etc)\n" 1426 "/ Filter symbol by name"; 1427 const char top_help[] = HIST_BROWSER_HELP_COMMON 1428 "P Print histograms to perf.hist.N\n" 1429 "t Zoom into current Thread\n" 1430 "V Verbose (DSO names in callchains, etc)\n" 1431 "/ Filter symbol by name"; 1432 1433 if (browser == NULL) 1434 return -1; 1435 1436 if (min_pcnt) { 1437 browser->min_pcnt = min_pcnt; 1438 hist_browser__update_nr_entries(browser); 1439 } 1440 1441 fstack = pstack__new(2); 1442 if (fstack == NULL) 1443 goto out; 1444 1445 ui_helpline__push(helpline); 1446 1447 memset(options, 0, sizeof(options)); 1448 1449 while (1) { 1450 const struct thread *thread = NULL; 1451 const struct dso *dso = NULL; 1452 int choice = 0, 1453 annotate = -2, zoom_dso = -2, zoom_thread = -2, 1454 annotate_f = -2, annotate_t = -2, browse_map = -2; 1455 int scripts_comm = -2, scripts_symbol = -2, 1456 scripts_all = -2, switch_data = -2; 1457 1458 nr_options = 0; 1459 1460 key = hist_browser__run(browser, ev_name, hbt); 1461 1462 if (browser->he_selection != NULL) { 1463 thread = hist_browser__selected_thread(browser); 1464 dso = browser->selection->map ? browser->selection->map->dso : NULL; 1465 } 1466 switch (key) { 1467 case K_TAB: 1468 case K_UNTAB: 1469 if (nr_events == 1) 1470 continue; 1471 /* 1472 * Exit the browser, let hists__browser_tree 1473 * go to the next or previous 1474 */ 1475 goto out_free_stack; 1476 case 'a': 1477 if (!sort__has_sym) { 1478 ui_browser__warning(&browser->b, delay_secs * 2, 1479 "Annotation is only available for symbolic views, " 1480 "include \"sym*\" in --sort to use it."); 1481 continue; 1482 } 1483 1484 if (browser->selection == NULL || 1485 browser->selection->sym == NULL || 1486 browser->selection->map->dso->annotate_warned) 1487 continue; 1488 goto do_annotate; 1489 case 'P': 1490 hist_browser__dump(browser); 1491 continue; 1492 case 'd': 1493 goto zoom_dso; 1494 case 'V': 1495 browser->show_dso = !browser->show_dso; 1496 continue; 1497 case 't': 1498 goto zoom_thread; 1499 case '/': 1500 if (ui_browser__input_window("Symbol to show", 1501 "Please enter the name of symbol you want to see", 1502 buf, "ENTER: OK, ESC: Cancel", 1503 delay_secs * 2) == K_ENTER) { 1504 hists->symbol_filter_str = *buf ? buf : NULL; 1505 hists__filter_by_symbol(hists); 1506 hist_browser__reset(browser); 1507 } 1508 continue; 1509 case 'r': 1510 if (is_report_browser(hbt)) 1511 goto do_scripts; 1512 continue; 1513 case 's': 1514 if (is_report_browser(hbt)) 1515 goto do_data_switch; 1516 continue; 1517 case 'i': 1518 /* env->arch is NULL for live-mode (i.e. perf top) */ 1519 if (env->arch) 1520 tui__header_window(env); 1521 continue; 1522 case 'F': 1523 symbol_conf.filter_relative ^= 1; 1524 continue; 1525 case K_F1: 1526 case 'h': 1527 case '?': 1528 ui_browser__help_window(&browser->b, 1529 is_report_browser(hbt) ? report_help : top_help); 1530 continue; 1531 case K_ENTER: 1532 case K_RIGHT: 1533 /* menu */ 1534 break; 1535 case K_LEFT: { 1536 const void *top; 1537 1538 if (pstack__empty(fstack)) { 1539 /* 1540 * Go back to the perf_evsel_menu__run or other user 1541 */ 1542 if (left_exits) 1543 goto out_free_stack; 1544 continue; 1545 } 1546 top = pstack__pop(fstack); 1547 if (top == &browser->hists->dso_filter) 1548 goto zoom_out_dso; 1549 if (top == &browser->hists->thread_filter) 1550 goto zoom_out_thread; 1551 continue; 1552 } 1553 case K_ESC: 1554 if (!left_exits && 1555 !ui_browser__dialog_yesno(&browser->b, 1556 "Do you really want to exit?")) 1557 continue; 1558 /* Fall thru */ 1559 case 'q': 1560 case CTRL('c'): 1561 goto out_free_stack; 1562 default: 1563 continue; 1564 } 1565 1566 if (!sort__has_sym) 1567 goto add_exit_option; 1568 1569 if (sort__mode == SORT_MODE__BRANCH) { 1570 bi = browser->he_selection->branch_info; 1571 if (browser->selection != NULL && 1572 bi && 1573 bi->from.sym != NULL && 1574 !bi->from.map->dso->annotate_warned && 1575 asprintf(&options[nr_options], "Annotate %s", 1576 bi->from.sym->name) > 0) 1577 annotate_f = nr_options++; 1578 1579 if (browser->selection != NULL && 1580 bi && 1581 bi->to.sym != NULL && 1582 !bi->to.map->dso->annotate_warned && 1583 (bi->to.sym != bi->from.sym || 1584 bi->to.map->dso != bi->from.map->dso) && 1585 asprintf(&options[nr_options], "Annotate %s", 1586 bi->to.sym->name) > 0) 1587 annotate_t = nr_options++; 1588 } else { 1589 1590 if (browser->selection != NULL && 1591 browser->selection->sym != NULL && 1592 !browser->selection->map->dso->annotate_warned && 1593 asprintf(&options[nr_options], "Annotate %s", 1594 browser->selection->sym->name) > 0) 1595 annotate = nr_options++; 1596 } 1597 1598 if (thread != NULL && 1599 asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 1600 (browser->hists->thread_filter ? "out of" : "into"), 1601 (thread->comm_set ? thread__comm_str(thread) : ""), 1602 thread->tid) > 0) 1603 zoom_thread = nr_options++; 1604 1605 if (dso != NULL && 1606 asprintf(&options[nr_options], "Zoom %s %s DSO", 1607 (browser->hists->dso_filter ? "out of" : "into"), 1608 (dso->kernel ? "the Kernel" : dso->short_name)) > 0) 1609 zoom_dso = nr_options++; 1610 1611 if (browser->selection != NULL && 1612 browser->selection->map != NULL && 1613 asprintf(&options[nr_options], "Browse map details") > 0) 1614 browse_map = nr_options++; 1615 1616 /* perf script support */ 1617 if (browser->he_selection) { 1618 struct symbol *sym; 1619 1620 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", 1621 thread__comm_str(browser->he_selection->thread)) > 0) 1622 scripts_comm = nr_options++; 1623 1624 sym = browser->he_selection->ms.sym; 1625 if (sym && sym->namelen && 1626 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", 1627 sym->name) > 0) 1628 scripts_symbol = nr_options++; 1629 } 1630 1631 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) 1632 scripts_all = nr_options++; 1633 1634 if (is_report_browser(hbt) && asprintf(&options[nr_options], 1635 "Switch to another data file in PWD") > 0) 1636 switch_data = nr_options++; 1637 add_exit_option: 1638 options[nr_options++] = (char *)"Exit"; 1639 retry_popup_menu: 1640 choice = ui__popup_menu(nr_options, options); 1641 1642 if (choice == nr_options - 1) 1643 break; 1644 1645 if (choice == -1) { 1646 free_popup_options(options, nr_options - 1); 1647 continue; 1648 } 1649 1650 if (choice == annotate || choice == annotate_t || choice == annotate_f) { 1651 struct hist_entry *he; 1652 int err; 1653 do_annotate: 1654 if (!objdump_path && perf_session_env__lookup_objdump(env)) 1655 continue; 1656 1657 he = hist_browser__selected_entry(browser); 1658 if (he == NULL) 1659 continue; 1660 1661 /* 1662 * we stash the branch_info symbol + map into the 1663 * the ms so we don't have to rewrite all the annotation 1664 * code to use branch_info. 1665 * in branch mode, the ms struct is not used 1666 */ 1667 if (choice == annotate_f) { 1668 he->ms.sym = he->branch_info->from.sym; 1669 he->ms.map = he->branch_info->from.map; 1670 } else if (choice == annotate_t) { 1671 he->ms.sym = he->branch_info->to.sym; 1672 he->ms.map = he->branch_info->to.map; 1673 } 1674 1675 /* 1676 * Don't let this be freed, say, by hists__decay_entry. 1677 */ 1678 he->used = true; 1679 err = hist_entry__tui_annotate(he, evsel, hbt); 1680 he->used = false; 1681 /* 1682 * offer option to annotate the other branch source or target 1683 * (if they exists) when returning from annotate 1684 */ 1685 if ((err == 'q' || err == CTRL('c')) 1686 && annotate_t != -2 && annotate_f != -2) 1687 goto retry_popup_menu; 1688 1689 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1690 if (err) 1691 ui_browser__handle_resize(&browser->b); 1692 1693 } else if (choice == browse_map) 1694 map__browse(browser->selection->map); 1695 else if (choice == zoom_dso) { 1696 zoom_dso: 1697 if (browser->hists->dso_filter) { 1698 pstack__remove(fstack, &browser->hists->dso_filter); 1699 zoom_out_dso: 1700 ui_helpline__pop(); 1701 browser->hists->dso_filter = NULL; 1702 sort_dso.elide = false; 1703 } else { 1704 if (dso == NULL) 1705 continue; 1706 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1707 dso->kernel ? "the Kernel" : dso->short_name); 1708 browser->hists->dso_filter = dso; 1709 sort_dso.elide = true; 1710 pstack__push(fstack, &browser->hists->dso_filter); 1711 } 1712 hists__filter_by_dso(hists); 1713 hist_browser__reset(browser); 1714 } else if (choice == zoom_thread) { 1715 zoom_thread: 1716 if (browser->hists->thread_filter) { 1717 pstack__remove(fstack, &browser->hists->thread_filter); 1718 zoom_out_thread: 1719 ui_helpline__pop(); 1720 browser->hists->thread_filter = NULL; 1721 sort_thread.elide = false; 1722 } else { 1723 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1724 thread->comm_set ? thread__comm_str(thread) : "", 1725 thread->tid); 1726 browser->hists->thread_filter = thread; 1727 sort_thread.elide = true; 1728 pstack__push(fstack, &browser->hists->thread_filter); 1729 } 1730 hists__filter_by_thread(hists); 1731 hist_browser__reset(browser); 1732 } 1733 /* perf scripts support */ 1734 else if (choice == scripts_all || choice == scripts_comm || 1735 choice == scripts_symbol) { 1736 do_scripts: 1737 memset(script_opt, 0, 64); 1738 1739 if (choice == scripts_comm) 1740 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); 1741 1742 if (choice == scripts_symbol) 1743 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); 1744 1745 script_browse(script_opt); 1746 } 1747 /* Switch to another data file */ 1748 else if (choice == switch_data) { 1749 do_data_switch: 1750 if (!switch_data_file()) { 1751 key = K_SWITCH_INPUT_DATA; 1752 break; 1753 } else 1754 ui__warning("Won't switch the data files due to\n" 1755 "no valid data file get selected!\n"); 1756 } 1757 } 1758 out_free_stack: 1759 pstack__delete(fstack); 1760 out: 1761 hist_browser__delete(browser); 1762 free_popup_options(options, nr_options - 1); 1763 return key; 1764 } 1765 1766 struct perf_evsel_menu { 1767 struct ui_browser b; 1768 struct perf_evsel *selection; 1769 bool lost_events, lost_events_warned; 1770 float min_pcnt; 1771 struct perf_session_env *env; 1772 }; 1773 1774 static void perf_evsel_menu__write(struct ui_browser *browser, 1775 void *entry, int row) 1776 { 1777 struct perf_evsel_menu *menu = container_of(browser, 1778 struct perf_evsel_menu, b); 1779 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 1780 bool current_entry = ui_browser__is_current_entry(browser, row); 1781 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1782 const char *ev_name = perf_evsel__name(evsel); 1783 char bf[256], unit; 1784 const char *warn = " "; 1785 size_t printed; 1786 1787 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 1788 HE_COLORSET_NORMAL); 1789 1790 if (perf_evsel__is_group_event(evsel)) { 1791 struct perf_evsel *pos; 1792 1793 ev_name = perf_evsel__group_name(evsel); 1794 1795 for_each_group_member(pos, evsel) { 1796 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1797 } 1798 } 1799 1800 nr_events = convert_unit(nr_events, &unit); 1801 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 1802 unit, unit == ' ' ? "" : " ", ev_name); 1803 slsmg_printf("%s", bf); 1804 1805 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; 1806 if (nr_events != 0) { 1807 menu->lost_events = true; 1808 if (!current_entry) 1809 ui_browser__set_color(browser, HE_COLORSET_TOP); 1810 nr_events = convert_unit(nr_events, &unit); 1811 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 1812 nr_events, unit, unit == ' ' ? "" : " "); 1813 warn = bf; 1814 } 1815 1816 slsmg_write_nstring(warn, browser->width - printed); 1817 1818 if (current_entry) 1819 menu->selection = evsel; 1820 } 1821 1822 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 1823 int nr_events, const char *help, 1824 struct hist_browser_timer *hbt) 1825 { 1826 struct perf_evlist *evlist = menu->b.priv; 1827 struct perf_evsel *pos; 1828 const char *ev_name, *title = "Available samples"; 1829 int delay_secs = hbt ? hbt->refresh : 0; 1830 int key; 1831 1832 if (ui_browser__show(&menu->b, title, 1833 "ESC: exit, ENTER|->: Browse histograms") < 0) 1834 return -1; 1835 1836 while (1) { 1837 key = ui_browser__run(&menu->b, delay_secs); 1838 1839 switch (key) { 1840 case K_TIMER: 1841 hbt->timer(hbt->arg); 1842 1843 if (!menu->lost_events_warned && menu->lost_events) { 1844 ui_browser__warn_lost_events(&menu->b); 1845 menu->lost_events_warned = true; 1846 } 1847 continue; 1848 case K_RIGHT: 1849 case K_ENTER: 1850 if (!menu->selection) 1851 continue; 1852 pos = menu->selection; 1853 browse_hists: 1854 perf_evlist__set_selected(evlist, pos); 1855 /* 1856 * Give the calling tool a chance to populate the non 1857 * default evsel resorted hists tree. 1858 */ 1859 if (hbt) 1860 hbt->timer(hbt->arg); 1861 ev_name = perf_evsel__name(pos); 1862 key = perf_evsel__hists_browse(pos, nr_events, help, 1863 ev_name, true, hbt, 1864 menu->min_pcnt, 1865 menu->env); 1866 ui_browser__show_title(&menu->b, title); 1867 switch (key) { 1868 case K_TAB: 1869 if (pos->node.next == &evlist->entries) 1870 pos = perf_evlist__first(evlist); 1871 else 1872 pos = perf_evsel__next(pos); 1873 goto browse_hists; 1874 case K_UNTAB: 1875 if (pos->node.prev == &evlist->entries) 1876 pos = perf_evlist__last(evlist); 1877 else 1878 pos = perf_evsel__prev(pos); 1879 goto browse_hists; 1880 case K_ESC: 1881 if (!ui_browser__dialog_yesno(&menu->b, 1882 "Do you really want to exit?")) 1883 continue; 1884 /* Fall thru */ 1885 case K_SWITCH_INPUT_DATA: 1886 case 'q': 1887 case CTRL('c'): 1888 goto out; 1889 default: 1890 continue; 1891 } 1892 case K_LEFT: 1893 continue; 1894 case K_ESC: 1895 if (!ui_browser__dialog_yesno(&menu->b, 1896 "Do you really want to exit?")) 1897 continue; 1898 /* Fall thru */ 1899 case 'q': 1900 case CTRL('c'): 1901 goto out; 1902 default: 1903 continue; 1904 } 1905 } 1906 1907 out: 1908 ui_browser__hide(&menu->b); 1909 return key; 1910 } 1911 1912 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 1913 void *entry) 1914 { 1915 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 1916 1917 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 1918 return true; 1919 1920 return false; 1921 } 1922 1923 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1924 int nr_entries, const char *help, 1925 struct hist_browser_timer *hbt, 1926 float min_pcnt, 1927 struct perf_session_env *env) 1928 { 1929 struct perf_evsel *pos; 1930 struct perf_evsel_menu menu = { 1931 .b = { 1932 .entries = &evlist->entries, 1933 .refresh = ui_browser__list_head_refresh, 1934 .seek = ui_browser__list_head_seek, 1935 .write = perf_evsel_menu__write, 1936 .filter = filter_group_entries, 1937 .nr_entries = nr_entries, 1938 .priv = evlist, 1939 }, 1940 .min_pcnt = min_pcnt, 1941 .env = env, 1942 }; 1943 1944 ui_helpline__push("Press ESC to exit"); 1945 1946 evlist__for_each(evlist, pos) { 1947 const char *ev_name = perf_evsel__name(pos); 1948 size_t line_len = strlen(ev_name) + 7; 1949 1950 if (menu.b.width < line_len) 1951 menu.b.width = line_len; 1952 } 1953 1954 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 1955 } 1956 1957 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 1958 struct hist_browser_timer *hbt, 1959 float min_pcnt, 1960 struct perf_session_env *env) 1961 { 1962 int nr_entries = evlist->nr_entries; 1963 1964 single_entry: 1965 if (nr_entries == 1) { 1966 struct perf_evsel *first = perf_evlist__first(evlist); 1967 const char *ev_name = perf_evsel__name(first); 1968 1969 return perf_evsel__hists_browse(first, nr_entries, help, 1970 ev_name, false, hbt, min_pcnt, 1971 env); 1972 } 1973 1974 if (symbol_conf.event_group) { 1975 struct perf_evsel *pos; 1976 1977 nr_entries = 0; 1978 evlist__for_each(evlist, pos) { 1979 if (perf_evsel__is_group_leader(pos)) 1980 nr_entries++; 1981 } 1982 1983 if (nr_entries == 1) 1984 goto single_entry; 1985 } 1986 1987 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 1988 hbt, min_pcnt, env); 1989 } 1990