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 int socket = hists->socket_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 char ref[30] = " show reference callgraph, "; 1272 bool enable_ref = false; 1273 1274 if (symbol_conf.filter_relative) { 1275 nr_samples = hists->stats.nr_non_filtered_samples; 1276 nr_events = hists->stats.total_non_filtered_period; 1277 } 1278 1279 if (perf_evsel__is_group_event(evsel)) { 1280 struct perf_evsel *pos; 1281 1282 perf_evsel__group_desc(evsel, buf, buflen); 1283 ev_name = buf; 1284 1285 for_each_group_member(pos, evsel) { 1286 struct hists *pos_hists = evsel__hists(pos); 1287 1288 if (symbol_conf.filter_relative) { 1289 nr_samples += pos_hists->stats.nr_non_filtered_samples; 1290 nr_events += pos_hists->stats.total_non_filtered_period; 1291 } else { 1292 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1293 nr_events += pos_hists->stats.total_period; 1294 } 1295 } 1296 } 1297 1298 if (symbol_conf.show_ref_callgraph && 1299 strstr(ev_name, "call-graph=no")) 1300 enable_ref = true; 1301 nr_samples = convert_unit(nr_samples, &unit); 1302 printed = scnprintf(bf, size, 1303 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, 1304 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); 1305 1306 1307 if (hists->uid_filter_str) 1308 printed += snprintf(bf + printed, size - printed, 1309 ", UID: %s", hists->uid_filter_str); 1310 if (thread) 1311 printed += scnprintf(bf + printed, size - printed, 1312 ", Thread: %s(%d)", 1313 (thread->comm_set ? thread__comm_str(thread) : ""), 1314 thread->tid); 1315 if (dso) 1316 printed += scnprintf(bf + printed, size - printed, 1317 ", DSO: %s", dso->short_name); 1318 if (socket > -1) 1319 printed += scnprintf(bf + printed, size - printed, 1320 ", Processor Socket: %d", socket); 1321 if (!is_report_browser(hbt)) { 1322 struct perf_top *top = hbt->arg; 1323 1324 if (top->zero) 1325 printed += scnprintf(bf + printed, size - printed, " [z]"); 1326 } 1327 1328 return printed; 1329 } 1330 1331 static inline void free_popup_options(char **options, int n) 1332 { 1333 int i; 1334 1335 for (i = 0; i < n; ++i) 1336 zfree(&options[i]); 1337 } 1338 1339 /* 1340 * Only runtime switching of perf data file will make "input_name" point 1341 * to a malloced buffer. So add "is_input_name_malloced" flag to decide 1342 * whether we need to call free() for current "input_name" during the switch. 1343 */ 1344 static bool is_input_name_malloced = false; 1345 1346 static int switch_data_file(void) 1347 { 1348 char *pwd, *options[32], *abs_path[32], *tmp; 1349 DIR *pwd_dir; 1350 int nr_options = 0, choice = -1, ret = -1; 1351 struct dirent *dent; 1352 1353 pwd = getenv("PWD"); 1354 if (!pwd) 1355 return ret; 1356 1357 pwd_dir = opendir(pwd); 1358 if (!pwd_dir) 1359 return ret; 1360 1361 memset(options, 0, sizeof(options)); 1362 memset(options, 0, sizeof(abs_path)); 1363 1364 while ((dent = readdir(pwd_dir))) { 1365 char path[PATH_MAX]; 1366 u64 magic; 1367 char *name = dent->d_name; 1368 FILE *file; 1369 1370 if (!(dent->d_type == DT_REG)) 1371 continue; 1372 1373 snprintf(path, sizeof(path), "%s/%s", pwd, name); 1374 1375 file = fopen(path, "r"); 1376 if (!file) 1377 continue; 1378 1379 if (fread(&magic, 1, 8, file) < 8) 1380 goto close_file_and_continue; 1381 1382 if (is_perf_magic(magic)) { 1383 options[nr_options] = strdup(name); 1384 if (!options[nr_options]) 1385 goto close_file_and_continue; 1386 1387 abs_path[nr_options] = strdup(path); 1388 if (!abs_path[nr_options]) { 1389 zfree(&options[nr_options]); 1390 ui__warning("Can't search all data files due to memory shortage.\n"); 1391 fclose(file); 1392 break; 1393 } 1394 1395 nr_options++; 1396 } 1397 1398 close_file_and_continue: 1399 fclose(file); 1400 if (nr_options >= 32) { 1401 ui__warning("Too many perf data files in PWD!\n" 1402 "Only the first 32 files will be listed.\n"); 1403 break; 1404 } 1405 } 1406 closedir(pwd_dir); 1407 1408 if (nr_options) { 1409 choice = ui__popup_menu(nr_options, options); 1410 if (choice < nr_options && choice >= 0) { 1411 tmp = strdup(abs_path[choice]); 1412 if (tmp) { 1413 if (is_input_name_malloced) 1414 free((void *)input_name); 1415 input_name = tmp; 1416 is_input_name_malloced = true; 1417 ret = 0; 1418 } else 1419 ui__warning("Data switch failed due to memory shortage!\n"); 1420 } 1421 } 1422 1423 free_popup_options(options, nr_options); 1424 free_popup_options(abs_path, nr_options); 1425 return ret; 1426 } 1427 1428 struct popup_action { 1429 struct thread *thread; 1430 struct dso *dso; 1431 struct map_symbol ms; 1432 1433 int (*fn)(struct hist_browser *browser, struct popup_action *act); 1434 }; 1435 1436 static int 1437 do_annotate(struct hist_browser *browser, struct popup_action *act) 1438 { 1439 struct perf_evsel *evsel; 1440 struct annotation *notes; 1441 struct hist_entry *he; 1442 int err; 1443 1444 if (!objdump_path && perf_env__lookup_objdump(browser->env)) 1445 return 0; 1446 1447 notes = symbol__annotation(act->ms.sym); 1448 if (!notes->src) 1449 return 0; 1450 1451 evsel = hists_to_evsel(browser->hists); 1452 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); 1453 he = hist_browser__selected_entry(browser); 1454 /* 1455 * offer option to annotate the other branch source or target 1456 * (if they exists) when returning from annotate 1457 */ 1458 if ((err == 'q' || err == CTRL('c')) && he->branch_info) 1459 return 1; 1460 1461 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1462 if (err) 1463 ui_browser__handle_resize(&browser->b); 1464 return 0; 1465 } 1466 1467 static int 1468 add_annotate_opt(struct hist_browser *browser __maybe_unused, 1469 struct popup_action *act, char **optstr, 1470 struct map *map, struct symbol *sym) 1471 { 1472 if (sym == NULL || map->dso->annotate_warned) 1473 return 0; 1474 1475 if (asprintf(optstr, "Annotate %s", sym->name) < 0) 1476 return 0; 1477 1478 act->ms.map = map; 1479 act->ms.sym = sym; 1480 act->fn = do_annotate; 1481 return 1; 1482 } 1483 1484 static int 1485 do_zoom_thread(struct hist_browser *browser, struct popup_action *act) 1486 { 1487 struct thread *thread = act->thread; 1488 1489 if (browser->hists->thread_filter) { 1490 pstack__remove(browser->pstack, &browser->hists->thread_filter); 1491 perf_hpp__set_elide(HISTC_THREAD, false); 1492 thread__zput(browser->hists->thread_filter); 1493 ui_helpline__pop(); 1494 } else { 1495 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1496 thread->comm_set ? thread__comm_str(thread) : "", 1497 thread->tid); 1498 browser->hists->thread_filter = thread__get(thread); 1499 perf_hpp__set_elide(HISTC_THREAD, false); 1500 pstack__push(browser->pstack, &browser->hists->thread_filter); 1501 } 1502 1503 hists__filter_by_thread(browser->hists); 1504 hist_browser__reset(browser); 1505 return 0; 1506 } 1507 1508 static int 1509 add_thread_opt(struct hist_browser *browser, struct popup_action *act, 1510 char **optstr, struct thread *thread) 1511 { 1512 if (thread == NULL) 1513 return 0; 1514 1515 if (asprintf(optstr, "Zoom %s %s(%d) thread", 1516 browser->hists->thread_filter ? "out of" : "into", 1517 thread->comm_set ? thread__comm_str(thread) : "", 1518 thread->tid) < 0) 1519 return 0; 1520 1521 act->thread = thread; 1522 act->fn = do_zoom_thread; 1523 return 1; 1524 } 1525 1526 static int 1527 do_zoom_dso(struct hist_browser *browser, struct popup_action *act) 1528 { 1529 struct dso *dso = act->dso; 1530 1531 if (browser->hists->dso_filter) { 1532 pstack__remove(browser->pstack, &browser->hists->dso_filter); 1533 perf_hpp__set_elide(HISTC_DSO, false); 1534 browser->hists->dso_filter = NULL; 1535 ui_helpline__pop(); 1536 } else { 1537 if (dso == NULL) 1538 return 0; 1539 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1540 dso->kernel ? "the Kernel" : dso->short_name); 1541 browser->hists->dso_filter = dso; 1542 perf_hpp__set_elide(HISTC_DSO, true); 1543 pstack__push(browser->pstack, &browser->hists->dso_filter); 1544 } 1545 1546 hists__filter_by_dso(browser->hists); 1547 hist_browser__reset(browser); 1548 return 0; 1549 } 1550 1551 static int 1552 add_dso_opt(struct hist_browser *browser, struct popup_action *act, 1553 char **optstr, struct dso *dso) 1554 { 1555 if (dso == NULL) 1556 return 0; 1557 1558 if (asprintf(optstr, "Zoom %s %s DSO", 1559 browser->hists->dso_filter ? "out of" : "into", 1560 dso->kernel ? "the Kernel" : dso->short_name) < 0) 1561 return 0; 1562 1563 act->dso = dso; 1564 act->fn = do_zoom_dso; 1565 return 1; 1566 } 1567 1568 static int 1569 do_browse_map(struct hist_browser *browser __maybe_unused, 1570 struct popup_action *act) 1571 { 1572 map__browse(act->ms.map); 1573 return 0; 1574 } 1575 1576 static int 1577 add_map_opt(struct hist_browser *browser __maybe_unused, 1578 struct popup_action *act, char **optstr, struct map *map) 1579 { 1580 if (map == NULL) 1581 return 0; 1582 1583 if (asprintf(optstr, "Browse map details") < 0) 1584 return 0; 1585 1586 act->ms.map = map; 1587 act->fn = do_browse_map; 1588 return 1; 1589 } 1590 1591 static int 1592 do_run_script(struct hist_browser *browser __maybe_unused, 1593 struct popup_action *act) 1594 { 1595 char script_opt[64]; 1596 memset(script_opt, 0, sizeof(script_opt)); 1597 1598 if (act->thread) { 1599 scnprintf(script_opt, sizeof(script_opt), " -c %s ", 1600 thread__comm_str(act->thread)); 1601 } else if (act->ms.sym) { 1602 scnprintf(script_opt, sizeof(script_opt), " -S %s ", 1603 act->ms.sym->name); 1604 } 1605 1606 script_browse(script_opt); 1607 return 0; 1608 } 1609 1610 static int 1611 add_script_opt(struct hist_browser *browser __maybe_unused, 1612 struct popup_action *act, char **optstr, 1613 struct thread *thread, struct symbol *sym) 1614 { 1615 if (thread) { 1616 if (asprintf(optstr, "Run scripts for samples of thread [%s]", 1617 thread__comm_str(thread)) < 0) 1618 return 0; 1619 } else if (sym) { 1620 if (asprintf(optstr, "Run scripts for samples of symbol [%s]", 1621 sym->name) < 0) 1622 return 0; 1623 } else { 1624 if (asprintf(optstr, "Run scripts for all samples") < 0) 1625 return 0; 1626 } 1627 1628 act->thread = thread; 1629 act->ms.sym = sym; 1630 act->fn = do_run_script; 1631 return 1; 1632 } 1633 1634 static int 1635 do_switch_data(struct hist_browser *browser __maybe_unused, 1636 struct popup_action *act __maybe_unused) 1637 { 1638 if (switch_data_file()) { 1639 ui__warning("Won't switch the data files due to\n" 1640 "no valid data file get selected!\n"); 1641 return 0; 1642 } 1643 1644 return K_SWITCH_INPUT_DATA; 1645 } 1646 1647 static int 1648 add_switch_opt(struct hist_browser *browser, 1649 struct popup_action *act, char **optstr) 1650 { 1651 if (!is_report_browser(browser->hbt)) 1652 return 0; 1653 1654 if (asprintf(optstr, "Switch to another data file in PWD") < 0) 1655 return 0; 1656 1657 act->fn = do_switch_data; 1658 return 1; 1659 } 1660 1661 static int 1662 do_exit_browser(struct hist_browser *browser __maybe_unused, 1663 struct popup_action *act __maybe_unused) 1664 { 1665 return 0; 1666 } 1667 1668 static int 1669 add_exit_opt(struct hist_browser *browser __maybe_unused, 1670 struct popup_action *act, char **optstr) 1671 { 1672 if (asprintf(optstr, "Exit") < 0) 1673 return 0; 1674 1675 act->fn = do_exit_browser; 1676 return 1; 1677 } 1678 1679 static void hist_browser__update_nr_entries(struct hist_browser *hb) 1680 { 1681 u64 nr_entries = 0; 1682 struct rb_node *nd = rb_first(&hb->hists->entries); 1683 1684 if (hb->min_pcnt == 0) { 1685 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 1686 return; 1687 } 1688 1689 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 1690 nr_entries++; 1691 nd = rb_next(nd); 1692 } 1693 1694 hb->nr_non_filtered_entries = nr_entries; 1695 } 1696 1697 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1698 const char *helpline, 1699 bool left_exits, 1700 struct hist_browser_timer *hbt, 1701 float min_pcnt, 1702 struct perf_env *env) 1703 { 1704 struct hists *hists = evsel__hists(evsel); 1705 struct hist_browser *browser = hist_browser__new(hists, hbt, env); 1706 struct branch_info *bi; 1707 #define MAX_OPTIONS 16 1708 char *options[MAX_OPTIONS]; 1709 struct popup_action actions[MAX_OPTIONS]; 1710 int nr_options = 0; 1711 int key = -1; 1712 char buf[64]; 1713 int delay_secs = hbt ? hbt->refresh : 0; 1714 struct perf_hpp_fmt *fmt; 1715 1716 #define HIST_BROWSER_HELP_COMMON \ 1717 "h/?/F1 Show this window\n" \ 1718 "UP/DOWN/PGUP\n" \ 1719 "PGDN/SPACE Navigate\n" \ 1720 "q/ESC/CTRL+C Exit browser\n\n" \ 1721 "For multiple event sessions:\n\n" \ 1722 "TAB/UNTAB Switch events\n\n" \ 1723 "For symbolic views (--sort has sym):\n\n" \ 1724 "-> Zoom into DSO/Threads & Annotate current symbol\n" \ 1725 "<- Zoom out\n" \ 1726 "a Annotate current symbol\n" \ 1727 "C Collapse all callchains\n" \ 1728 "d Zoom into current DSO\n" \ 1729 "E Expand all callchains\n" \ 1730 "F Toggle percentage of filtered entries\n" \ 1731 "H Display column headers\n" \ 1732 1733 /* help messages are sorted by lexical order of the hotkey */ 1734 const char report_help[] = HIST_BROWSER_HELP_COMMON 1735 "i Show header information\n" 1736 "P Print histograms to perf.hist.N\n" 1737 "r Run available scripts\n" 1738 "s Switch to another data file in PWD\n" 1739 "t Zoom into current Thread\n" 1740 "V Verbose (DSO names in callchains, etc)\n" 1741 "/ Filter symbol by name"; 1742 const char top_help[] = HIST_BROWSER_HELP_COMMON 1743 "P Print histograms to perf.hist.N\n" 1744 "t Zoom into current Thread\n" 1745 "V Verbose (DSO names in callchains, etc)\n" 1746 "z Toggle zeroing of samples\n" 1747 "f Enable/Disable events\n" 1748 "/ Filter symbol by name"; 1749 1750 if (browser == NULL) 1751 return -1; 1752 1753 /* reset abort key so that it can get Ctrl-C as a key */ 1754 SLang_reset_tty(); 1755 SLang_init_tty(0, 0, 0); 1756 1757 if (min_pcnt) { 1758 browser->min_pcnt = min_pcnt; 1759 hist_browser__update_nr_entries(browser); 1760 } 1761 1762 browser->pstack = pstack__new(2); 1763 if (browser->pstack == NULL) 1764 goto out; 1765 1766 ui_helpline__push(helpline); 1767 1768 memset(options, 0, sizeof(options)); 1769 memset(actions, 0, sizeof(actions)); 1770 1771 perf_hpp__for_each_format(fmt) 1772 perf_hpp__reset_width(fmt, hists); 1773 1774 if (symbol_conf.col_width_list_str) 1775 perf_hpp__set_user_width(symbol_conf.col_width_list_str); 1776 1777 while (1) { 1778 struct thread *thread = NULL; 1779 struct dso *dso = NULL; 1780 int choice = 0; 1781 1782 nr_options = 0; 1783 1784 key = hist_browser__run(browser, helpline); 1785 1786 if (browser->he_selection != NULL) { 1787 thread = hist_browser__selected_thread(browser); 1788 dso = browser->selection->map ? browser->selection->map->dso : NULL; 1789 } 1790 switch (key) { 1791 case K_TAB: 1792 case K_UNTAB: 1793 if (nr_events == 1) 1794 continue; 1795 /* 1796 * Exit the browser, let hists__browser_tree 1797 * go to the next or previous 1798 */ 1799 goto out_free_stack; 1800 case 'a': 1801 if (!sort__has_sym) { 1802 ui_browser__warning(&browser->b, delay_secs * 2, 1803 "Annotation is only available for symbolic views, " 1804 "include \"sym*\" in --sort to use it."); 1805 continue; 1806 } 1807 1808 if (browser->selection == NULL || 1809 browser->selection->sym == NULL || 1810 browser->selection->map->dso->annotate_warned) 1811 continue; 1812 1813 actions->ms.map = browser->selection->map; 1814 actions->ms.sym = browser->selection->sym; 1815 do_annotate(browser, actions); 1816 continue; 1817 case 'P': 1818 hist_browser__dump(browser); 1819 continue; 1820 case 'd': 1821 actions->dso = dso; 1822 do_zoom_dso(browser, actions); 1823 continue; 1824 case 'V': 1825 browser->show_dso = !browser->show_dso; 1826 continue; 1827 case 't': 1828 actions->thread = thread; 1829 do_zoom_thread(browser, actions); 1830 continue; 1831 case '/': 1832 if (ui_browser__input_window("Symbol to show", 1833 "Please enter the name of symbol you want to see", 1834 buf, "ENTER: OK, ESC: Cancel", 1835 delay_secs * 2) == K_ENTER) { 1836 hists->symbol_filter_str = *buf ? buf : NULL; 1837 hists__filter_by_symbol(hists); 1838 hist_browser__reset(browser); 1839 } 1840 continue; 1841 case 'r': 1842 if (is_report_browser(hbt)) { 1843 actions->thread = NULL; 1844 actions->ms.sym = NULL; 1845 do_run_script(browser, actions); 1846 } 1847 continue; 1848 case 's': 1849 if (is_report_browser(hbt)) { 1850 key = do_switch_data(browser, actions); 1851 if (key == K_SWITCH_INPUT_DATA) 1852 goto out_free_stack; 1853 } 1854 continue; 1855 case 'i': 1856 /* env->arch is NULL for live-mode (i.e. perf top) */ 1857 if (env->arch) 1858 tui__header_window(env); 1859 continue; 1860 case 'F': 1861 symbol_conf.filter_relative ^= 1; 1862 continue; 1863 case 'z': 1864 if (!is_report_browser(hbt)) { 1865 struct perf_top *top = hbt->arg; 1866 1867 top->zero = !top->zero; 1868 } 1869 continue; 1870 case K_F1: 1871 case 'h': 1872 case '?': 1873 ui_browser__help_window(&browser->b, 1874 is_report_browser(hbt) ? report_help : top_help); 1875 continue; 1876 case K_ENTER: 1877 case K_RIGHT: 1878 /* menu */ 1879 break; 1880 case K_ESC: 1881 case K_LEFT: { 1882 const void *top; 1883 1884 if (pstack__empty(browser->pstack)) { 1885 /* 1886 * Go back to the perf_evsel_menu__run or other user 1887 */ 1888 if (left_exits) 1889 goto out_free_stack; 1890 1891 if (key == K_ESC && 1892 ui_browser__dialog_yesno(&browser->b, 1893 "Do you really want to exit?")) 1894 goto out_free_stack; 1895 1896 continue; 1897 } 1898 top = pstack__peek(browser->pstack); 1899 if (top == &browser->hists->dso_filter) { 1900 /* 1901 * No need to set actions->dso here since 1902 * it's just to remove the current filter. 1903 * Ditto for thread below. 1904 */ 1905 do_zoom_dso(browser, actions); 1906 } 1907 if (top == &browser->hists->thread_filter) 1908 do_zoom_thread(browser, actions); 1909 continue; 1910 } 1911 case 'q': 1912 case CTRL('c'): 1913 goto out_free_stack; 1914 case 'f': 1915 if (!is_report_browser(hbt)) { 1916 struct perf_top *top = hbt->arg; 1917 1918 perf_evlist__toggle_enable(top->evlist); 1919 /* 1920 * No need to refresh, resort/decay histogram 1921 * entries if we are not collecting samples: 1922 */ 1923 if (top->evlist->enabled) { 1924 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 1925 hbt->refresh = delay_secs; 1926 } else { 1927 helpline = "Press 'f' again to re-enable the events"; 1928 hbt->refresh = 0; 1929 } 1930 continue; 1931 } 1932 /* Fall thru */ 1933 default: 1934 helpline = "Press '?' for help on key bindings"; 1935 continue; 1936 } 1937 1938 if (!sort__has_sym) 1939 goto add_exit_option; 1940 1941 if (browser->selection == NULL) 1942 goto skip_annotation; 1943 1944 if (sort__mode == SORT_MODE__BRANCH) { 1945 bi = browser->he_selection->branch_info; 1946 1947 if (bi == NULL) 1948 goto skip_annotation; 1949 1950 nr_options += add_annotate_opt(browser, 1951 &actions[nr_options], 1952 &options[nr_options], 1953 bi->from.map, 1954 bi->from.sym); 1955 if (bi->to.sym != bi->from.sym) 1956 nr_options += add_annotate_opt(browser, 1957 &actions[nr_options], 1958 &options[nr_options], 1959 bi->to.map, 1960 bi->to.sym); 1961 } else { 1962 nr_options += add_annotate_opt(browser, 1963 &actions[nr_options], 1964 &options[nr_options], 1965 browser->selection->map, 1966 browser->selection->sym); 1967 } 1968 skip_annotation: 1969 nr_options += add_thread_opt(browser, &actions[nr_options], 1970 &options[nr_options], thread); 1971 nr_options += add_dso_opt(browser, &actions[nr_options], 1972 &options[nr_options], dso); 1973 nr_options += add_map_opt(browser, &actions[nr_options], 1974 &options[nr_options], 1975 browser->selection->map); 1976 1977 /* perf script support */ 1978 if (browser->he_selection) { 1979 nr_options += add_script_opt(browser, 1980 &actions[nr_options], 1981 &options[nr_options], 1982 thread, NULL); 1983 nr_options += add_script_opt(browser, 1984 &actions[nr_options], 1985 &options[nr_options], 1986 NULL, browser->selection->sym); 1987 } 1988 nr_options += add_script_opt(browser, &actions[nr_options], 1989 &options[nr_options], NULL, NULL); 1990 nr_options += add_switch_opt(browser, &actions[nr_options], 1991 &options[nr_options]); 1992 add_exit_option: 1993 nr_options += add_exit_opt(browser, &actions[nr_options], 1994 &options[nr_options]); 1995 1996 do { 1997 struct popup_action *act; 1998 1999 choice = ui__popup_menu(nr_options, options); 2000 if (choice == -1 || choice >= nr_options) 2001 break; 2002 2003 act = &actions[choice]; 2004 key = act->fn(browser, act); 2005 } while (key == 1); 2006 2007 if (key == K_SWITCH_INPUT_DATA) 2008 break; 2009 } 2010 out_free_stack: 2011 pstack__delete(browser->pstack); 2012 out: 2013 hist_browser__delete(browser); 2014 free_popup_options(options, MAX_OPTIONS); 2015 return key; 2016 } 2017 2018 struct perf_evsel_menu { 2019 struct ui_browser b; 2020 struct perf_evsel *selection; 2021 bool lost_events, lost_events_warned; 2022 float min_pcnt; 2023 struct perf_env *env; 2024 }; 2025 2026 static void perf_evsel_menu__write(struct ui_browser *browser, 2027 void *entry, int row) 2028 { 2029 struct perf_evsel_menu *menu = container_of(browser, 2030 struct perf_evsel_menu, b); 2031 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2032 struct hists *hists = evsel__hists(evsel); 2033 bool current_entry = ui_browser__is_current_entry(browser, row); 2034 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2035 const char *ev_name = perf_evsel__name(evsel); 2036 char bf[256], unit; 2037 const char *warn = " "; 2038 size_t printed; 2039 2040 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 2041 HE_COLORSET_NORMAL); 2042 2043 if (perf_evsel__is_group_event(evsel)) { 2044 struct perf_evsel *pos; 2045 2046 ev_name = perf_evsel__group_name(evsel); 2047 2048 for_each_group_member(pos, evsel) { 2049 struct hists *pos_hists = evsel__hists(pos); 2050 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; 2051 } 2052 } 2053 2054 nr_events = convert_unit(nr_events, &unit); 2055 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 2056 unit, unit == ' ' ? "" : " ", ev_name); 2057 ui_browser__printf(browser, "%s", bf); 2058 2059 nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; 2060 if (nr_events != 0) { 2061 menu->lost_events = true; 2062 if (!current_entry) 2063 ui_browser__set_color(browser, HE_COLORSET_TOP); 2064 nr_events = convert_unit(nr_events, &unit); 2065 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 2066 nr_events, unit, unit == ' ' ? "" : " "); 2067 warn = bf; 2068 } 2069 2070 ui_browser__write_nstring(browser, warn, browser->width - printed); 2071 2072 if (current_entry) 2073 menu->selection = evsel; 2074 } 2075 2076 static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 2077 int nr_events, const char *help, 2078 struct hist_browser_timer *hbt) 2079 { 2080 struct perf_evlist *evlist = menu->b.priv; 2081 struct perf_evsel *pos; 2082 const char *title = "Available samples"; 2083 int delay_secs = hbt ? hbt->refresh : 0; 2084 int key; 2085 2086 if (ui_browser__show(&menu->b, title, 2087 "ESC: exit, ENTER|->: Browse histograms") < 0) 2088 return -1; 2089 2090 while (1) { 2091 key = ui_browser__run(&menu->b, delay_secs); 2092 2093 switch (key) { 2094 case K_TIMER: 2095 hbt->timer(hbt->arg); 2096 2097 if (!menu->lost_events_warned && menu->lost_events) { 2098 ui_browser__warn_lost_events(&menu->b); 2099 menu->lost_events_warned = true; 2100 } 2101 continue; 2102 case K_RIGHT: 2103 case K_ENTER: 2104 if (!menu->selection) 2105 continue; 2106 pos = menu->selection; 2107 browse_hists: 2108 perf_evlist__set_selected(evlist, pos); 2109 /* 2110 * Give the calling tool a chance to populate the non 2111 * default evsel resorted hists tree. 2112 */ 2113 if (hbt) 2114 hbt->timer(hbt->arg); 2115 key = perf_evsel__hists_browse(pos, nr_events, help, 2116 true, hbt, 2117 menu->min_pcnt, 2118 menu->env); 2119 ui_browser__show_title(&menu->b, title); 2120 switch (key) { 2121 case K_TAB: 2122 if (pos->node.next == &evlist->entries) 2123 pos = perf_evlist__first(evlist); 2124 else 2125 pos = perf_evsel__next(pos); 2126 goto browse_hists; 2127 case K_UNTAB: 2128 if (pos->node.prev == &evlist->entries) 2129 pos = perf_evlist__last(evlist); 2130 else 2131 pos = perf_evsel__prev(pos); 2132 goto browse_hists; 2133 case K_SWITCH_INPUT_DATA: 2134 case 'q': 2135 case CTRL('c'): 2136 goto out; 2137 case K_ESC: 2138 default: 2139 continue; 2140 } 2141 case K_LEFT: 2142 continue; 2143 case K_ESC: 2144 if (!ui_browser__dialog_yesno(&menu->b, 2145 "Do you really want to exit?")) 2146 continue; 2147 /* Fall thru */ 2148 case 'q': 2149 case CTRL('c'): 2150 goto out; 2151 default: 2152 continue; 2153 } 2154 } 2155 2156 out: 2157 ui_browser__hide(&menu->b); 2158 return key; 2159 } 2160 2161 static bool filter_group_entries(struct ui_browser *browser __maybe_unused, 2162 void *entry) 2163 { 2164 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 2165 2166 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) 2167 return true; 2168 2169 return false; 2170 } 2171 2172 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 2173 int nr_entries, const char *help, 2174 struct hist_browser_timer *hbt, 2175 float min_pcnt, 2176 struct perf_env *env) 2177 { 2178 struct perf_evsel *pos; 2179 struct perf_evsel_menu menu = { 2180 .b = { 2181 .entries = &evlist->entries, 2182 .refresh = ui_browser__list_head_refresh, 2183 .seek = ui_browser__list_head_seek, 2184 .write = perf_evsel_menu__write, 2185 .filter = filter_group_entries, 2186 .nr_entries = nr_entries, 2187 .priv = evlist, 2188 }, 2189 .min_pcnt = min_pcnt, 2190 .env = env, 2191 }; 2192 2193 ui_helpline__push("Press ESC to exit"); 2194 2195 evlist__for_each(evlist, pos) { 2196 const char *ev_name = perf_evsel__name(pos); 2197 size_t line_len = strlen(ev_name) + 7; 2198 2199 if (menu.b.width < line_len) 2200 menu.b.width = line_len; 2201 } 2202 2203 return perf_evsel_menu__run(&menu, nr_entries, help, hbt); 2204 } 2205 2206 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 2207 struct hist_browser_timer *hbt, 2208 float min_pcnt, 2209 struct perf_env *env) 2210 { 2211 int nr_entries = evlist->nr_entries; 2212 2213 single_entry: 2214 if (nr_entries == 1) { 2215 struct perf_evsel *first = perf_evlist__first(evlist); 2216 2217 return perf_evsel__hists_browse(first, nr_entries, help, 2218 false, hbt, min_pcnt, 2219 env); 2220 } 2221 2222 if (symbol_conf.event_group) { 2223 struct perf_evsel *pos; 2224 2225 nr_entries = 0; 2226 evlist__for_each(evlist, pos) { 2227 if (perf_evsel__is_group_leader(pos)) 2228 nr_entries++; 2229 } 2230 2231 if (nr_entries == 1) 2232 goto single_entry; 2233 } 2234 2235 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, 2236 hbt, min_pcnt, env); 2237 } 2238