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