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