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