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