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