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