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