| .. | .. |
|---|
| 6 | 6 | #include <stdlib.h> |
|---|
| 7 | 7 | #include <string.h> |
|---|
| 8 | 8 | #include <linux/rbtree.h> |
|---|
| 9 | +#include <linux/string.h> |
|---|
| 9 | 10 | #include <sys/ttydefaults.h> |
|---|
| 11 | +#include <linux/time64.h> |
|---|
| 12 | +#include <linux/zalloc.h> |
|---|
| 10 | 13 | |
|---|
| 14 | +#include "../../util/debug.h" |
|---|
| 15 | +#include "../../util/dso.h" |
|---|
| 16 | +#include "../../util/callchain.h" |
|---|
| 11 | 17 | #include "../../util/evsel.h" |
|---|
| 12 | 18 | #include "../../util/evlist.h" |
|---|
| 19 | +#include "../../util/header.h" |
|---|
| 13 | 20 | #include "../../util/hist.h" |
|---|
| 21 | +#include "../../util/machine.h" |
|---|
| 22 | +#include "../../util/map.h" |
|---|
| 23 | +#include "../../util/maps.h" |
|---|
| 24 | +#include "../../util/symbol.h" |
|---|
| 25 | +#include "../../util/map_symbol.h" |
|---|
| 26 | +#include "../../util/branch.h" |
|---|
| 14 | 27 | #include "../../util/pstack.h" |
|---|
| 15 | 28 | #include "../../util/sort.h" |
|---|
| 16 | | -#include "../../util/util.h" |
|---|
| 17 | 29 | #include "../../util/top.h" |
|---|
| 18 | 30 | #include "../../util/thread.h" |
|---|
| 31 | +#include "../../util/block-info.h" |
|---|
| 19 | 32 | #include "../../arch/common.h" |
|---|
| 33 | +#include "../../perf.h" |
|---|
| 20 | 34 | |
|---|
| 21 | 35 | #include "../browsers/hists.h" |
|---|
| 22 | 36 | #include "../helpline.h" |
|---|
| .. | .. |
|---|
| 27 | 41 | #include "srcline.h" |
|---|
| 28 | 42 | #include "string2.h" |
|---|
| 29 | 43 | #include "units.h" |
|---|
| 44 | +#include "time-utils.h" |
|---|
| 30 | 45 | |
|---|
| 31 | | -#include "sane_ctype.h" |
|---|
| 46 | +#include <linux/ctype.h> |
|---|
| 32 | 47 | |
|---|
| 33 | 48 | extern void hist_browser__init_hpp(void); |
|---|
| 34 | 49 | |
|---|
| .. | .. |
|---|
| 49 | 64 | struct hists *hists = browser->hists; |
|---|
| 50 | 65 | int unfolded_rows = 0; |
|---|
| 51 | 66 | |
|---|
| 52 | | - for (nd = rb_first(&hists->entries); |
|---|
| 67 | + for (nd = rb_first_cached(&hists->entries); |
|---|
| 53 | 68 | (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; |
|---|
| 54 | 69 | nd = rb_hierarchy_next(nd)) { |
|---|
| 55 | 70 | struct hist_entry *he = |
|---|
| .. | .. |
|---|
| 267 | 282 | if (he->has_no_entry) |
|---|
| 268 | 283 | return 1; |
|---|
| 269 | 284 | |
|---|
| 270 | | - node = rb_first(&he->hroot_out); |
|---|
| 285 | + node = rb_first_cached(&he->hroot_out); |
|---|
| 271 | 286 | while (node) { |
|---|
| 272 | 287 | float percent; |
|---|
| 273 | 288 | |
|---|
| .. | .. |
|---|
| 372 | 387 | he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); |
|---|
| 373 | 388 | callchain__init_have_children(&he->sorted_chain); |
|---|
| 374 | 389 | } else { |
|---|
| 375 | | - he->has_children = !RB_EMPTY_ROOT(&he->hroot_out); |
|---|
| 390 | + he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root); |
|---|
| 376 | 391 | } |
|---|
| 377 | 392 | |
|---|
| 378 | 393 | he->init_have_children = true; |
|---|
| 394 | +} |
|---|
| 395 | + |
|---|
| 396 | +static bool hist_browser__selection_has_children(struct hist_browser *browser) |
|---|
| 397 | +{ |
|---|
| 398 | + struct hist_entry *he = browser->he_selection; |
|---|
| 399 | + struct map_symbol *ms = browser->selection; |
|---|
| 400 | + |
|---|
| 401 | + if (!he || !ms) |
|---|
| 402 | + return false; |
|---|
| 403 | + |
|---|
| 404 | + if (ms == &he->ms) |
|---|
| 405 | + return he->has_children; |
|---|
| 406 | + |
|---|
| 407 | + return container_of(ms, struct callchain_list, ms)->has_children; |
|---|
| 408 | +} |
|---|
| 409 | + |
|---|
| 410 | +static bool hist_browser__he_selection_unfolded(struct hist_browser *browser) |
|---|
| 411 | +{ |
|---|
| 412 | + return browser->he_selection ? browser->he_selection->unfolded : false; |
|---|
| 413 | +} |
|---|
| 414 | + |
|---|
| 415 | +static bool hist_browser__selection_unfolded(struct hist_browser *browser) |
|---|
| 416 | +{ |
|---|
| 417 | + struct hist_entry *he = browser->he_selection; |
|---|
| 418 | + struct map_symbol *ms = browser->selection; |
|---|
| 419 | + |
|---|
| 420 | + if (!he || !ms) |
|---|
| 421 | + return false; |
|---|
| 422 | + |
|---|
| 423 | + if (ms == &he->ms) |
|---|
| 424 | + return he->unfolded; |
|---|
| 425 | + |
|---|
| 426 | + return container_of(ms, struct callchain_list, ms)->unfolded; |
|---|
| 427 | +} |
|---|
| 428 | + |
|---|
| 429 | +static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size) |
|---|
| 430 | +{ |
|---|
| 431 | + struct hist_entry *he = browser->he_selection; |
|---|
| 432 | + struct map_symbol *ms = browser->selection; |
|---|
| 433 | + struct callchain_list *callchain_entry; |
|---|
| 434 | + |
|---|
| 435 | + if (!he || !ms) |
|---|
| 436 | + return NULL; |
|---|
| 437 | + |
|---|
| 438 | + if (ms == &he->ms) { |
|---|
| 439 | + hist_entry__sym_snprintf(he, bf, size, 0); |
|---|
| 440 | + return bf + 4; // skip the level, e.g. '[k] ' |
|---|
| 441 | + } |
|---|
| 442 | + |
|---|
| 443 | + callchain_entry = container_of(ms, struct callchain_list, ms); |
|---|
| 444 | + return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso); |
|---|
| 379 | 445 | } |
|---|
| 380 | 446 | |
|---|
| 381 | 447 | static bool hist_browser__toggle_fold(struct hist_browser *browser) |
|---|
| .. | .. |
|---|
| 508 | 574 | struct hist_entry *child; |
|---|
| 509 | 575 | int n = 0; |
|---|
| 510 | 576 | |
|---|
| 511 | | - for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) { |
|---|
| 577 | + for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) { |
|---|
| 512 | 578 | child = rb_entry(nd, struct hist_entry, rb_node); |
|---|
| 513 | 579 | percent = hist_entry__get_percent_limit(child); |
|---|
| 514 | 580 | if (!child->filtered && percent >= hb->min_pcnt) |
|---|
| .. | .. |
|---|
| 566 | 632 | struct rb_node *nd; |
|---|
| 567 | 633 | struct hist_entry *he; |
|---|
| 568 | 634 | |
|---|
| 569 | | - nd = rb_first(&browser->hists->entries); |
|---|
| 635 | + nd = rb_first_cached(&browser->hists->entries); |
|---|
| 570 | 636 | while (nd) { |
|---|
| 571 | 637 | he = rb_entry(nd, struct hist_entry, rb_node); |
|---|
| 572 | 638 | |
|---|
| .. | .. |
|---|
| 611 | 677 | return browser->title ? browser->title(browser, bf, size) : 0; |
|---|
| 612 | 678 | } |
|---|
| 613 | 679 | |
|---|
| 614 | | -int hist_browser__run(struct hist_browser *browser, const char *help, |
|---|
| 615 | | - bool warn_lost_event) |
|---|
| 680 | +static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key) |
|---|
| 616 | 681 | { |
|---|
| 617 | | - int key; |
|---|
| 682 | + switch (key) { |
|---|
| 683 | + case K_TIMER: { |
|---|
| 684 | + struct hist_browser_timer *hbt = browser->hbt; |
|---|
| 685 | + u64 nr_entries; |
|---|
| 686 | + |
|---|
| 687 | + WARN_ON_ONCE(!hbt); |
|---|
| 688 | + |
|---|
| 689 | + if (hbt) |
|---|
| 690 | + hbt->timer(hbt->arg); |
|---|
| 691 | + |
|---|
| 692 | + if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy) |
|---|
| 693 | + hist_browser__update_nr_entries(browser); |
|---|
| 694 | + |
|---|
| 695 | + nr_entries = hist_browser__nr_entries(browser); |
|---|
| 696 | + ui_browser__update_nr_entries(&browser->b, nr_entries); |
|---|
| 697 | + |
|---|
| 698 | + if (warn_lost_event && |
|---|
| 699 | + (browser->hists->stats.nr_lost_warned != |
|---|
| 700 | + browser->hists->stats.nr_events[PERF_RECORD_LOST])) { |
|---|
| 701 | + browser->hists->stats.nr_lost_warned = |
|---|
| 702 | + browser->hists->stats.nr_events[PERF_RECORD_LOST]; |
|---|
| 703 | + ui_browser__warn_lost_events(&browser->b); |
|---|
| 704 | + } |
|---|
| 705 | + |
|---|
| 706 | + hist_browser__title(browser, title, size); |
|---|
| 707 | + ui_browser__show_title(&browser->b, title); |
|---|
| 708 | + break; |
|---|
| 709 | + } |
|---|
| 710 | + case 'D': { /* Debug */ |
|---|
| 711 | + struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node); |
|---|
| 712 | + static int seq; |
|---|
| 713 | + |
|---|
| 714 | + ui_helpline__pop(); |
|---|
| 715 | + ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", |
|---|
| 716 | + seq++, browser->b.nr_entries, browser->hists->nr_entries, |
|---|
| 717 | + browser->b.extra_title_lines, browser->b.rows, |
|---|
| 718 | + browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows); |
|---|
| 719 | + } |
|---|
| 720 | + break; |
|---|
| 721 | + case 'C': |
|---|
| 722 | + /* Collapse the whole world. */ |
|---|
| 723 | + hist_browser__set_folding(browser, false); |
|---|
| 724 | + break; |
|---|
| 725 | + case 'c': |
|---|
| 726 | + /* Collapse the selected entry. */ |
|---|
| 727 | + hist_browser__set_folding_selected(browser, false); |
|---|
| 728 | + break; |
|---|
| 729 | + case 'E': |
|---|
| 730 | + /* Expand the whole world. */ |
|---|
| 731 | + hist_browser__set_folding(browser, true); |
|---|
| 732 | + break; |
|---|
| 733 | + case 'e': |
|---|
| 734 | + /* Expand the selected entry. */ |
|---|
| 735 | + hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser)); |
|---|
| 736 | + break; |
|---|
| 737 | + case 'H': |
|---|
| 738 | + browser->show_headers = !browser->show_headers; |
|---|
| 739 | + hist_browser__update_rows(browser); |
|---|
| 740 | + break; |
|---|
| 741 | + case '+': |
|---|
| 742 | + if (hist_browser__toggle_fold(browser)) |
|---|
| 743 | + break; |
|---|
| 744 | + /* fall thru */ |
|---|
| 745 | + default: |
|---|
| 746 | + return -1; |
|---|
| 747 | + } |
|---|
| 748 | + |
|---|
| 749 | + return 0; |
|---|
| 750 | +} |
|---|
| 751 | + |
|---|
| 752 | +int hist_browser__run(struct hist_browser *browser, const char *help, |
|---|
| 753 | + bool warn_lost_event, int key) |
|---|
| 754 | +{ |
|---|
| 618 | 755 | char title[160]; |
|---|
| 619 | 756 | struct hist_browser_timer *hbt = browser->hbt; |
|---|
| 620 | 757 | int delay_secs = hbt ? hbt->refresh : 0; |
|---|
| .. | .. |
|---|
| 627 | 764 | if (ui_browser__show(&browser->b, title, "%s", help) < 0) |
|---|
| 628 | 765 | return -1; |
|---|
| 629 | 766 | |
|---|
| 767 | + if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) |
|---|
| 768 | + goto out; |
|---|
| 769 | + |
|---|
| 630 | 770 | while (1) { |
|---|
| 631 | 771 | key = ui_browser__run(&browser->b, delay_secs); |
|---|
| 632 | 772 | |
|---|
| 633 | | - switch (key) { |
|---|
| 634 | | - case K_TIMER: { |
|---|
| 635 | | - u64 nr_entries; |
|---|
| 636 | | - |
|---|
| 637 | | - WARN_ON_ONCE(!hbt); |
|---|
| 638 | | - |
|---|
| 639 | | - if (hbt) |
|---|
| 640 | | - hbt->timer(hbt->arg); |
|---|
| 641 | | - |
|---|
| 642 | | - if (hist_browser__has_filter(browser) || |
|---|
| 643 | | - symbol_conf.report_hierarchy) |
|---|
| 644 | | - hist_browser__update_nr_entries(browser); |
|---|
| 645 | | - |
|---|
| 646 | | - nr_entries = hist_browser__nr_entries(browser); |
|---|
| 647 | | - ui_browser__update_nr_entries(&browser->b, nr_entries); |
|---|
| 648 | | - |
|---|
| 649 | | - if (warn_lost_event && |
|---|
| 650 | | - (browser->hists->stats.nr_lost_warned != |
|---|
| 651 | | - browser->hists->stats.nr_events[PERF_RECORD_LOST])) { |
|---|
| 652 | | - browser->hists->stats.nr_lost_warned = |
|---|
| 653 | | - browser->hists->stats.nr_events[PERF_RECORD_LOST]; |
|---|
| 654 | | - ui_browser__warn_lost_events(&browser->b); |
|---|
| 655 | | - } |
|---|
| 656 | | - |
|---|
| 657 | | - hist_browser__title(browser, title, sizeof(title)); |
|---|
| 658 | | - ui_browser__show_title(&browser->b, title); |
|---|
| 659 | | - continue; |
|---|
| 660 | | - } |
|---|
| 661 | | - case 'D': { /* Debug */ |
|---|
| 662 | | - static int seq; |
|---|
| 663 | | - struct hist_entry *h = rb_entry(browser->b.top, |
|---|
| 664 | | - struct hist_entry, rb_node); |
|---|
| 665 | | - ui_helpline__pop(); |
|---|
| 666 | | - ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", |
|---|
| 667 | | - seq++, browser->b.nr_entries, |
|---|
| 668 | | - browser->hists->nr_entries, |
|---|
| 669 | | - browser->b.extra_title_lines, |
|---|
| 670 | | - browser->b.rows, |
|---|
| 671 | | - browser->b.index, |
|---|
| 672 | | - browser->b.top_idx, |
|---|
| 673 | | - h->row_offset, h->nr_rows); |
|---|
| 674 | | - } |
|---|
| 773 | + if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) |
|---|
| 675 | 774 | break; |
|---|
| 676 | | - case 'C': |
|---|
| 677 | | - /* Collapse the whole world. */ |
|---|
| 678 | | - hist_browser__set_folding(browser, false); |
|---|
| 679 | | - break; |
|---|
| 680 | | - case 'c': |
|---|
| 681 | | - /* Collapse the selected entry. */ |
|---|
| 682 | | - hist_browser__set_folding_selected(browser, false); |
|---|
| 683 | | - break; |
|---|
| 684 | | - case 'E': |
|---|
| 685 | | - /* Expand the whole world. */ |
|---|
| 686 | | - hist_browser__set_folding(browser, true); |
|---|
| 687 | | - break; |
|---|
| 688 | | - case 'e': |
|---|
| 689 | | - /* Expand the selected entry. */ |
|---|
| 690 | | - hist_browser__set_folding_selected(browser, true); |
|---|
| 691 | | - break; |
|---|
| 692 | | - case 'H': |
|---|
| 693 | | - browser->show_headers = !browser->show_headers; |
|---|
| 694 | | - hist_browser__update_rows(browser); |
|---|
| 695 | | - break; |
|---|
| 696 | | - case K_ENTER: |
|---|
| 697 | | - if (hist_browser__toggle_fold(browser)) |
|---|
| 698 | | - break; |
|---|
| 699 | | - /* fall thru */ |
|---|
| 700 | | - default: |
|---|
| 701 | | - goto out; |
|---|
| 702 | | - } |
|---|
| 703 | 775 | } |
|---|
| 704 | 776 | out: |
|---|
| 705 | 777 | ui_browser__hide(&browser->b); |
|---|
| .. | .. |
|---|
| 1225 | 1297 | hist_browser__hpp_color_overhead_guest_us; |
|---|
| 1226 | 1298 | perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = |
|---|
| 1227 | 1299 | hist_browser__hpp_color_overhead_acc; |
|---|
| 1300 | + |
|---|
| 1301 | + res_sample_init(); |
|---|
| 1228 | 1302 | } |
|---|
| 1229 | 1303 | |
|---|
| 1230 | 1304 | static int hist_browser__show_entry(struct hist_browser *browser, |
|---|
| .. | .. |
|---|
| 1467 | 1541 | int i = 0; |
|---|
| 1468 | 1542 | |
|---|
| 1469 | 1543 | width -= fmt->entry(fmt, &hpp, entry); |
|---|
| 1470 | | - ui_browser__printf(&browser->b, "%s", ltrim(s)); |
|---|
| 1544 | + ui_browser__printf(&browser->b, "%s", skip_spaces(s)); |
|---|
| 1471 | 1545 | |
|---|
| 1472 | 1546 | while (isspace(s[i++])) |
|---|
| 1473 | 1547 | width++; |
|---|
| .. | .. |
|---|
| 1683 | 1757 | ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); |
|---|
| 1684 | 1758 | dummy_hpp.buf[ret] = '\0'; |
|---|
| 1685 | 1759 | |
|---|
| 1686 | | - start = trim(dummy_hpp.buf); |
|---|
| 1760 | + start = strim(dummy_hpp.buf); |
|---|
| 1687 | 1761 | ret = strlen(start); |
|---|
| 1688 | 1762 | |
|---|
| 1689 | 1763 | if (start != dummy_hpp.buf) |
|---|
| .. | .. |
|---|
| 1742 | 1816 | struct hist_browser *hb; |
|---|
| 1743 | 1817 | |
|---|
| 1744 | 1818 | hb = container_of(browser, struct hist_browser, b); |
|---|
| 1745 | | - browser->top = rb_first(&hb->hists->entries); |
|---|
| 1819 | + browser->top = rb_first_cached(&hb->hists->entries); |
|---|
| 1746 | 1820 | } |
|---|
| 1747 | 1821 | } |
|---|
| 1748 | 1822 | |
|---|
| .. | .. |
|---|
| 1769 | 1843 | continue; |
|---|
| 1770 | 1844 | } |
|---|
| 1771 | 1845 | |
|---|
| 1772 | | - percent = hist_entry__get_percent_limit(h); |
|---|
| 1846 | + if (symbol_conf.report_individual_block) |
|---|
| 1847 | + percent = block_info__total_cycles_percent(h); |
|---|
| 1848 | + else |
|---|
| 1849 | + percent = hist_entry__get_percent_limit(h); |
|---|
| 1850 | + |
|---|
| 1773 | 1851 | if (percent < hb->min_pcnt) |
|---|
| 1774 | 1852 | continue; |
|---|
| 1775 | 1853 | |
|---|
| .. | .. |
|---|
| 2067 | 2145 | advance_hpp(&hpp, ret); |
|---|
| 2068 | 2146 | } |
|---|
| 2069 | 2147 | |
|---|
| 2070 | | - printed += fprintf(fp, "%s\n", rtrim(s)); |
|---|
| 2148 | + strim(s); |
|---|
| 2149 | + printed += fprintf(fp, "%s\n", s); |
|---|
| 2071 | 2150 | |
|---|
| 2072 | 2151 | if (he->leaf && folded_sign == '-') { |
|---|
| 2073 | 2152 | printed += hist_browser__fprintf_callchain(browser, he, fp, |
|---|
| .. | .. |
|---|
| 2178 | 2257 | } |
|---|
| 2179 | 2258 | |
|---|
| 2180 | 2259 | static struct hist_browser * |
|---|
| 2181 | | -perf_evsel_browser__new(struct perf_evsel *evsel, |
|---|
| 2260 | +perf_evsel_browser__new(struct evsel *evsel, |
|---|
| 2182 | 2261 | struct hist_browser_timer *hbt, |
|---|
| 2183 | 2262 | struct perf_env *env, |
|---|
| 2184 | 2263 | struct annotation_options *annotation_opts) |
|---|
| .. | .. |
|---|
| 2209 | 2288 | return browser->he_selection->thread; |
|---|
| 2210 | 2289 | } |
|---|
| 2211 | 2290 | |
|---|
| 2291 | +static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser) |
|---|
| 2292 | +{ |
|---|
| 2293 | + return browser->he_selection ? browser->he_selection->res_samples : NULL; |
|---|
| 2294 | +} |
|---|
| 2295 | + |
|---|
| 2212 | 2296 | /* Check whether the browser is for 'top' or 'report' */ |
|---|
| 2213 | 2297 | static inline bool is_report_browser(void *timer) |
|---|
| 2214 | 2298 | { |
|---|
| .. | .. |
|---|
| 2223 | 2307 | if (!is_report_browser(hbt)) { |
|---|
| 2224 | 2308 | struct perf_top *top = hbt->arg; |
|---|
| 2225 | 2309 | |
|---|
| 2310 | + printed += scnprintf(bf + printed, size - printed, |
|---|
| 2311 | + " lost: %" PRIu64 "/%" PRIu64, |
|---|
| 2312 | + top->lost, top->lost_total); |
|---|
| 2313 | + |
|---|
| 2314 | + printed += scnprintf(bf + printed, size - printed, |
|---|
| 2315 | + " drop: %" PRIu64 "/%" PRIu64, |
|---|
| 2316 | + top->drop, top->drop_total); |
|---|
| 2317 | + |
|---|
| 2226 | 2318 | if (top->zero) |
|---|
| 2227 | 2319 | printed += scnprintf(bf + printed, size - printed, " [z]"); |
|---|
| 2320 | + |
|---|
| 2321 | + perf_top__reset_sample_counters(top); |
|---|
| 2228 | 2322 | } |
|---|
| 2323 | + |
|---|
| 2229 | 2324 | |
|---|
| 2230 | 2325 | return printed; |
|---|
| 2231 | 2326 | } |
|---|
| .. | .. |
|---|
| 2308 | 2403 | closedir(pwd_dir); |
|---|
| 2309 | 2404 | |
|---|
| 2310 | 2405 | if (nr_options) { |
|---|
| 2311 | | - choice = ui__popup_menu(nr_options, options); |
|---|
| 2406 | + choice = ui__popup_menu(nr_options, options, NULL); |
|---|
| 2312 | 2407 | if (choice < nr_options && choice >= 0) { |
|---|
| 2313 | 2408 | tmp = strdup(abs_path[choice]); |
|---|
| 2314 | 2409 | if (tmp) { |
|---|
| .. | .. |
|---|
| 2328 | 2423 | } |
|---|
| 2329 | 2424 | |
|---|
| 2330 | 2425 | struct popup_action { |
|---|
| 2426 | + unsigned long time; |
|---|
| 2331 | 2427 | struct thread *thread; |
|---|
| 2332 | 2428 | struct map_symbol ms; |
|---|
| 2333 | 2429 | int socket; |
|---|
| 2430 | + struct evsel *evsel; |
|---|
| 2431 | + enum rstype rstype; |
|---|
| 2334 | 2432 | |
|---|
| 2335 | 2433 | int (*fn)(struct hist_browser *browser, struct popup_action *act); |
|---|
| 2336 | 2434 | }; |
|---|
| .. | .. |
|---|
| 2338 | 2436 | static int |
|---|
| 2339 | 2437 | do_annotate(struct hist_browser *browser, struct popup_action *act) |
|---|
| 2340 | 2438 | { |
|---|
| 2341 | | - struct perf_evsel *evsel; |
|---|
| 2439 | + struct evsel *evsel; |
|---|
| 2342 | 2440 | struct annotation *notes; |
|---|
| 2343 | 2441 | struct hist_entry *he; |
|---|
| 2344 | 2442 | int err; |
|---|
| .. | .. |
|---|
| 2351 | 2449 | if (!notes->src) |
|---|
| 2352 | 2450 | return 0; |
|---|
| 2353 | 2451 | |
|---|
| 2354 | | - evsel = hists_to_evsel(browser->hists); |
|---|
| 2452 | + if (browser->block_evsel) |
|---|
| 2453 | + evsel = browser->block_evsel; |
|---|
| 2454 | + else |
|---|
| 2455 | + evsel = hists_to_evsel(browser->hists); |
|---|
| 2456 | + |
|---|
| 2355 | 2457 | err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, |
|---|
| 2356 | 2458 | browser->annotation_opts); |
|---|
| 2357 | 2459 | he = hist_browser__selected_entry(browser); |
|---|
| .. | .. |
|---|
| 2368 | 2470 | return 0; |
|---|
| 2369 | 2471 | } |
|---|
| 2370 | 2472 | |
|---|
| 2473 | +static struct symbol *symbol__new_unresolved(u64 addr, struct map *map) |
|---|
| 2474 | +{ |
|---|
| 2475 | + struct annotated_source *src; |
|---|
| 2476 | + struct symbol *sym; |
|---|
| 2477 | + char name[64]; |
|---|
| 2478 | + |
|---|
| 2479 | + snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr); |
|---|
| 2480 | + |
|---|
| 2481 | + sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name); |
|---|
| 2482 | + if (sym) { |
|---|
| 2483 | + src = symbol__hists(sym, 1); |
|---|
| 2484 | + if (!src) { |
|---|
| 2485 | + symbol__delete(sym); |
|---|
| 2486 | + return NULL; |
|---|
| 2487 | + } |
|---|
| 2488 | + |
|---|
| 2489 | + dso__insert_symbol(map->dso, sym); |
|---|
| 2490 | + } |
|---|
| 2491 | + |
|---|
| 2492 | + return sym; |
|---|
| 2493 | +} |
|---|
| 2494 | + |
|---|
| 2371 | 2495 | static int |
|---|
| 2372 | 2496 | add_annotate_opt(struct hist_browser *browser __maybe_unused, |
|---|
| 2373 | 2497 | struct popup_action *act, char **optstr, |
|---|
| 2374 | | - struct map *map, struct symbol *sym) |
|---|
| 2498 | + struct map_symbol *ms, |
|---|
| 2499 | + u64 addr) |
|---|
| 2375 | 2500 | { |
|---|
| 2376 | | - if (sym == NULL || map->dso->annotate_warned) |
|---|
| 2501 | + if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned) |
|---|
| 2377 | 2502 | return 0; |
|---|
| 2378 | 2503 | |
|---|
| 2379 | | - if (asprintf(optstr, "Annotate %s", sym->name) < 0) |
|---|
| 2504 | + if (!ms->sym) |
|---|
| 2505 | + ms->sym = symbol__new_unresolved(addr, ms->map); |
|---|
| 2506 | + |
|---|
| 2507 | + if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL) |
|---|
| 2380 | 2508 | return 0; |
|---|
| 2381 | 2509 | |
|---|
| 2382 | | - act->ms.map = map; |
|---|
| 2383 | | - act->ms.sym = sym; |
|---|
| 2510 | + if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) |
|---|
| 2511 | + return 0; |
|---|
| 2512 | + |
|---|
| 2513 | + act->ms = *ms; |
|---|
| 2384 | 2514 | act->fn = do_annotate; |
|---|
| 2385 | 2515 | return 1; |
|---|
| 2386 | 2516 | } |
|---|
| .. | .. |
|---|
| 2447 | 2577 | return 1; |
|---|
| 2448 | 2578 | } |
|---|
| 2449 | 2579 | |
|---|
| 2450 | | -static int |
|---|
| 2451 | | -do_zoom_dso(struct hist_browser *browser, struct popup_action *act) |
|---|
| 2580 | +static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map) |
|---|
| 2452 | 2581 | { |
|---|
| 2453 | | - struct map *map = act->ms.map; |
|---|
| 2454 | | - |
|---|
| 2455 | 2582 | if (!hists__has(browser->hists, dso) || map == NULL) |
|---|
| 2456 | 2583 | return 0; |
|---|
| 2457 | 2584 | |
|---|
| .. | .. |
|---|
| 2474 | 2601 | } |
|---|
| 2475 | 2602 | |
|---|
| 2476 | 2603 | static int |
|---|
| 2604 | +do_zoom_dso(struct hist_browser *browser, struct popup_action *act) |
|---|
| 2605 | +{ |
|---|
| 2606 | + return hists_browser__zoom_map(browser, act->ms.map); |
|---|
| 2607 | +} |
|---|
| 2608 | + |
|---|
| 2609 | +static int |
|---|
| 2477 | 2610 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, |
|---|
| 2478 | 2611 | char **optstr, struct map *map) |
|---|
| 2479 | 2612 | { |
|---|
| 2480 | 2613 | if (!hists__has(browser->hists, dso) || map == NULL) |
|---|
| 2481 | 2614 | return 0; |
|---|
| 2482 | 2615 | |
|---|
| 2483 | | - if (asprintf(optstr, "Zoom %s %s DSO", |
|---|
| 2616 | + if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)", |
|---|
| 2484 | 2617 | browser->hists->dso_filter ? "out of" : "into", |
|---|
| 2485 | 2618 | __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) |
|---|
| 2486 | 2619 | return 0; |
|---|
| 2487 | 2620 | |
|---|
| 2488 | 2621 | act->ms.map = map; |
|---|
| 2489 | 2622 | act->fn = do_zoom_dso; |
|---|
| 2623 | + return 1; |
|---|
| 2624 | +} |
|---|
| 2625 | + |
|---|
| 2626 | +static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused) |
|---|
| 2627 | +{ |
|---|
| 2628 | + hist_browser__toggle_fold(browser); |
|---|
| 2629 | + return 0; |
|---|
| 2630 | +} |
|---|
| 2631 | + |
|---|
| 2632 | +static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) |
|---|
| 2633 | +{ |
|---|
| 2634 | + char sym_name[512]; |
|---|
| 2635 | + |
|---|
| 2636 | + if (!hist_browser__selection_has_children(browser)) |
|---|
| 2637 | + return 0; |
|---|
| 2638 | + |
|---|
| 2639 | + if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)", |
|---|
| 2640 | + hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand", |
|---|
| 2641 | + hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0) |
|---|
| 2642 | + return 0; |
|---|
| 2643 | + |
|---|
| 2644 | + act->fn = do_toggle_callchain; |
|---|
| 2490 | 2645 | return 1; |
|---|
| 2491 | 2646 | } |
|---|
| 2492 | 2647 | |
|---|
| .. | .. |
|---|
| 2517 | 2672 | do_run_script(struct hist_browser *browser __maybe_unused, |
|---|
| 2518 | 2673 | struct popup_action *act) |
|---|
| 2519 | 2674 | { |
|---|
| 2520 | | - char script_opt[64]; |
|---|
| 2521 | | - memset(script_opt, 0, sizeof(script_opt)); |
|---|
| 2675 | + char *script_opt; |
|---|
| 2676 | + int len; |
|---|
| 2677 | + int n = 0; |
|---|
| 2522 | 2678 | |
|---|
| 2679 | + len = 100; |
|---|
| 2680 | + if (act->thread) |
|---|
| 2681 | + len += strlen(thread__comm_str(act->thread)); |
|---|
| 2682 | + else if (act->ms.sym) |
|---|
| 2683 | + len += strlen(act->ms.sym->name); |
|---|
| 2684 | + script_opt = malloc(len); |
|---|
| 2685 | + if (!script_opt) |
|---|
| 2686 | + return -1; |
|---|
| 2687 | + |
|---|
| 2688 | + script_opt[0] = 0; |
|---|
| 2523 | 2689 | if (act->thread) { |
|---|
| 2524 | | - scnprintf(script_opt, sizeof(script_opt), " -c %s ", |
|---|
| 2690 | + n = scnprintf(script_opt, len, " -c %s ", |
|---|
| 2525 | 2691 | thread__comm_str(act->thread)); |
|---|
| 2526 | 2692 | } else if (act->ms.sym) { |
|---|
| 2527 | | - scnprintf(script_opt, sizeof(script_opt), " -S %s ", |
|---|
| 2693 | + n = scnprintf(script_opt, len, " -S %s ", |
|---|
| 2528 | 2694 | act->ms.sym->name); |
|---|
| 2529 | 2695 | } |
|---|
| 2530 | 2696 | |
|---|
| 2531 | | - script_browse(script_opt); |
|---|
| 2697 | + if (act->time) { |
|---|
| 2698 | + char start[32], end[32]; |
|---|
| 2699 | + unsigned long starttime = act->time; |
|---|
| 2700 | + unsigned long endtime = act->time + symbol_conf.time_quantum; |
|---|
| 2701 | + |
|---|
| 2702 | + if (starttime == endtime) { /* Display 1ms as fallback */ |
|---|
| 2703 | + starttime -= 1*NSEC_PER_MSEC; |
|---|
| 2704 | + endtime += 1*NSEC_PER_MSEC; |
|---|
| 2705 | + } |
|---|
| 2706 | + timestamp__scnprintf_usec(starttime, start, sizeof start); |
|---|
| 2707 | + timestamp__scnprintf_usec(endtime, end, sizeof end); |
|---|
| 2708 | + n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); |
|---|
| 2709 | + } |
|---|
| 2710 | + |
|---|
| 2711 | + script_browse(script_opt, act->evsel); |
|---|
| 2712 | + free(script_opt); |
|---|
| 2532 | 2713 | return 0; |
|---|
| 2533 | 2714 | } |
|---|
| 2534 | 2715 | |
|---|
| 2535 | 2716 | static int |
|---|
| 2536 | | -add_script_opt(struct hist_browser *browser __maybe_unused, |
|---|
| 2537 | | - struct popup_action *act, char **optstr, |
|---|
| 2538 | | - struct thread *thread, struct symbol *sym) |
|---|
| 2717 | +do_res_sample_script(struct hist_browser *browser __maybe_unused, |
|---|
| 2718 | + struct popup_action *act) |
|---|
| 2539 | 2719 | { |
|---|
| 2720 | + struct hist_entry *he; |
|---|
| 2721 | + |
|---|
| 2722 | + he = hist_browser__selected_entry(browser); |
|---|
| 2723 | + res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); |
|---|
| 2724 | + return 0; |
|---|
| 2725 | +} |
|---|
| 2726 | + |
|---|
| 2727 | +static int |
|---|
| 2728 | +add_script_opt_2(struct hist_browser *browser __maybe_unused, |
|---|
| 2729 | + struct popup_action *act, char **optstr, |
|---|
| 2730 | + struct thread *thread, struct symbol *sym, |
|---|
| 2731 | + struct evsel *evsel, const char *tstr) |
|---|
| 2732 | +{ |
|---|
| 2733 | + |
|---|
| 2540 | 2734 | if (thread) { |
|---|
| 2541 | | - if (asprintf(optstr, "Run scripts for samples of thread [%s]", |
|---|
| 2542 | | - thread__comm_str(thread)) < 0) |
|---|
| 2735 | + if (asprintf(optstr, "Run scripts for samples of thread [%s]%s", |
|---|
| 2736 | + thread__comm_str(thread), tstr) < 0) |
|---|
| 2543 | 2737 | return 0; |
|---|
| 2544 | 2738 | } else if (sym) { |
|---|
| 2545 | | - if (asprintf(optstr, "Run scripts for samples of symbol [%s]", |
|---|
| 2546 | | - sym->name) < 0) |
|---|
| 2739 | + if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s", |
|---|
| 2740 | + sym->name, tstr) < 0) |
|---|
| 2547 | 2741 | return 0; |
|---|
| 2548 | 2742 | } else { |
|---|
| 2549 | | - if (asprintf(optstr, "Run scripts for all samples") < 0) |
|---|
| 2743 | + if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0) |
|---|
| 2550 | 2744 | return 0; |
|---|
| 2551 | 2745 | } |
|---|
| 2552 | 2746 | |
|---|
| 2553 | 2747 | act->thread = thread; |
|---|
| 2554 | 2748 | act->ms.sym = sym; |
|---|
| 2749 | + act->evsel = evsel; |
|---|
| 2555 | 2750 | act->fn = do_run_script; |
|---|
| 2751 | + return 1; |
|---|
| 2752 | +} |
|---|
| 2753 | + |
|---|
| 2754 | +static int |
|---|
| 2755 | +add_script_opt(struct hist_browser *browser, |
|---|
| 2756 | + struct popup_action *act, char **optstr, |
|---|
| 2757 | + struct thread *thread, struct symbol *sym, |
|---|
| 2758 | + struct evsel *evsel) |
|---|
| 2759 | +{ |
|---|
| 2760 | + int n, j; |
|---|
| 2761 | + struct hist_entry *he; |
|---|
| 2762 | + |
|---|
| 2763 | + n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); |
|---|
| 2764 | + |
|---|
| 2765 | + he = hist_browser__selected_entry(browser); |
|---|
| 2766 | + if (sort_order && strstr(sort_order, "time")) { |
|---|
| 2767 | + char tstr[128]; |
|---|
| 2768 | + |
|---|
| 2769 | + optstr++; |
|---|
| 2770 | + act++; |
|---|
| 2771 | + j = sprintf(tstr, " in "); |
|---|
| 2772 | + j += timestamp__scnprintf_usec(he->time, tstr + j, |
|---|
| 2773 | + sizeof tstr - j); |
|---|
| 2774 | + j += sprintf(tstr + j, "-"); |
|---|
| 2775 | + timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, |
|---|
| 2776 | + tstr + j, sizeof tstr - j); |
|---|
| 2777 | + n += add_script_opt_2(browser, act, optstr, thread, sym, |
|---|
| 2778 | + evsel, tstr); |
|---|
| 2779 | + act->time = he->time; |
|---|
| 2780 | + } |
|---|
| 2781 | + return n; |
|---|
| 2782 | +} |
|---|
| 2783 | + |
|---|
| 2784 | +static int |
|---|
| 2785 | +add_res_sample_opt(struct hist_browser *browser __maybe_unused, |
|---|
| 2786 | + struct popup_action *act, char **optstr, |
|---|
| 2787 | + struct res_sample *res_sample, |
|---|
| 2788 | + struct evsel *evsel, |
|---|
| 2789 | + enum rstype type) |
|---|
| 2790 | +{ |
|---|
| 2791 | + if (!res_sample) |
|---|
| 2792 | + return 0; |
|---|
| 2793 | + |
|---|
| 2794 | + if (asprintf(optstr, "Show context for individual samples %s", |
|---|
| 2795 | + type == A_ASM ? "with assembler" : |
|---|
| 2796 | + type == A_SOURCE ? "with source" : "") < 0) |
|---|
| 2797 | + return 0; |
|---|
| 2798 | + |
|---|
| 2799 | + act->fn = do_res_sample_script; |
|---|
| 2800 | + act->evsel = evsel; |
|---|
| 2801 | + act->rstype = type; |
|---|
| 2556 | 2802 | return 1; |
|---|
| 2557 | 2803 | } |
|---|
| 2558 | 2804 | |
|---|
| .. | .. |
|---|
| 2642 | 2888 | static void hist_browser__update_nr_entries(struct hist_browser *hb) |
|---|
| 2643 | 2889 | { |
|---|
| 2644 | 2890 | u64 nr_entries = 0; |
|---|
| 2645 | | - struct rb_node *nd = rb_first(&hb->hists->entries); |
|---|
| 2891 | + struct rb_node *nd = rb_first_cached(&hb->hists->entries); |
|---|
| 2646 | 2892 | |
|---|
| 2647 | 2893 | if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { |
|---|
| 2648 | 2894 | hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; |
|---|
| .. | .. |
|---|
| 2662 | 2908 | double percent) |
|---|
| 2663 | 2909 | { |
|---|
| 2664 | 2910 | struct hist_entry *he; |
|---|
| 2665 | | - struct rb_node *nd = rb_first(&hb->hists->entries); |
|---|
| 2911 | + struct rb_node *nd = rb_first_cached(&hb->hists->entries); |
|---|
| 2666 | 2912 | u64 total = hists__total_period(hb->hists); |
|---|
| 2667 | 2913 | u64 min_callchain_hits = total * (percent / 100); |
|---|
| 2668 | 2914 | |
|---|
| .. | .. |
|---|
| 2700 | 2946 | } |
|---|
| 2701 | 2947 | } |
|---|
| 2702 | 2948 | |
|---|
| 2703 | | -static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
|---|
| 2949 | +static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, |
|---|
| 2704 | 2950 | const char *helpline, |
|---|
| 2705 | 2951 | bool left_exits, |
|---|
| 2706 | 2952 | struct hist_browser_timer *hbt, |
|---|
| .. | .. |
|---|
| 2717 | 2963 | struct popup_action actions[MAX_OPTIONS]; |
|---|
| 2718 | 2964 | int nr_options = 0; |
|---|
| 2719 | 2965 | int key = -1; |
|---|
| 2720 | | - char buf[64]; |
|---|
| 2966 | + char buf[128]; |
|---|
| 2721 | 2967 | int delay_secs = hbt ? hbt->refresh : 0; |
|---|
| 2722 | 2968 | |
|---|
| 2723 | 2969 | #define HIST_BROWSER_HELP_COMMON \ |
|---|
| .. | .. |
|---|
| 2730 | 2976 | "For symbolic views (--sort has sym):\n\n" \ |
|---|
| 2731 | 2977 | "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ |
|---|
| 2732 | 2978 | "ESC Zoom out\n" \ |
|---|
| 2979 | + "+ Expand/Collapse one callchain level\n" \ |
|---|
| 2733 | 2980 | "a Annotate current symbol\n" \ |
|---|
| 2734 | 2981 | "C Collapse all callchains\n" \ |
|---|
| 2735 | 2982 | "d Zoom into current DSO\n" \ |
|---|
| 2983 | + "e Expand/Collapse main entry callchains\n" \ |
|---|
| 2736 | 2984 | "E Expand all callchains\n" \ |
|---|
| 2737 | 2985 | "F Toggle percentage of filtered entries\n" \ |
|---|
| 2738 | 2986 | "H Display column headers\n" \ |
|---|
| 2987 | + "k Zoom into the kernel map\n" \ |
|---|
| 2739 | 2988 | "L Change percent limit\n" \ |
|---|
| 2740 | 2989 | "m Display context menu\n" \ |
|---|
| 2741 | 2990 | "S Zoom into current Processor Socket\n" \ |
|---|
| 2742 | 2991 | |
|---|
| 2743 | 2992 | /* help messages are sorted by lexical order of the hotkey */ |
|---|
| 2744 | | - const char report_help[] = HIST_BROWSER_HELP_COMMON |
|---|
| 2993 | + static const char report_help[] = HIST_BROWSER_HELP_COMMON |
|---|
| 2745 | 2994 | "i Show header information\n" |
|---|
| 2746 | 2995 | "P Print histograms to perf.hist.N\n" |
|---|
| 2747 | 2996 | "r Run available scripts\n" |
|---|
| 2748 | 2997 | "s Switch to another data file in PWD\n" |
|---|
| 2749 | 2998 | "t Zoom into current Thread\n" |
|---|
| 2750 | 2999 | "V Verbose (DSO names in callchains, etc)\n" |
|---|
| 2751 | | - "/ Filter symbol by name"; |
|---|
| 2752 | | - const char top_help[] = HIST_BROWSER_HELP_COMMON |
|---|
| 3000 | + "/ Filter symbol by name\n" |
|---|
| 3001 | + "0-9 Sort by event n in group"; |
|---|
| 3002 | + static const char top_help[] = HIST_BROWSER_HELP_COMMON |
|---|
| 2753 | 3003 | "P Print histograms to perf.hist.N\n" |
|---|
| 2754 | 3004 | "t Zoom into current Thread\n" |
|---|
| 2755 | 3005 | "V Verbose (DSO names in callchains, etc)\n" |
|---|
| .. | .. |
|---|
| 2780 | 3030 | if (symbol_conf.col_width_list_str) |
|---|
| 2781 | 3031 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); |
|---|
| 2782 | 3032 | |
|---|
| 3033 | + if (!is_report_browser(hbt)) |
|---|
| 3034 | + browser->b.no_samples_msg = "Collecting samples..."; |
|---|
| 3035 | + |
|---|
| 2783 | 3036 | while (1) { |
|---|
| 2784 | 3037 | struct thread *thread = NULL; |
|---|
| 2785 | 3038 | struct map *map = NULL; |
|---|
| 2786 | | - int choice = 0; |
|---|
| 3039 | + int choice; |
|---|
| 2787 | 3040 | int socked_id = -1; |
|---|
| 2788 | 3041 | |
|---|
| 2789 | | - nr_options = 0; |
|---|
| 2790 | | - |
|---|
| 2791 | | - key = hist_browser__run(browser, helpline, |
|---|
| 2792 | | - warn_lost_event); |
|---|
| 3042 | + key = 0; // reset key |
|---|
| 3043 | +do_hotkey: // key came straight from options ui__popup_menu() |
|---|
| 3044 | + choice = nr_options = 0; |
|---|
| 3045 | + key = hist_browser__run(browser, helpline, warn_lost_event, key); |
|---|
| 2793 | 3046 | |
|---|
| 2794 | 3047 | if (browser->he_selection != NULL) { |
|---|
| 2795 | 3048 | thread = hist_browser__selected_thread(browser); |
|---|
| .. | .. |
|---|
| 2806 | 3059 | * go to the next or previous |
|---|
| 2807 | 3060 | */ |
|---|
| 2808 | 3061 | goto out_free_stack; |
|---|
| 3062 | + case '0' ... '9': |
|---|
| 3063 | + if (!symbol_conf.event_group || |
|---|
| 3064 | + evsel->core.nr_members < 2) { |
|---|
| 3065 | + snprintf(buf, sizeof(buf), |
|---|
| 3066 | + "Sort by index only available with group events!"); |
|---|
| 3067 | + helpline = buf; |
|---|
| 3068 | + continue; |
|---|
| 3069 | + } |
|---|
| 3070 | + |
|---|
| 3071 | + if (key - '0' == symbol_conf.group_sort_idx) |
|---|
| 3072 | + continue; |
|---|
| 3073 | + |
|---|
| 3074 | + symbol_conf.group_sort_idx = key - '0'; |
|---|
| 3075 | + |
|---|
| 3076 | + if (symbol_conf.group_sort_idx >= evsel->core.nr_members) { |
|---|
| 3077 | + snprintf(buf, sizeof(buf), |
|---|
| 3078 | + "Max event group index to sort is %d (index from 0 to %d)", |
|---|
| 3079 | + evsel->core.nr_members - 1, |
|---|
| 3080 | + evsel->core.nr_members - 1); |
|---|
| 3081 | + helpline = buf; |
|---|
| 3082 | + continue; |
|---|
| 3083 | + } |
|---|
| 3084 | + |
|---|
| 3085 | + key = K_RELOAD; |
|---|
| 3086 | + goto out_free_stack; |
|---|
| 2809 | 3087 | case 'a': |
|---|
| 2810 | 3088 | if (!hists__has(hists, sym)) { |
|---|
| 2811 | 3089 | ui_browser__warning(&browser->b, delay_secs * 2, |
|---|
| .. | .. |
|---|
| 2814 | 3092 | continue; |
|---|
| 2815 | 3093 | } |
|---|
| 2816 | 3094 | |
|---|
| 2817 | | - if (browser->selection == NULL || |
|---|
| 2818 | | - browser->selection->sym == NULL || |
|---|
| 2819 | | - browser->selection->map->dso->annotate_warned) |
|---|
| 3095 | + if (!browser->selection || |
|---|
| 3096 | + !browser->selection->map || |
|---|
| 3097 | + !browser->selection->map->dso || |
|---|
| 3098 | + browser->selection->map->dso->annotate_warned) { |
|---|
| 2820 | 3099 | continue; |
|---|
| 3100 | + } |
|---|
| 2821 | 3101 | |
|---|
| 2822 | | - actions->ms.map = browser->selection->map; |
|---|
| 2823 | | - actions->ms.sym = browser->selection->sym; |
|---|
| 3102 | + if (!browser->selection->sym) { |
|---|
| 3103 | + if (!browser->he_selection) |
|---|
| 3104 | + continue; |
|---|
| 3105 | + |
|---|
| 3106 | + if (sort__mode == SORT_MODE__BRANCH) { |
|---|
| 3107 | + bi = browser->he_selection->branch_info; |
|---|
| 3108 | + if (!bi || !bi->to.ms.map) |
|---|
| 3109 | + continue; |
|---|
| 3110 | + |
|---|
| 3111 | + actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map); |
|---|
| 3112 | + actions->ms.map = bi->to.ms.map; |
|---|
| 3113 | + } else { |
|---|
| 3114 | + actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip, |
|---|
| 3115 | + browser->selection->map); |
|---|
| 3116 | + actions->ms.map = browser->selection->map; |
|---|
| 3117 | + } |
|---|
| 3118 | + |
|---|
| 3119 | + if (!actions->ms.sym) |
|---|
| 3120 | + continue; |
|---|
| 3121 | + } else { |
|---|
| 3122 | + if (symbol__annotation(browser->selection->sym)->src == NULL) { |
|---|
| 3123 | + ui_browser__warning(&browser->b, delay_secs * 2, |
|---|
| 3124 | + "No samples for the \"%s\" symbol.\n\n" |
|---|
| 3125 | + "Probably appeared just in a callchain", |
|---|
| 3126 | + browser->selection->sym->name); |
|---|
| 3127 | + continue; |
|---|
| 3128 | + } |
|---|
| 3129 | + |
|---|
| 3130 | + actions->ms.map = browser->selection->map; |
|---|
| 3131 | + actions->ms.sym = browser->selection->sym; |
|---|
| 3132 | + } |
|---|
| 3133 | + |
|---|
| 2824 | 3134 | do_annotate(browser, actions); |
|---|
| 2825 | 3135 | continue; |
|---|
| 2826 | 3136 | case 'P': |
|---|
| .. | .. |
|---|
| 2829 | 3139 | case 'd': |
|---|
| 2830 | 3140 | actions->ms.map = map; |
|---|
| 2831 | 3141 | do_zoom_dso(browser, actions); |
|---|
| 3142 | + continue; |
|---|
| 3143 | + case 'k': |
|---|
| 3144 | + if (browser->selection != NULL) |
|---|
| 3145 | + hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map); |
|---|
| 2832 | 3146 | continue; |
|---|
| 2833 | 3147 | case 'V': |
|---|
| 2834 | 3148 | verbose = (verbose + 1) % 4; |
|---|
| .. | .. |
|---|
| 2988 | 3302 | nr_options += add_annotate_opt(browser, |
|---|
| 2989 | 3303 | &actions[nr_options], |
|---|
| 2990 | 3304 | &options[nr_options], |
|---|
| 2991 | | - bi->from.map, |
|---|
| 2992 | | - bi->from.sym); |
|---|
| 2993 | | - if (bi->to.sym != bi->from.sym) |
|---|
| 3305 | + &bi->from.ms, |
|---|
| 3306 | + bi->from.al_addr); |
|---|
| 3307 | + if (bi->to.ms.sym != bi->from.ms.sym) |
|---|
| 2994 | 3308 | nr_options += add_annotate_opt(browser, |
|---|
| 2995 | 3309 | &actions[nr_options], |
|---|
| 2996 | 3310 | &options[nr_options], |
|---|
| 2997 | | - bi->to.map, |
|---|
| 2998 | | - bi->to.sym); |
|---|
| 3311 | + &bi->to.ms, |
|---|
| 3312 | + bi->to.al_addr); |
|---|
| 2999 | 3313 | } else { |
|---|
| 3000 | 3314 | nr_options += add_annotate_opt(browser, |
|---|
| 3001 | 3315 | &actions[nr_options], |
|---|
| 3002 | 3316 | &options[nr_options], |
|---|
| 3003 | | - browser->selection->map, |
|---|
| 3004 | | - browser->selection->sym); |
|---|
| 3317 | + browser->selection, |
|---|
| 3318 | + browser->he_selection->ip); |
|---|
| 3005 | 3319 | } |
|---|
| 3006 | 3320 | skip_annotation: |
|---|
| 3007 | 3321 | nr_options += add_thread_opt(browser, &actions[nr_options], |
|---|
| 3008 | 3322 | &options[nr_options], thread); |
|---|
| 3009 | 3323 | nr_options += add_dso_opt(browser, &actions[nr_options], |
|---|
| 3010 | 3324 | &options[nr_options], map); |
|---|
| 3325 | + nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]); |
|---|
| 3011 | 3326 | nr_options += add_map_opt(browser, &actions[nr_options], |
|---|
| 3012 | 3327 | &options[nr_options], |
|---|
| 3013 | 3328 | browser->selection ? |
|---|
| .. | .. |
|---|
| 3024 | 3339 | nr_options += add_script_opt(browser, |
|---|
| 3025 | 3340 | &actions[nr_options], |
|---|
| 3026 | 3341 | &options[nr_options], |
|---|
| 3027 | | - thread, NULL); |
|---|
| 3342 | + thread, NULL, evsel); |
|---|
| 3028 | 3343 | } |
|---|
| 3029 | 3344 | /* |
|---|
| 3030 | 3345 | * Note that browser->selection != NULL |
|---|
| .. | .. |
|---|
| 3039 | 3354 | nr_options += add_script_opt(browser, |
|---|
| 3040 | 3355 | &actions[nr_options], |
|---|
| 3041 | 3356 | &options[nr_options], |
|---|
| 3042 | | - NULL, browser->selection->sym); |
|---|
| 3357 | + NULL, browser->selection->sym, |
|---|
| 3358 | + evsel); |
|---|
| 3043 | 3359 | } |
|---|
| 3044 | 3360 | } |
|---|
| 3045 | 3361 | nr_options += add_script_opt(browser, &actions[nr_options], |
|---|
| 3046 | | - &options[nr_options], NULL, NULL); |
|---|
| 3362 | + &options[nr_options], NULL, NULL, evsel); |
|---|
| 3363 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
|---|
| 3364 | + &options[nr_options], |
|---|
| 3365 | + hist_browser__selected_res_sample(browser), |
|---|
| 3366 | + evsel, A_NORMAL); |
|---|
| 3367 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
|---|
| 3368 | + &options[nr_options], |
|---|
| 3369 | + hist_browser__selected_res_sample(browser), |
|---|
| 3370 | + evsel, A_ASM); |
|---|
| 3371 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
|---|
| 3372 | + &options[nr_options], |
|---|
| 3373 | + hist_browser__selected_res_sample(browser), |
|---|
| 3374 | + evsel, A_SOURCE); |
|---|
| 3047 | 3375 | nr_options += add_switch_opt(browser, &actions[nr_options], |
|---|
| 3048 | 3376 | &options[nr_options]); |
|---|
| 3049 | 3377 | skip_scripting: |
|---|
| .. | .. |
|---|
| 3053 | 3381 | do { |
|---|
| 3054 | 3382 | struct popup_action *act; |
|---|
| 3055 | 3383 | |
|---|
| 3056 | | - choice = ui__popup_menu(nr_options, options); |
|---|
| 3057 | | - if (choice == -1 || choice >= nr_options) |
|---|
| 3384 | + choice = ui__popup_menu(nr_options, options, &key); |
|---|
| 3385 | + if (choice == -1) |
|---|
| 3058 | 3386 | break; |
|---|
| 3387 | + |
|---|
| 3388 | + if (choice == nr_options) |
|---|
| 3389 | + goto do_hotkey; |
|---|
| 3059 | 3390 | |
|---|
| 3060 | 3391 | act = &actions[choice]; |
|---|
| 3061 | 3392 | key = act->fn(browser, act); |
|---|
| .. | .. |
|---|
| 3072 | 3403 | return key; |
|---|
| 3073 | 3404 | } |
|---|
| 3074 | 3405 | |
|---|
| 3075 | | -struct perf_evsel_menu { |
|---|
| 3406 | +struct evsel_menu { |
|---|
| 3076 | 3407 | struct ui_browser b; |
|---|
| 3077 | | - struct perf_evsel *selection; |
|---|
| 3408 | + struct evsel *selection; |
|---|
| 3078 | 3409 | struct annotation_options *annotation_opts; |
|---|
| 3079 | 3410 | bool lost_events, lost_events_warned; |
|---|
| 3080 | 3411 | float min_pcnt; |
|---|
| .. | .. |
|---|
| 3084 | 3415 | static void perf_evsel_menu__write(struct ui_browser *browser, |
|---|
| 3085 | 3416 | void *entry, int row) |
|---|
| 3086 | 3417 | { |
|---|
| 3087 | | - struct perf_evsel_menu *menu = container_of(browser, |
|---|
| 3088 | | - struct perf_evsel_menu, b); |
|---|
| 3089 | | - struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
|---|
| 3418 | + struct evsel_menu *menu = container_of(browser, |
|---|
| 3419 | + struct evsel_menu, b); |
|---|
| 3420 | + struct evsel *evsel = list_entry(entry, struct evsel, core.node); |
|---|
| 3090 | 3421 | struct hists *hists = evsel__hists(evsel); |
|---|
| 3091 | 3422 | bool current_entry = ui_browser__is_current_entry(browser, row); |
|---|
| 3092 | 3423 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
|---|
| 3093 | | - const char *ev_name = perf_evsel__name(evsel); |
|---|
| 3424 | + const char *ev_name = evsel__name(evsel); |
|---|
| 3094 | 3425 | char bf[256], unit; |
|---|
| 3095 | 3426 | const char *warn = " "; |
|---|
| 3096 | 3427 | size_t printed; |
|---|
| .. | .. |
|---|
| 3098 | 3429 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
|---|
| 3099 | 3430 | HE_COLORSET_NORMAL); |
|---|
| 3100 | 3431 | |
|---|
| 3101 | | - if (perf_evsel__is_group_event(evsel)) { |
|---|
| 3102 | | - struct perf_evsel *pos; |
|---|
| 3432 | + if (evsel__is_group_event(evsel)) { |
|---|
| 3433 | + struct evsel *pos; |
|---|
| 3103 | 3434 | |
|---|
| 3104 | | - ev_name = perf_evsel__group_name(evsel); |
|---|
| 3435 | + ev_name = evsel__group_name(evsel); |
|---|
| 3105 | 3436 | |
|---|
| 3106 | 3437 | for_each_group_member(pos, evsel) { |
|---|
| 3107 | 3438 | struct hists *pos_hists = evsel__hists(pos); |
|---|
| .. | .. |
|---|
| 3131 | 3462 | menu->selection = evsel; |
|---|
| 3132 | 3463 | } |
|---|
| 3133 | 3464 | |
|---|
| 3134 | | -static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
|---|
| 3465 | +static int perf_evsel_menu__run(struct evsel_menu *menu, |
|---|
| 3135 | 3466 | int nr_events, const char *help, |
|---|
| 3136 | 3467 | struct hist_browser_timer *hbt, |
|---|
| 3137 | 3468 | bool warn_lost_event) |
|---|
| 3138 | 3469 | { |
|---|
| 3139 | | - struct perf_evlist *evlist = menu->b.priv; |
|---|
| 3140 | | - struct perf_evsel *pos; |
|---|
| 3470 | + struct evlist *evlist = menu->b.priv; |
|---|
| 3471 | + struct evsel *pos; |
|---|
| 3141 | 3472 | const char *title = "Available samples"; |
|---|
| 3142 | 3473 | int delay_secs = hbt ? hbt->refresh : 0; |
|---|
| 3143 | 3474 | int key; |
|---|
| .. | .. |
|---|
| 3183 | 3514 | ui_browser__show_title(&menu->b, title); |
|---|
| 3184 | 3515 | switch (key) { |
|---|
| 3185 | 3516 | case K_TAB: |
|---|
| 3186 | | - if (pos->node.next == &evlist->entries) |
|---|
| 3187 | | - pos = perf_evlist__first(evlist); |
|---|
| 3517 | + if (pos->core.node.next == &evlist->core.entries) |
|---|
| 3518 | + pos = evlist__first(evlist); |
|---|
| 3188 | 3519 | else |
|---|
| 3189 | | - pos = perf_evsel__next(pos); |
|---|
| 3520 | + pos = evsel__next(pos); |
|---|
| 3190 | 3521 | goto browse_hists; |
|---|
| 3191 | 3522 | case K_UNTAB: |
|---|
| 3192 | | - if (pos->node.prev == &evlist->entries) |
|---|
| 3193 | | - pos = perf_evlist__last(evlist); |
|---|
| 3523 | + if (pos->core.node.prev == &evlist->core.entries) |
|---|
| 3524 | + pos = evlist__last(evlist); |
|---|
| 3194 | 3525 | else |
|---|
| 3195 | | - pos = perf_evsel__prev(pos); |
|---|
| 3526 | + pos = evsel__prev(pos); |
|---|
| 3196 | 3527 | goto browse_hists; |
|---|
| 3197 | 3528 | case K_SWITCH_INPUT_DATA: |
|---|
| 3529 | + case K_RELOAD: |
|---|
| 3198 | 3530 | case 'q': |
|---|
| 3199 | 3531 | case CTRL('c'): |
|---|
| 3200 | 3532 | goto out; |
|---|
| .. | .. |
|---|
| 3225 | 3557 | static bool filter_group_entries(struct ui_browser *browser __maybe_unused, |
|---|
| 3226 | 3558 | void *entry) |
|---|
| 3227 | 3559 | { |
|---|
| 3228 | | - struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
|---|
| 3560 | + struct evsel *evsel = list_entry(entry, struct evsel, core.node); |
|---|
| 3229 | 3561 | |
|---|
| 3230 | | - if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) |
|---|
| 3562 | + if (symbol_conf.event_group && !evsel__is_group_leader(evsel)) |
|---|
| 3231 | 3563 | return true; |
|---|
| 3232 | 3564 | |
|---|
| 3233 | 3565 | return false; |
|---|
| 3234 | 3566 | } |
|---|
| 3235 | 3567 | |
|---|
| 3236 | | -static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
|---|
| 3568 | +static int __perf_evlist__tui_browse_hists(struct evlist *evlist, |
|---|
| 3237 | 3569 | int nr_entries, const char *help, |
|---|
| 3238 | 3570 | struct hist_browser_timer *hbt, |
|---|
| 3239 | 3571 | float min_pcnt, |
|---|
| .. | .. |
|---|
| 3241 | 3573 | bool warn_lost_event, |
|---|
| 3242 | 3574 | struct annotation_options *annotation_opts) |
|---|
| 3243 | 3575 | { |
|---|
| 3244 | | - struct perf_evsel *pos; |
|---|
| 3245 | | - struct perf_evsel_menu menu = { |
|---|
| 3576 | + struct evsel *pos; |
|---|
| 3577 | + struct evsel_menu menu = { |
|---|
| 3246 | 3578 | .b = { |
|---|
| 3247 | | - .entries = &evlist->entries, |
|---|
| 3579 | + .entries = &evlist->core.entries, |
|---|
| 3248 | 3580 | .refresh = ui_browser__list_head_refresh, |
|---|
| 3249 | 3581 | .seek = ui_browser__list_head_seek, |
|---|
| 3250 | 3582 | .write = perf_evsel_menu__write, |
|---|
| .. | .. |
|---|
| 3260 | 3592 | ui_helpline__push("Press ESC to exit"); |
|---|
| 3261 | 3593 | |
|---|
| 3262 | 3594 | evlist__for_each_entry(evlist, pos) { |
|---|
| 3263 | | - const char *ev_name = perf_evsel__name(pos); |
|---|
| 3595 | + const char *ev_name = evsel__name(pos); |
|---|
| 3264 | 3596 | size_t line_len = strlen(ev_name) + 7; |
|---|
| 3265 | 3597 | |
|---|
| 3266 | 3598 | if (menu.b.width < line_len) |
|---|
| .. | .. |
|---|
| 3271 | 3603 | hbt, warn_lost_event); |
|---|
| 3272 | 3604 | } |
|---|
| 3273 | 3605 | |
|---|
| 3274 | | -int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
|---|
| 3606 | +static bool perf_evlist__single_entry(struct evlist *evlist) |
|---|
| 3607 | +{ |
|---|
| 3608 | + int nr_entries = evlist->core.nr_entries; |
|---|
| 3609 | + |
|---|
| 3610 | + if (nr_entries == 1) |
|---|
| 3611 | + return true; |
|---|
| 3612 | + |
|---|
| 3613 | + if (nr_entries == 2) { |
|---|
| 3614 | + struct evsel *last = evlist__last(evlist); |
|---|
| 3615 | + |
|---|
| 3616 | + if (evsel__is_dummy_event(last)) |
|---|
| 3617 | + return true; |
|---|
| 3618 | + } |
|---|
| 3619 | + |
|---|
| 3620 | + return false; |
|---|
| 3621 | +} |
|---|
| 3622 | + |
|---|
| 3623 | +int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help, |
|---|
| 3275 | 3624 | struct hist_browser_timer *hbt, |
|---|
| 3276 | 3625 | float min_pcnt, |
|---|
| 3277 | 3626 | struct perf_env *env, |
|---|
| 3278 | 3627 | bool warn_lost_event, |
|---|
| 3279 | 3628 | struct annotation_options *annotation_opts) |
|---|
| 3280 | 3629 | { |
|---|
| 3281 | | - int nr_entries = evlist->nr_entries; |
|---|
| 3630 | + int nr_entries = evlist->core.nr_entries; |
|---|
| 3282 | 3631 | |
|---|
| 3283 | | -single_entry: |
|---|
| 3284 | | - if (nr_entries == 1) { |
|---|
| 3285 | | - struct perf_evsel *first = perf_evlist__first(evlist); |
|---|
| 3632 | + if (perf_evlist__single_entry(evlist)) { |
|---|
| 3633 | +single_entry: { |
|---|
| 3634 | + struct evsel *first = evlist__first(evlist); |
|---|
| 3286 | 3635 | |
|---|
| 3287 | 3636 | return perf_evsel__hists_browse(first, nr_entries, help, |
|---|
| 3288 | 3637 | false, hbt, min_pcnt, |
|---|
| 3289 | 3638 | env, warn_lost_event, |
|---|
| 3290 | 3639 | annotation_opts); |
|---|
| 3291 | 3640 | } |
|---|
| 3641 | + } |
|---|
| 3292 | 3642 | |
|---|
| 3293 | 3643 | if (symbol_conf.event_group) { |
|---|
| 3294 | | - struct perf_evsel *pos; |
|---|
| 3644 | + struct evsel *pos; |
|---|
| 3295 | 3645 | |
|---|
| 3296 | 3646 | nr_entries = 0; |
|---|
| 3297 | 3647 | evlist__for_each_entry(evlist, pos) { |
|---|
| 3298 | | - if (perf_evsel__is_group_leader(pos)) |
|---|
| 3648 | + if (evsel__is_group_leader(pos)) |
|---|
| 3299 | 3649 | nr_entries++; |
|---|
| 3300 | 3650 | } |
|---|
| 3301 | 3651 | |
|---|
| .. | .. |
|---|
| 3308 | 3658 | warn_lost_event, |
|---|
| 3309 | 3659 | annotation_opts); |
|---|
| 3310 | 3660 | } |
|---|
| 3661 | + |
|---|
| 3662 | +static int block_hists_browser__title(struct hist_browser *browser, char *bf, |
|---|
| 3663 | + size_t size) |
|---|
| 3664 | +{ |
|---|
| 3665 | + struct hists *hists = evsel__hists(browser->block_evsel); |
|---|
| 3666 | + const char *evname = evsel__name(browser->block_evsel); |
|---|
| 3667 | + unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
|---|
| 3668 | + int ret; |
|---|
| 3669 | + |
|---|
| 3670 | + ret = scnprintf(bf, size, "# Samples: %lu", nr_samples); |
|---|
| 3671 | + if (evname) |
|---|
| 3672 | + scnprintf(bf + ret, size - ret, " of event '%s'", evname); |
|---|
| 3673 | + |
|---|
| 3674 | + return 0; |
|---|
| 3675 | +} |
|---|
| 3676 | + |
|---|
| 3677 | +int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, |
|---|
| 3678 | + float min_percent, struct perf_env *env, |
|---|
| 3679 | + struct annotation_options *annotation_opts) |
|---|
| 3680 | +{ |
|---|
| 3681 | + struct hists *hists = &bh->block_hists; |
|---|
| 3682 | + struct hist_browser *browser; |
|---|
| 3683 | + int key = -1; |
|---|
| 3684 | + struct popup_action action; |
|---|
| 3685 | + static const char help[] = |
|---|
| 3686 | + " q Quit \n"; |
|---|
| 3687 | + |
|---|
| 3688 | + browser = hist_browser__new(hists); |
|---|
| 3689 | + if (!browser) |
|---|
| 3690 | + return -1; |
|---|
| 3691 | + |
|---|
| 3692 | + browser->block_evsel = evsel; |
|---|
| 3693 | + browser->title = block_hists_browser__title; |
|---|
| 3694 | + browser->min_pcnt = min_percent; |
|---|
| 3695 | + browser->env = env; |
|---|
| 3696 | + browser->annotation_opts = annotation_opts; |
|---|
| 3697 | + |
|---|
| 3698 | + /* reset abort key so that it can get Ctrl-C as a key */ |
|---|
| 3699 | + SLang_reset_tty(); |
|---|
| 3700 | + SLang_init_tty(0, 0, 0); |
|---|
| 3701 | + |
|---|
| 3702 | + memset(&action, 0, sizeof(action)); |
|---|
| 3703 | + |
|---|
| 3704 | + while (1) { |
|---|
| 3705 | + key = hist_browser__run(browser, "? - help", true, 0); |
|---|
| 3706 | + |
|---|
| 3707 | + switch (key) { |
|---|
| 3708 | + case 'q': |
|---|
| 3709 | + goto out; |
|---|
| 3710 | + case '?': |
|---|
| 3711 | + ui_browser__help_window(&browser->b, help); |
|---|
| 3712 | + break; |
|---|
| 3713 | + case 'a': |
|---|
| 3714 | + case K_ENTER: |
|---|
| 3715 | + if (!browser->selection || |
|---|
| 3716 | + !browser->selection->sym) { |
|---|
| 3717 | + continue; |
|---|
| 3718 | + } |
|---|
| 3719 | + |
|---|
| 3720 | + action.ms.map = browser->selection->map; |
|---|
| 3721 | + action.ms.sym = browser->selection->sym; |
|---|
| 3722 | + do_annotate(browser, &action); |
|---|
| 3723 | + continue; |
|---|
| 3724 | + default: |
|---|
| 3725 | + break; |
|---|
| 3726 | + } |
|---|
| 3727 | + } |
|---|
| 3728 | + |
|---|
| 3729 | +out: |
|---|
| 3730 | + hist_browser__delete(browser); |
|---|
| 3731 | + return 0; |
|---|
| 3732 | +} |
|---|