1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <linux/rbtree.h> 5 6 #include "../../util/evsel.h" 7 #include "../../util/evlist.h" 8 #include "../../util/hist.h" 9 #include "../../util/pstack.h" 10 #include "../../util/sort.h" 11 #include "../../util/util.h" 12 #include "../../util/top.h" 13 #include "../../arch/common.h" 14 15 #include "../browser.h" 16 #include "../helpline.h" 17 #include "../util.h" 18 #include "../ui.h" 19 #include "map.h" 20 #include "annotate.h" 21 22 struct hist_browser { 23 struct ui_browser b; 24 struct hists *hists; 25 struct hist_entry *he_selection; 26 struct map_symbol *selection; 27 struct hist_browser_timer *hbt; 28 struct pstack *pstack; 29 struct perf_env *env; 30 int print_seq; 31 bool show_dso; 32 bool show_headers; 33 float min_pcnt; 34 u64 nr_non_filtered_entries; 35 u64 nr_callchain_rows; 36 }; 37 38 extern void hist_browser__init_hpp(void); 39 40 static int hists__browser_title(struct hists *hists, 41 struct hist_browser_timer *hbt, 42 char *bf, size_t size); 43 static void hist_browser__update_nr_entries(struct hist_browser *hb); 44 45 static struct rb_node *hists__filter_entries(struct rb_node *nd, 46 float min_pcnt); 47 48 static bool hist_browser__has_filter(struct hist_browser *hb) 49 { 50 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter; 51 } 52 53 static int hist_browser__get_folding(struct hist_browser *browser) 54 { 55 struct rb_node *nd; 56 struct hists *hists = browser->hists; 57 int unfolded_rows = 0; 58 59 for (nd = rb_first(&hists->entries); 60 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 61 nd = rb_next(nd)) { 62 struct hist_entry *he = 63 rb_entry(nd, struct hist_entry, rb_node); 64 65 if (he->unfolded) 66 unfolded_rows += he->nr_rows; 67 } 68 return unfolded_rows; 69 } 70 71 static u32 hist_browser__nr_entries(struct hist_browser *hb) 72 { 73 u32 nr_entries; 74 75 if (hist_browser__has_filter(hb)) 76 nr_entries = hb->nr_non_filtered_entries; 77 else 78 nr_entries = hb->hists->nr_entries; 79 80 hb->nr_callchain_rows = hist_browser__get_folding(hb); 81 return nr_entries + hb->nr_callchain_rows; 82 } 83 84 static void hist_browser__update_rows(struct hist_browser *hb) 85 { 86 struct ui_browser *browser = &hb->b; 87 u16 header_offset = hb->show_headers ? 1 : 0, index_row; 88 89 browser->rows = browser->height - header_offset; 90 /* 91 * Verify if we were at the last line and that line isn't 92 * visibe because we now show the header line(s). 93 */ 94 index_row = browser->index - browser->top_idx; 95 if (index_row >= browser->rows) 96 browser->index -= index_row - browser->rows + 1; 97 } 98 99 static void hist_browser__refresh_dimensions(struct ui_browser *browser) 100 { 101 struct hist_browser *hb = container_of(browser, struct hist_browser, b); 102 103 /* 3 == +/- toggle symbol before actual hist_entry rendering */ 104 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]")); 105 /* 106 * FIXME: Just keeping existing behaviour, but this really should be 107 * before updating browser->width, as it will invalidate the 108 * calculation above. Fix this and the fallout in another 109 * changeset. 110 */ 111 ui_browser__refresh_dimensions(browser); 112 hist_browser__update_rows(hb); 113 } 114 115 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column) 116 { 117 u16 header_offset = browser->show_headers ? 1 : 0; 118 119 ui_browser__gotorc(&browser->b, row + header_offset, column); 120 } 121 122 static void hist_browser__reset(struct hist_browser *browser) 123 { 124 /* 125 * The hists__remove_entry_filter() already folds non-filtered 126 * entries so we can assume it has 0 callchain rows. 127 */ 128 browser->nr_callchain_rows = 0; 129 130 hist_browser__update_nr_entries(browser); 131 browser->b.nr_entries = hist_browser__nr_entries(browser); 132 hist_browser__refresh_dimensions(&browser->b); 133 ui_browser__reset_index(&browser->b); 134 } 135 136 static char tree__folded_sign(bool unfolded) 137 { 138 return unfolded ? '-' : '+'; 139 } 140 141 static char hist_entry__folded(const struct hist_entry *he) 142 { 143 return he->has_children ? tree__folded_sign(he->unfolded) : ' '; 144 } 145 146 static char callchain_list__folded(const struct callchain_list *cl) 147 { 148 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; 149 } 150 151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) 152 { 153 cl->unfolded = unfold ? cl->has_children : false; 154 } 155 156 static int callchain_node__count_rows_rb_tree(struct callchain_node *node) 157 { 158 int n = 0; 159 struct rb_node *nd; 160 161 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 162 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 163 struct callchain_list *chain; 164 char folded_sign = ' '; /* No children */ 165 166 list_for_each_entry(chain, &child->val, list) { 167 ++n; 168 /* We need this because we may not have children */ 169 folded_sign = callchain_list__folded(chain); 170 if (folded_sign == '+') 171 break; 172 } 173 174 if (folded_sign == '-') /* Have children and they're unfolded */ 175 n += callchain_node__count_rows_rb_tree(child); 176 } 177 178 return n; 179 } 180 181 static int callchain_node__count_rows(struct callchain_node *node) 182 { 183 struct callchain_list *chain; 184 bool unfolded = false; 185 int n = 0; 186 187 list_for_each_entry(chain, &node->val, list) { 188 ++n; 189 unfolded = chain->unfolded; 190 } 191 192 if (unfolded) 193 n += callchain_node__count_rows_rb_tree(node); 194 195 return n; 196 } 197 198 static int callchain__count_rows(struct rb_root *chain) 199 { 200 struct rb_node *nd; 201 int n = 0; 202 203 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 204 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 205 n += callchain_node__count_rows(node); 206 } 207 208 return n; 209 } 210 211 static bool hist_entry__toggle_fold(struct hist_entry *he) 212 { 213 if (!he) 214 return false; 215 216 if (!he->has_children) 217 return false; 218 219 he->unfolded = !he->unfolded; 220 return true; 221 } 222 223 static bool callchain_list__toggle_fold(struct callchain_list *cl) 224 { 225 if (!cl) 226 return false; 227 228 if (!cl->has_children) 229 return false; 230 231 cl->unfolded = !cl->unfolded; 232 return true; 233 } 234 235 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node) 236 { 237 struct rb_node *nd = rb_first(&node->rb_root); 238 239 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 240 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 241 struct callchain_list *chain; 242 bool first = true; 243 244 list_for_each_entry(chain, &child->val, list) { 245 if (first) { 246 first = false; 247 chain->has_children = chain->list.next != &child->val || 248 !RB_EMPTY_ROOT(&child->rb_root); 249 } else 250 chain->has_children = chain->list.next == &child->val && 251 !RB_EMPTY_ROOT(&child->rb_root); 252 } 253 254 callchain_node__init_have_children_rb_tree(child); 255 } 256 } 257 258 static void callchain_node__init_have_children(struct callchain_node *node, 259 bool has_sibling) 260 { 261 struct callchain_list *chain; 262 263 chain = list_entry(node->val.next, struct callchain_list, list); 264 chain->has_children = has_sibling; 265 266 if (!list_empty(&node->val)) { 267 chain = list_entry(node->val.prev, struct callchain_list, list); 268 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); 269 } 270 271 callchain_node__init_have_children_rb_tree(node); 272 } 273 274 static void callchain__init_have_children(struct rb_root *root) 275 { 276 struct rb_node *nd = rb_first(root); 277 bool has_sibling = nd && rb_next(nd); 278 279 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 280 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 281 callchain_node__init_have_children(node, has_sibling); 282 } 283 } 284 285 static void hist_entry__init_have_children(struct hist_entry *he) 286 { 287 if (!he->init_have_children) { 288 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 289 callchain__init_have_children(&he->sorted_chain); 290 he->init_have_children = true; 291 } 292 } 293 294 static bool hist_browser__toggle_fold(struct hist_browser *browser) 295 { 296 struct hist_entry *he = browser->he_selection; 297 struct map_symbol *ms = browser->selection; 298 struct callchain_list *cl = container_of(ms, struct callchain_list, ms); 299 bool has_children; 300 301 if (ms == &he->ms) 302 has_children = hist_entry__toggle_fold(he); 303 else 304 has_children = callchain_list__toggle_fold(cl); 305 306 if (has_children) { 307 hist_entry__init_have_children(he); 308 browser->b.nr_entries -= he->nr_rows; 309 browser->nr_callchain_rows -= he->nr_rows; 310 311 if (he->unfolded) 312 he->nr_rows = callchain__count_rows(&he->sorted_chain); 313 else 314 he->nr_rows = 0; 315 316 browser->b.nr_entries += he->nr_rows; 317 browser->nr_callchain_rows += he->nr_rows; 318 319 return true; 320 } 321 322 /* If it doesn't have children, no toggling performed */ 323 return false; 324 } 325 326 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold) 327 { 328 int n = 0; 329 struct rb_node *nd; 330 331 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 332 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 333 struct callchain_list *chain; 334 bool has_children = false; 335 336 list_for_each_entry(chain, &child->val, list) { 337 ++n; 338 callchain_list__set_folding(chain, unfold); 339 has_children = chain->has_children; 340 } 341 342 if (has_children) 343 n += callchain_node__set_folding_rb_tree(child, unfold); 344 } 345 346 return n; 347 } 348 349 static int callchain_node__set_folding(struct callchain_node *node, bool unfold) 350 { 351 struct callchain_list *chain; 352 bool has_children = false; 353 int n = 0; 354 355 list_for_each_entry(chain, &node->val, list) { 356 ++n; 357 callchain_list__set_folding(chain, unfold); 358 has_children = chain->has_children; 359 } 360 361 if (has_children) 362 n += callchain_node__set_folding_rb_tree(node, unfold); 363 364 return n; 365 } 366 367 static int callchain__set_folding(struct rb_root *chain, bool unfold) 368 { 369 struct rb_node *nd; 370 int n = 0; 371 372 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 373 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 374 n += callchain_node__set_folding(node, unfold); 375 } 376 377 return n; 378 } 379 380 static void hist_entry__set_folding(struct hist_entry *he, bool unfold) 381 { 382 hist_entry__init_have_children(he); 383 he->unfolded = unfold ? he->has_children : false; 384 385 if (he->has_children) { 386 int n = callchain__set_folding(&he->sorted_chain, unfold); 387 he->nr_rows = unfold ? n : 0; 388 } else 389 he->nr_rows = 0; 390 } 391 392 static void 393 __hist_browser__set_folding(struct hist_browser *browser, bool unfold) 394 { 395 struct rb_node *nd; 396 struct hists *hists = browser->hists; 397 398 for (nd = rb_first(&hists->entries); 399 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 400 nd = rb_next(nd)) { 401 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 402 hist_entry__set_folding(he, unfold); 403 browser->nr_callchain_rows += he->nr_rows; 404 } 405 } 406 407 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 408 { 409 browser->nr_callchain_rows = 0; 410 __hist_browser__set_folding(browser, unfold); 411 412 browser->b.nr_entries = hist_browser__nr_entries(browser); 413 /* Go to the start, we may be way after valid entries after a collapse */ 414 ui_browser__reset_index(&browser->b); 415 } 416 417 static void ui_browser__warn_lost_events(struct ui_browser *browser) 418 { 419 ui_browser__warning(browser, 4, 420 "Events are being lost, check IO/CPU overload!\n\n" 421 "You may want to run 'perf' using a RT scheduler policy:\n\n" 422 " perf top -r 80\n\n" 423 "Or reduce the sampling frequency."); 424 } 425 426 static int hist_browser__run(struct hist_browser *browser, const char *help) 427 { 428 int key; 429 char title[160]; 430 struct hist_browser_timer *hbt = browser->hbt; 431 int delay_secs = hbt ? hbt->refresh : 0; 432 433 browser->b.entries = &browser->hists->entries; 434 browser->b.nr_entries = hist_browser__nr_entries(browser); 435 436 hists__browser_title(browser->hists, hbt, title, sizeof(title)); 437 438 if (ui_browser__show(&browser->b, title, help) < 0) 439 return -1; 440 441 while (1) { 442 key = ui_browser__run(&browser->b, delay_secs); 443 444 switch (key) { 445 case K_TIMER: { 446 u64 nr_entries; 447 hbt->timer(hbt->arg); 448 449 if (hist_browser__has_filter(browser)) 450 hist_browser__update_nr_entries(browser); 451 452 nr_entries = hist_browser__nr_entries(browser); 453 ui_browser__update_nr_entries(&browser->b, nr_entries); 454 455 if (browser->hists->stats.nr_lost_warned != 456 browser->hists->stats.nr_events[PERF_RECORD_LOST]) { 457 browser->hists->stats.nr_lost_warned = 458 browser->hists->stats.nr_events[PERF_RECORD_LOST]; 459 ui_browser__warn_lost_events(&browser->b); 460 } 461 462 hists__browser_title(browser->hists, 463 hbt, title, sizeof(title)); 464 ui_browser__show_title(&browser->b, title); 465 continue; 466 } 467 case 'D': { /* Debug */ 468 static int seq; 469 struct hist_entry *h = rb_entry(browser->b.top, 470 struct hist_entry, rb_node); 471 ui_helpline__pop(); 472 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 473 seq++, browser->b.nr_entries, 474 browser->hists->nr_entries, 475 browser->b.rows, 476 browser->b.index, 477 browser->b.top_idx, 478 h->row_offset, h->nr_rows); 479 } 480 break; 481 case 'C': 482 /* Collapse the whole world. */ 483 hist_browser__set_folding(browser, false); 484 break; 485 case 'E': 486 /* Expand the whole world. */ 487 hist_browser__set_folding(browser, true); 488 break; 489 case 'H': 490 browser->show_headers = !browser->show_headers; 491 hist_browser__update_rows(browser); 492 break; 493 case K_ENTER: 494 if (hist_browser__toggle_fold(browser)) 495 break; 496 /* fall thru */ 497 default: 498 goto out; 499 } 500 } 501 out: 502 ui_browser__hide(&browser->b); 503 return key; 504 } 505 506 struct callchain_print_arg { 507 /* for hists browser */ 508 off_t row_offset; 509 bool is_current_entry; 510 511 /* for file dump */ 512 FILE *fp; 513 int printed; 514 }; 515 516 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, 517 struct callchain_list *chain, 518 const char *str, int offset, 519 unsigned short row, 520 struct callchain_print_arg *arg); 521 522 static void hist_browser__show_callchain_entry(struct hist_browser *browser, 523 struct callchain_list *chain, 524 const char *str, int offset, 525 unsigned short row, 526 struct callchain_print_arg *arg) 527 { 528 int color, width; 529 char folded_sign = callchain_list__folded(chain); 530 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src; 531 532 color = HE_COLORSET_NORMAL; 533 width = browser->b.width - (offset + 2); 534 if (ui_browser__is_current_entry(&browser->b, row)) { 535 browser->selection = &chain->ms; 536 color = HE_COLORSET_SELECTED; 537 arg->is_current_entry = true; 538 } 539 540 ui_browser__set_color(&browser->b, color); 541 hist_browser__gotorc(browser, row, 0); 542 ui_browser__write_nstring(&browser->b, " ", offset); 543 ui_browser__printf(&browser->b, "%c", folded_sign); 544 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' '); 545 ui_browser__write_nstring(&browser->b, str, width); 546 } 547 548 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, 549 struct callchain_list *chain, 550 const char *str, int offset, 551 unsigned short row __maybe_unused, 552 struct callchain_print_arg *arg) 553 { 554 char folded_sign = callchain_list__folded(chain); 555 556 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", 557 folded_sign, str); 558 } 559 560 typedef bool (*check_output_full_fn)(struct hist_browser *browser, 561 unsigned short row); 562 563 static bool hist_browser__check_output_full(struct hist_browser *browser, 564 unsigned short row) 565 { 566 return browser->b.rows == row; 567 } 568 569 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, 570 unsigned short row __maybe_unused) 571 { 572 return false; 573 } 574 575 #define LEVEL_OFFSET_STEP 3 576 577 static int hist_browser__show_callchain(struct hist_browser *browser, 578 struct rb_root *root, int level, 579 unsigned short row, u64 total, 580 print_callchain_entry_fn print, 581 struct callchain_print_arg *arg, 582 check_output_full_fn is_output_full) 583 { 584 struct rb_node *node; 585 int first_row = row, offset = level * LEVEL_OFFSET_STEP; 586 u64 new_total; 587 bool need_percent; 588 589 node = rb_first(root); 590 need_percent = node && rb_next(node); 591 592 while (node) { 593 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 594 struct rb_node *next = rb_next(node); 595 u64 cumul = callchain_cumul_hits(child); 596 struct callchain_list *chain; 597 char folded_sign = ' '; 598 int first = true; 599 int extra_offset = 0; 600 601 list_for_each_entry(chain, &child->val, list) { 602 char bf[1024], *alloc_str; 603 const char *str; 604 bool was_first = first; 605 606 if (first) 607 first = false; 608 else if (need_percent) 609 extra_offset = LEVEL_OFFSET_STEP; 610 611 folded_sign = callchain_list__folded(chain); 612 if (arg->row_offset != 0) { 613 arg->row_offset--; 614 goto do_next; 615 } 616 617 alloc_str = NULL; 618 str = callchain_list__sym_name(chain, bf, sizeof(bf), 619 browser->show_dso); 620 621 if (was_first && need_percent) { 622 double percent = cumul * 100.0 / total; 623 624 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) 625 str = "Not enough memory!"; 626 else 627 str = alloc_str; 628 } 629 630 print(browser, chain, str, offset + extra_offset, row, arg); 631 632 free(alloc_str); 633 634 if (is_output_full(browser, ++row)) 635 goto out; 636 do_next: 637 if (folded_sign == '+') 638 break; 639 } 640 641 if (folded_sign == '-') { 642 const int new_level = level + (extra_offset ? 2 : 1); 643 644 if (callchain_param.mode == CHAIN_GRAPH_REL) 645 new_total = child->children_hit; 646 else 647 new_total = total; 648 649 row += hist_browser__show_callchain(browser, &child->rb_root, 650 new_level, row, new_total, 651 print, arg, is_output_full); 652 } 653 if (is_output_full(browser, row)) 654 break; 655 node = next; 656 } 657 out: 658 return row - first_row; 659 } 660 661 struct hpp_arg { 662 struct ui_browser *b; 663 char folded_sign; 664 bool current_entry; 665 }; 666 667 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 668 { 669 struct hpp_arg *arg = hpp->ptr; 670 int ret, len; 671 va_list args; 672 double percent; 673 674 va_start(args, fmt); 675 len = va_arg(args, int); 676 percent = va_arg(args, double); 677 va_end(args); 678 679 ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 680 681 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 682 ui_browser__printf(arg->b, "%s", hpp->buf); 683 684 advance_hpp(hpp, ret); 685 return ret; 686 } 687 688 #define __HPP_COLOR_PERCENT_FN(_type, _field) \ 689 static u64 __hpp_get_##_field(struct hist_entry *he) \ 690 { \ 691 return he->stat._field; \ 692 } \ 693 \ 694 static int \ 695 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 696 struct perf_hpp *hpp, \ 697 struct hist_entry *he) \ 698 { \ 699 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 700 __hpp__slsmg_color_printf, true); \ 701 } 702 703 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 704 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 705 { \ 706 return he->stat_acc->_field; \ 707 } \ 708 \ 709 static int \ 710 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 711 struct perf_hpp *hpp, \ 712 struct hist_entry *he) \ 713 { \ 714 if (!symbol_conf.cumulate_callchain) { \ 715 struct hpp_arg *arg = hpp->ptr; \ 716 int len = fmt->user_len ?: fmt->len; \ 717 int ret = scnprintf(hpp->buf, hpp->size, \ 718 "%*s", len, "N/A"); \ 719 ui_browser__printf(arg->b, "%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 ui_browser__printf(&browser->b, "%c ", folded_sign); 805 width -= 2; 806 } 807 first = false; 808 } else { 809 ui_browser__printf(&browser->b, " "); 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 ui_browser__printf(&browser->b, "%s", s); 818 } 819 } 820 821 /* The scroll bar isn't being used */ 822 if (!browser->b.navkeypressed) 823 width += 1; 824 825 ui_browser__write_nstring(&browser->b, "", 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 ui_browser__write_nstring(&browser->b, 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_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 char ref[30] = " show reference callgraph, "; 1271 bool enable_ref = false; 1272 1273 if (symbol_conf.filter_relative) { 1274 nr_samples = hists->stats.nr_non_filtered_samples; 1275 nr_events = hists->stats.total_non_filtered_period; 1276 } 1277 1278 if (perf_evsel__is_group_event(evsel)) { 1279 struct perf_evsel *pos; 1280 1281 perf_evsel__group_desc(evsel, buf, buflen); 1282 ev_name = buf; 1283 1284 for_each_group_member(pos, evsel) { 1285 struct hists *pos_hists = evsel__hists(pos); 1286 1287 if (symbol_conf.filter_relative) { 1288 nr_samples += pos_hists->stats.nr_non_filtered_samples; 1289 nr_events += pos_hists->stats.total_non_filtered_period; 1290 } else { 1291 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1292 nr_events += pos_hists->stats.total_period; 1293 } 1294 } 1295 } 1296 1297 if (symbol_conf.show_ref_callgraph && 1298 strstr(ev_name, "call-graph=no")) 1299 enable_ref = true; 1300 nr_samples = convert_unit(nr_samples, &unit); 1301 printed = scnprintf(bf, size, 1302 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, 1303 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); 1304 1305 1306 if (hists->uid_filter_str) 1307 printed += snprintf(bf + printed, size - printed, 1308 ", UID: %s", hists->uid_filter_str); 1309 if (thread) 1310 printed += scnprintf(bf + printed, size - printed, 1311 ", Thread: %s(%d)", 1312 (thread->comm_set ? thread__comm_str(thread) : ""), 1313 thread->tid); 1314 if (dso) 1315 printed += scnprintf(bf + printed, size - printed, 1316 ", DSO: %s", dso->short_name); 1317 if (!is_report_browser(hbt)) { 1318 struct perf_top *top = hbt->arg; 1319 1320 if (top->zero) 1321 printed += scnprintf(bf + printed, size - printed, " [z]"); 1322 } 1323 1324 return printed; 1325 } 1326 1327 static inline void free_popup_options(char **options, int n) 1328 { 1329 int i; 1330 1331 for (i = 0; i < n; ++i) 1332 zfree(&options[i]); 1333 } 1334 1335 /* 1336 * Only runtime switching of perf data file will make "input_name" point 1337 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 1338 * whether we need to call free() for current "input_name" during the switch. 1339 */ 1340 static bool is_input_name_malloced = false; 1341 1342 static int switch_data_file(void) 1343 { 1344 char *pwd, *options[32], *abs_path[32], *tmp; 1345 DIR *pwd_dir; 1346 int nr_options = 0, choice = -1, ret = -1; 1347 struct dirent *dent; 1348 1349 pwd = getenv("PWD"); 1350 if (!pwd) 1351 return ret; 1352 1353 pwd_dir = opendir(pwd); 1354 if (!pwd_dir) 1355 return ret; 1356 1357 memset(options, 0, sizeof(options)); 1358 memset(options, 0, sizeof(abs_path)); 1359 1360 while ((dent = readdir(pwd_dir))) { 1361 char path[PATH_MAX]; 1362 u64 magic; 1363 char *name = dent->d_name; 1364 FILE *file; 1365 1366 if (!(dent->d_type == DT_REG)) 1367 continue; 1368 1369 snprintf(path, sizeof(path), "%s/%s", pwd, name); 1370 1371 file = fopen(path, "r"); 1372 if (!file) 1373 continue; 1374 1375 if (fread(&magic, 1, 8, file) < 8) 1376 goto close_file_and_continue; 1377 1378 if (is_perf_magic(magic)) { 1379 options[nr_options] = strdup(name); 1380 if (!options[nr_options]) 1381 goto close_file_and_continue; 1382 1383 abs_path[nr_options] = strdup(path); 1384 if (!abs_path[nr_options]) { 1385 zfree(&options[nr_options]); 1386 ui__warning("Can't search all data files due to memory shortage.\n"); 1387 fclose(file); 1388 break; 1389 } 1390 1391 nr_options++; 1392 } 1393 1394 close_file_and_continue: 1395 fclose(file); 1396 if (nr_options >= 32) { 1397 ui__warning("Too many perf data files in PWD!\n" 1398 "Only the first 32 files will be listed.\n"); 1399 break; 1400 } 1401 } 1402 closedir(pwd_dir); 1403 1404 if (nr_options) { 1405 choice = ui__popup_menu(nr_options, options); 1406 if (choice < nr_options && choice >= 0) { 1407 tmp = strdup(abs_path[choice]); 1408 if (tmp) { 1409 if (is_input_name_malloced) 1410 free((void *)input_name); 1411 input_name = tmp; 1412 is_input_name_malloced = true; 1413 ret = 0; 1414 } else 1415 ui__warning("Data switch failed due to memory shortage!\n"); 1416 } 1417 } 1418 1419 free_popup_options(options, nr_options); 1420 free_popup_options(abs_path, nr_options); 1421 return ret; 1422 } 1423 1424 struct popup_action { 1425 struct thread *thread; 1426 struct dso *dso; 1427 struct map_symbol ms; 1428 1429 int (*fn)(struct hist_browser *browser, struct popup_action *act); 1430 }; 1431 1432 static int 1433 do_annotate(struct hist_browser *browser, struct popup_action *act) 1434 { 1435 struct perf_evsel *evsel; 1436 struct annotation *notes; 1437 struct hist_entry *he; 1438 int err; 1439 1440 if (!objdump_path && perf_session_env__lookup_objdump(browser->env)) 1441 return 0; 1442 1443 notes = symbol__annotation(act->ms.sym); 1444 if (!notes->src) 1445 return 0; 1446 1447 evsel = hists_to_evsel(browser->hists); 1448 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); 1449 he = hist_browser__selected_entry(browser); 1450 /* 1451 * offer option to annotate the other branch source or target 1452 * (if they exists) when returning from annotate 1453 */ 1454 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 1455 return 1; 1456 1457 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1458 if (err) 1459 ui_browser__handle_resize(&browser->b); 1460 return 0; 1461 } 1462 1463 static int 1464 add_annotate_opt(struct hist_browser *browser __maybe_unused, 1465 struct popup_action *act, char **optstr, 1466 struct map *map, struct symbol *sym) 1467 { 1468 if (sym == NULL || map->dso->annotate_warned) 1469 return 0; 1470 1471 if (asprintf(optstr, "Annotate %s", sym->name) < 0) 1472 return 0; 1473 1474 act->ms.map = map; 1475 act->ms.sym = sym; 1476 act->fn = do_annotate; 1477 return 1; 1478 } 1479 1480 static int 1481 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 1482 { 1483 struct thread *thread = act->thread; 1484 1485 if (browser->hists->thread_filter) { 1486 pstack__remove(browser->pstack, &browser->hists->thread_filter); 1487 perf_hpp__set_elide(HISTC_THREAD, false); 1488 thread__zput(browser->hists->thread_filter); 1489 ui_helpline__pop(); 1490 } else { 1491 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1492 thread->comm_set ? thread__comm_str(thread) : "", 1493 thread->tid); 1494 browser->hists->thread_filter = thread__get(thread); 1495 perf_hpp__set_elide(HISTC_THREAD, false); 1496 pstack__push(browser->pstack, &browser->hists->thread_filter); 1497 } 1498 1499 hists__filter_by_thread(browser->hists); 1500 hist_browser__reset(browser); 1501 return 0; 1502 } 1503 1504 static int 1505 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 1506 char **optstr, struct thread *thread) 1507 { 1508 if (thread == NULL) 1509 return 0; 1510 1511 if (asprintf(optstr, "Zoom %s %s(%d) thread", 1512 browser->hists->thread_filter ? "out of" : "into", 1513 thread->comm_set ? thread__comm_str(thread) : "", 1514 thread->tid) < 0) 1515 return 0; 1516 1517 act->thread = thread; 1518 act->fn = do_zoom_thread; 1519 return 1; 1520 } 1521 1522 static int 1523 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 1524 { 1525 struct dso *dso = act->dso; 1526 1527 if (browser->hists->dso_filter) { 1528 pstack__remove(browser->pstack, &browser->hists->dso_filter); 1529 perf_hpp__set_elide(HISTC_DSO, false); 1530 browser->hists->dso_filter = NULL; 1531 ui_helpline__pop(); 1532 } else { 1533 if (dso == NULL) 1534 return 0; 1535 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1536 dso->kernel ? "the Kernel" : dso->short_name); 1537 browser->hists->dso_filter = dso; 1538 perf_hpp__set_elide(HISTC_DSO, true); 1539 pstack__push(browser->pstack, &browser->hists->dso_filter); 1540 } 1541 1542 hists__filter_by_dso(browser->hists); 1543 hist_browser__reset(browser); 1544 return 0; 1545 } 1546 1547 static int 1548 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 1549 char **optstr, struct dso *dso) 1550 { 1551 if (dso == NULL) 1552 return 0; 1553 1554 if (asprintf(optstr, "Zoom %s %s DSO", 1555 browser->hists->dso_filter ? "out of" : "into", 1556 dso->kernel ? "the Kernel" : dso->short_name) < 0) 1557 return 0; 1558 1559 act->dso = dso; 1560 act->fn = do_zoom_dso; 1561 return 1; 1562 } 1563 1564 static int 1565 do_browse_map(struct hist_browser *browser __maybe_unused, 1566 struct popup_action *act) 1567 { 1568 map__browse(act->ms.map); 1569 return 0; 1570 } 1571 1572 static int 1573 add_map_opt(struct hist_browser *browser __maybe_unused, 1574 struct popup_action *act, char **optstr, struct map *map) 1575 { 1576 if (map == NULL) 1577 return 0; 1578 1579 if (asprintf(optstr, "Browse map details") < 0) 1580 return 0; 1581 1582 act->ms.map = map; 1583 act->fn = do_browse_map; 1584 return 1; 1585 } 1586 1587 static int 1588 do_run_script(struct hist_browser *browser __maybe_unused, 1589 struct popup_action *act) 1590 { 1591 char script_opt[64]; 1592 memset(script_opt, 0, sizeof(script_opt)); 1593 1594 if (act->thread) { 1595 scnprintf(script_opt, sizeof(script_opt), " -c %s ", 1596 thread__comm_str(act->thread)); 1597 } else if (act->ms.sym) { 1598 scnprintf(script_opt, sizeof(script_opt), " -S %s ", 1599 act->ms.sym->name); 1600 } 1601 1602 script_browse(script_opt); 1603 return 0; 1604 } 1605 1606 static int 1607 add_script_opt(struct hist_browser *browser __maybe_unused, 1608 struct popup_action *act, char **optstr, 1609 struct thread *thread, struct symbol *sym) 1610 { 1611 if (thread) { 1612 if (asprintf(optstr, "Run scripts for samples of thread [%s]", 1613 thread__comm_str(thread)) < 0) 1614 return 0; 1615 } else if (sym) { 1616 if (asprintf(optstr, "Run scripts for samples of symbol [%s]", 1617 sym->name) < 0) 1618 return 0; 1619 } else { 1620 if (asprintf(optstr, "Run scripts for all samples") < 0) 1621 return 0; 1622 } 1623 1624 act->thread = thread; 1625 act->ms.sym = sym; 1626 act->fn = do_run_script; 1627 return 1; 1628 } 1629 1630 static int 1631 do_switch_data(struct hist_browser *browser __maybe_unused, 1632 struct popup_action *act __maybe_unused) 1633 { 1634 if (switch_data_file()) { 1635 ui__warning("Won't switch the data files due to\n" 1636 "no valid data file get selected!\n"); 1637 return 0; 1638 } 1639 1640 return K_SWITCH_INPUT_DATA; 1641 } 1642 1643 static int 1644 add_switch_opt(struct hist_browser *browser, 1645 struct popup_action *act, char **optstr) 1646 { 1647 if (!is_report_browser(browser->hbt)) 1648 return 0; 1649 1650 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 1651 return 0; 1652 1653 act->fn = do_switch_data; 1654 return 1; 1655 } 1656 1657 static int 1658 do_exit_browser(struct hist_browser *browser __maybe_unused, 1659 struct popup_action *act __maybe_unused) 1660 { 1661 return 0; 1662 } 1663 1664 static int 1665 add_exit_opt(struct hist_browser *browser __maybe_unused, 1666 struct popup_action *act, char **optstr) 1667 { 1668 if (asprintf(optstr, "Exit") < 0) 1669 return 0; 1670 1671 act->fn = do_exit_browser; 1672 return 1; 1673 } 1674 1675 static void hist_browser__update_nr_entries(struct hist_browser *hb) 1676 { 1677 u64 nr_entries = 0; 1678 struct rb_node *nd = rb_first(&hb->hists->entries); 1679 1680 if (hb->min_pcnt == 0) { 1681 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 1682 return; 1683 } 1684 1685 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 1686 nr_entries++; 1687 nd = rb_next(nd); 1688 } 1689 1690 hb->nr_non_filtered_entries = nr_entries; 1691 } 1692 1693 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1694 const char *helpline, 1695 bool left_exits, 1696 struct hist_browser_timer *hbt, 1697 float min_pcnt, 1698 struct perf_env *env) 1699 { 1700 struct hists *hists = evsel__hists(evsel); 1701 struct hist_browser *browser = hist_browser__new(hists, hbt, env); 1702 struct branch_info *bi; 1703 #define MAX_OPTIONS 16 1704 char *options[MAX_OPTIONS]; 1705 struct popup_action actions[MAX_OPTIONS]; 1706 int nr_options = 0; 1707 int key = -1; 1708 char buf[64]; 1709 int delay_secs = hbt ? hbt->refresh : 0; 1710 struct perf_hpp_fmt *fmt; 1711 1712 #define HIST_BROWSER_HELP_COMMON \ 1713 "h/?/F1 Show this window\n" \ 1714 "UP/DOWN/PGUP\n" \ 1715 "PGDN/SPACE Navigate\n" \ 1716 "q/ESC/CTRL+C Exit browser\n\n" \ 1717 "For multiple event sessions:\n\n" \ 1718 "TAB/UNTAB Switch events\n\n" \ 1719 "For symbolic views (--sort has sym):\n\n" \ 1720 "-> Zoom into DSO/Threads & Annotate current symbol\n" \ 1721 "<- Zoom out\n" \ 1722 "a Annotate current symbol\n" \ 1723 "C Collapse all callchains\n" \ 1724 "d Zoom into current DSO\n" \ 1725 "E Expand all callchains\n" \ 1726 "F Toggle percentage of filtered entries\n" \ 1727 "H Display column headers\n" \ 1728 1729 /* help messages are sorted by lexical order of the hotkey */ 1730 const char report_help[] = HIST_BROWSER_HELP_COMMON 1731 "i Show header information\n" 1732 "P Print histograms to perf.hist.N\n" 1733 "r Run available scripts\n" 1734 "s Switch to another data file in PWD\n" 1735 "t Zoom into current Thread\n" 1736 "V Verbose (DSO names in callchains, etc)\n" 1737 "/ Filter symbol by name"; 1738 const char top_help[] = HIST_BROWSER_HELP_COMMON 1739 "P Print histograms to perf.hist.N\n" 1740 "t Zoom into current Thread\n" 1741 "V Verbose (DSO names in callchains, etc)\n" 1742 "z Toggle zeroing of samples\n" 1743 "f Enable/Disable events\n" 1744 "/ Filter symbol by name"; 1745 1746 if (browser == NULL) 1747 return -1; 1748 1749 /* reset abort key so that it can get Ctrl-C as a key */ 1750 SLang_reset_tty(); 1751 SLang_init_tty(0, 0, 0); 1752 1753 if (min_pcnt) { 1754 browser->min_pcnt = min_pcnt; 1755 hist_browser__update_nr_entries(browser); 1756 } 1757 1758 browser->pstack = pstack__new(2); 1759 if (browser->pstack == NULL) 1760 goto out; 1761 1762 ui_helpline__push(helpline); 1763 1764 memset(options, 0, sizeof(options)); 1765 memset(actions, 0, sizeof(actions)); 1766 1767 perf_hpp__for_each_format(fmt) 1768 perf_hpp__reset_width(fmt, hists); 1769 1770 if (symbol_conf.col_width_list_str) 1771 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 1772 1773 while (1) { 1774 struct thread *thread = NULL; 1775 struct dso *dso = NULL; 1776 int choice = 0; 1777 1778 nr_options = 0; 1779 1780 key = hist_browser__run(browser, helpline); 1781 1782 if (browser->he_selection != NULL) { 1783 thread = hist_browser__selected_thread(browser); 1784 dso = browser->selection->map ? browser->selection->map->dso : NULL; 1785 } 1786 switch (key) { 1787 case K_TAB: 1788 case K_UNTAB: 1789 if (nr_events == 1) 1790 continue; 1791 /* 1792 * Exit the browser, let hists__browser_tree 1793 * go to the next or previous 1794 */ 1795 goto out_free_stack; 1796 case 'a': 1797 if (!sort__has_sym) { 1798 ui_browser__warning(&browser->b, delay_secs * 2, 1799 "Annotation is only available for symbolic views, " 1800 "include \"sym*\" in --sort to use it."); 1801 continue; 1802 } 1803 1804 if (browser->selection == NULL || 1805 browser->selection->sym == NULL || 1806 browser->selection->map->dso->annotate_warned) 1807 continue; 1808 1809 actions->ms.map = browser->selection->map; 1810 actions->ms.sym = browser->selection->sym; 1811 do_annotate(browser, actions); 1812 continue; 1813 case 'P': 1814 hist_browser__dump(browser); 1815 continue; 1816 case 'd': 1817 actions->dso = dso; 1818 do_zoom_dso(browser, actions); 1819 continue; 1820 case 'V': 1821 browser->show_dso = !browser->show_dso; 1822 continue; 1823 case 't': 1824 actions->thread = thread; 1825 do_zoom_thread(browser, actions); 1826 continue; 1827 case '/': 1828 if (ui_browser__input_window("Symbol to show", 1829 "Please enter the name of symbol you want to see", 1830 buf, "ENTER: OK, ESC: Cancel", 1831 delay_secs * 2) == K_ENTER) { 1832 hists->symbol_filter_str = *buf ? buf : NULL; 1833 hists__filter_by_symbol(hists); 1834 hist_browser__reset(browser); 1835 } 1836 continue; 1837 case 'r': 1838 if (is_report_browser(hbt)) { 1839 actions->thread = NULL; 1840 actions->ms.sym = NULL; 1841 do_run_script(browser, actions); 1842 } 1843 continue; 1844 case 's': 1845 if (is_report_browser(hbt)) { 1846 key = do_switch_data(browser, actions); 1847 if (key == K_SWITCH_INPUT_DATA) 1848 goto out_free_stack; 1849 } 1850 continue; 1851 case 'i': 1852 /* env->arch is NULL for live-mode (i.e. perf top) */ 1853 if (env->arch) 1854 tui__header_window(env); 1855 continue; 1856 case 'F': 1857 symbol_conf.filter_relative ^= 1; 1858 continue; 1859 case 'z': 1860 if (!is_report_browser(hbt)) { 1861 struct perf_top *top = hbt->arg; 1862 1863 top->zero = !top->zero; 1864 } 1865 continue; 1866 case K_F1: 1867 case 'h': 1868 case '?': 1869 ui_browser__help_window(&browser->b, 1870 is_report_browser(hbt) ? report_help : top_help); 1871 continue; 1872 case K_ENTER: 1873 case K_RIGHT: 1874 /* menu */ 1875 break; 1876 case K_ESC: 1877 case K_LEFT: { 1878 const void *top; 1879 1880 if (pstack__empty(browser->pstack)) { 1881 /* 1882 * Go back to the perf_evsel_menu__run or other user 1883 */ 1884 if (left_exits) 1885 goto out_free_stack; 1886 1887 if (key == K_ESC && 1888 ui_browser__dialog_yesno(&browser->b, 1889 "Do you really want to exit?")) 1890 goto out_free_stack; 1891 1892 continue; 1893 } 1894 top = pstack__peek(browser->pstack); 1895 if (top == &browser->hists->dso_filter) { 1896 /* 1897 * No need to set actions->dso here since 1898 * it's just to remove the current filter. 1899 * Ditto for thread below. 1900 */ 1901 do_zoom_dso(browser, actions); 1902 } 1903 if (top == &browser->hists->thread_filter) 1904 do_zoom_thread(browser, actions); 1905 continue; 1906 } 1907 case 'q': 1908 case CTRL('c'): 1909 goto out_free_stack; 1910 case 'f': 1911 if (!is_report_browser(hbt)) { 1912 struct perf_top *top = hbt->arg; 1913 1914 perf_evlist__toggle_enable(top->evlist); 1915 /* 1916 * No need to refresh, resort/decay histogram 1917 * entries if we are not collecting samples: 1918 */ 1919 if (top->evlist->enabled) { 1920 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 1921 hbt->refresh = delay_secs; 1922 } else { 1923 helpline = "Press 'f' again to re-enable the events"; 1924 hbt->refresh = 0; 1925 } 1926 continue; 1927 } 1928 /* Fall thru */ 1929 default: 1930 helpline = "Press '?' for help on key bindings"; 1931 continue; 1932 } 1933 1934 if (!sort__has_sym) 1935 goto add_exit_option; 1936 1937 if (browser->selection == NULL) 1938 goto skip_annotation; 1939 1940 if (sort__mode == SORT_MODE__BRANCH) { 1941 bi = browser->he_selection->branch_info; 1942 1943 if (bi == NULL) 1944 goto skip_annotation; 1945 1946 nr_options += add_annotate_opt(browser, 1947 &actions[nr_options], 1948 &options[nr_options], 1949 bi->from.map, 1950 bi->from.sym); 1951 if (bi->to.sym != bi->from.sym) 1952 nr_options += add_annotate_opt(browser, 1953 &actions[nr_options], 1954 &options[nr_options], 1955 bi->to.map, 1956 bi->to.sym); 1957 } else { 1958 nr_options += add_annotate_opt(browser, 1959 &actions[nr_options], 1960 &options[nr_options], 1961 browser->selection->map, 1962 browser->selection->sym); 1963 } 1964 skip_annotation: 1965 nr_options += add_thread_opt(browser, &actions[nr_options], 1966 &options[nr_options], thread); 1967 nr_options += add_dso_opt(browser, &actions[nr_options], 1968 &options[nr_options], dso); 1969 nr_options += add_map_opt(browser, &actions[nr_options], 1970 &options[nr_options], 1971 browser->selection ? 1972 browser->selection->map : NULL); 1973 1974 /* perf script support */ 1975 if (browser->he_selection) { 1976 nr_options += add_script_opt(browser, 1977 &actions[nr_options], 1978 &options[nr_options], 1979 thread, NULL); 1980 /* 1981 * Note that browser->selection != NULL 1982 * when browser->he_selection is not NULL, 1983 * so we don't need to check browser->selection 1984 * before fetching browser->selection->sym like what 1985 * we do before fetching browser->selection->map. 1986 * 1987 * See hist_browser__show_entry. 1988 */ 1989 nr_options += add_script_opt(browser, 1990 &actions[nr_options], 1991 &options[nr_options], 1992 NULL, browser->selection->sym); 1993 } 1994 nr_options += add_script_opt(browser, &actions[nr_options], 1995 &options[nr_options], NULL, NULL); 1996 nr_options += add_switch_opt(browser, &actions[nr_options], 1997 &options[nr_options]); 1998 add_exit_option: 1999 nr_options += add_exit_opt(browser, &actions[nr_options], 2000 &options[nr_options]); 2001 2002 do { 2003 struct popup_action *act; 2004 2005 choice = ui__popup_menu(nr_options, options); 2006 if (choice == -1 || choice >= nr_options) 2007 break; 2008 2009 act = &actions[choice]; 2010 key = act->fn(browser, act); 2011 } while (key == 1); 2012 2013 if (key == K_SWITCH_INPUT_DATA) 2014 break; 2015 } 2016 out_free_stack: 2017 pstack__delete(browser->pstack); 2018 out: 2019 hist_browser__delete(browser); 2020 free_popup_options(options, MAX_OPTIONS); 2021 return key; 2022 } 2023 2024 struct perf_evsel_menu { 2025 struct ui_browser b; 2026 struct perf_evsel *selection; 2027 bool lost_events, lost_events_warned; 2028 float min_pcnt; 2029 struct perf_env *env; 2030 }; 2031 2032 static void perf_evsel_menu__write(struct ui_browser *browser, 2033 void *entry, int row) 2034 { 2035 struct perf_evsel_menu *menu = container_of(browser, 2036 struct perf_evsel_menu, b); 2037 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2038 struct hists *hists = evsel__hists(evsel); 2039 bool current_entry = ui_browser__is_current_entry(browser, row); 2040 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2041 const char *ev_name = perf_evsel__name(evsel); 2042 char bf[256], unit; 2043 const char *warn = " "; 2044 size_t printed; 2045 2046 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 2047 HE_COLORSET_NORMAL); 2048 2049 if (perf_evsel__is_group_event(evsel)) { 2050 struct perf_evsel *pos; 2051 2052 ev_name = perf_evsel__group_name(evsel); 2053 2054 for_each_group_member(pos, evsel) { 2055 struct hists *pos_hists = evsel__hists(pos); 2056 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2057 } 2058 } 2059 2060 nr_events = convert_unit(nr_events, &unit); 2061 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 2062 unit, unit == ' ' ? "" : " ", ev_name); 2063 ui_browser__printf(browser, "%s", bf); 2064 2065 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 2066 if (nr_events != 0) { 2067 menu->lost_events = true; 2068 if (!current_entry) 2069 ui_browser__set_color(browser, HE_COLORSET_TOP); 2070 nr_events = convert_unit(nr_events, &unit); 2071 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 2072 nr_events, unit, unit == ' ' ? "" : " "); 2073 warn = bf; 2074 } 2075 2076 ui_browser__write_nstring(browser, warn, browser->width - printed); 2077 2078 if (current_entry) 2079 menu->selection = evsel; 2080 } 2081 2082 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 2083 int nr_events, const char *help, 2084 struct hist_browser_timer *hbt) 2085 { 2086 struct perf_evlist *evlist = menu->b.priv; 2087 struct perf_evsel *pos; 2088 const char *title = "Available samples"; 2089 int delay_secs = hbt ? hbt->refresh : 0; 2090 int key; 2091 2092 if (ui_browser__show(&menu->b, title, 2093 "ESC: exit, ENTER|->: Browse histograms") < 0) 2094 return -1; 2095 2096 while (1) { 2097 key = ui_browser__run(&menu->b, delay_secs); 2098 2099 switch (key) { 2100 case K_TIMER: 2101 hbt->timer(hbt->arg); 2102 2103 if (!menu->lost_events_warned && menu->lost_events) { 2104 ui_browser__warn_lost_events(&menu->b); 2105 menu->lost_events_warned = true; 2106 } 2107 continue; 2108 case K_RIGHT: 2109 case K_ENTER: 2110 if (!menu->selection) 2111 continue; 2112 pos = menu->selection; 2113 browse_hists: 2114 perf_evlist__set_selected(evlist, pos); 2115 /* 2116 * Give the calling tool a chance to populate the non 2117 * default evsel resorted hists tree. 2118 */ 2119 if (hbt) 2120 hbt->timer(hbt->arg); 2121 key = perf_evsel__hists_browse(pos, nr_events, help, 2122 true, hbt, 2123 menu->min_pcnt, 2124 menu->env); 2125 ui_browser__show_title(&menu->b, title); 2126 switch (key) { 2127 case K_TAB: 2128 if (pos->node.next == &evlist->entries) 2129 pos = perf_evlist__first(evlist); 2130 else 2131 pos = perf_evsel__next(pos); 2132 goto browse_hists; 2133 case K_UNTAB: 2134 if (pos->node.prev == &evlist->entries) 2135 pos = perf_evlist__last(evlist); 2136 else 2137 pos = perf_evsel__prev(pos); 2138 goto browse_hists; 2139 case K_SWITCH_INPUT_DATA: 2140 case 'q': 2141 case CTRL('c'): 2142 goto out; 2143 case K_ESC: 2144 default: 2145 continue; 2146 } 2147 case K_LEFT: 2148 continue; 2149 case K_ESC: 2150 if (!ui_browser__dialog_yesno(&menu->b, 2151 "Do you really want to exit?")) 2152 continue; 2153 /* Fall thru */ 2154 case 'q': 2155 case CTRL('c'): 2156 goto out; 2157 default: 2158 continue; 2159 } 2160 } 2161 2162 out: 2163 ui_browser__hide(&menu->b); 2164 return key; 2165 } 2166 2167 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 2168 void *entry) 2169 { 2170 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2171 2172 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 2173 return true; 2174 2175 return false; 2176 } 2177 2178 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 2179 int nr_entries, const char *help, 2180 struct hist_browser_timer *hbt, 2181 float min_pcnt, 2182 struct perf_env *env) 2183 { 2184 struct perf_evsel *pos; 2185 struct perf_evsel_menu menu = { 2186 .b = { 2187 .entries = &evlist->entries, 2188 .refresh = ui_browser__list_head_refresh, 2189 .seek = ui_browser__list_head_seek, 2190 .write = perf_evsel_menu__write, 2191 .filter = filter_group_entries, 2192 .nr_entries = nr_entries, 2193 .priv = evlist, 2194 }, 2195 .min_pcnt = min_pcnt, 2196 .env = env, 2197 }; 2198 2199 ui_helpline__push("Press ESC to exit"); 2200 2201 evlist__for_each(evlist, pos) { 2202 const char *ev_name = perf_evsel__name(pos); 2203 size_t line_len = strlen(ev_name) + 7; 2204 2205 if (menu.b.width < line_len) 2206 menu.b.width = line_len; 2207 } 2208 2209 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 2210 } 2211 2212 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 2213 struct hist_browser_timer *hbt, 2214 float min_pcnt, 2215 struct perf_env *env) 2216 { 2217 int nr_entries = evlist->nr_entries; 2218 2219 single_entry: 2220 if (nr_entries == 1) { 2221 struct perf_evsel *first = perf_evlist__first(evlist); 2222 2223 return perf_evsel__hists_browse(first, nr_entries, help, 2224 false, hbt, min_pcnt, 2225 env); 2226 } 2227 2228 if (symbol_conf.event_group) { 2229 struct perf_evsel *pos; 2230 2231 nr_entries = 0; 2232 evlist__for_each(evlist, pos) { 2233 if (perf_evsel__is_group_leader(pos)) 2234 nr_entries++; 2235 } 2236 2237 if (nr_entries == 1) 2238 goto single_entry; 2239 } 2240 2241 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 2242 hbt, min_pcnt, env); 2243 } 2244