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