diff options
Diffstat (limited to 'kernel/tools/perf/ui/browsers/hists.c')
-rw-r--r-- | kernel/tools/perf/ui/browsers/hists.c | 828 |
1 files changed, 551 insertions, 277 deletions
diff --git a/kernel/tools/perf/ui/browsers/hists.c b/kernel/tools/perf/ui/browsers/hists.c index 658b0a897..81def6c3f 100644 --- a/kernel/tools/perf/ui/browsers/hists.c +++ b/kernel/tools/perf/ui/browsers/hists.c @@ -1,5 +1,4 @@ #include <stdio.h> -#include "../libslang.h" #include <stdlib.h> #include <string.h> #include <linux/rbtree.h> @@ -25,6 +24,9 @@ struct hist_browser { struct hists *hists; struct hist_entry *he_selection; struct map_symbol *selection; + struct hist_browser_timer *hbt; + struct pstack *pstack; + struct perf_env *env; int print_seq; bool show_dso; bool show_headers; @@ -60,7 +62,7 @@ static int hist_browser__get_folding(struct hist_browser *browser) struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); - if (he->ms.unfolded) + if (he->unfolded) unfolded_rows += he->nr_rows; } return unfolded_rows; @@ -136,24 +138,19 @@ static char tree__folded_sign(bool unfolded) return unfolded ? '-' : '+'; } -static char map_symbol__folded(const struct map_symbol *ms) -{ - return ms->has_children ? tree__folded_sign(ms->unfolded) : ' '; -} - static char hist_entry__folded(const struct hist_entry *he) { - return map_symbol__folded(&he->ms); + return he->has_children ? tree__folded_sign(he->unfolded) : ' '; } static char callchain_list__folded(const struct callchain_list *cl) { - return map_symbol__folded(&cl->ms); + return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; } -static void map_symbol__set_folding(struct map_symbol *ms, bool unfold) +static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) { - ms->unfolded = unfold ? ms->has_children : false; + cl->unfolded = unfold ? cl->has_children : false; } static int callchain_node__count_rows_rb_tree(struct callchain_node *node) @@ -189,7 +186,7 @@ static int callchain_node__count_rows(struct callchain_node *node) list_for_each_entry(chain, &node->val, list) { ++n; - unfolded = chain->ms.unfolded; + unfolded = chain->unfolded; } if (unfolded) @@ -211,15 +208,27 @@ static int callchain__count_rows(struct rb_root *chain) return n; } -static bool map_symbol__toggle_fold(struct map_symbol *ms) +static bool hist_entry__toggle_fold(struct hist_entry *he) { - if (!ms) + if (!he) return false; - if (!ms->has_children) + if (!he->has_children) return false; - ms->unfolded = !ms->unfolded; + he->unfolded = !he->unfolded; + return true; +} + +static bool callchain_list__toggle_fold(struct callchain_list *cl) +{ + if (!cl) + return false; + + if (!cl->has_children) + return false; + + cl->unfolded = !cl->unfolded; return true; } @@ -235,10 +244,10 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no list_for_each_entry(chain, &child->val, list) { if (first) { first = false; - chain->ms.has_children = chain->list.next != &child->val || + chain->has_children = chain->list.next != &child->val || !RB_EMPTY_ROOT(&child->rb_root); } else - chain->ms.has_children = chain->list.next == &child->val && + chain->has_children = chain->list.next == &child->val && !RB_EMPTY_ROOT(&child->rb_root); } @@ -252,11 +261,11 @@ static void callchain_node__init_have_children(struct callchain_node *node, struct callchain_list *chain; chain = list_entry(node->val.next, struct callchain_list, list); - chain->ms.has_children = has_sibling; + chain->has_children = has_sibling; if (!list_empty(&node->val)) { chain = list_entry(node->val.prev, struct callchain_list, list); - chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); + chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); } callchain_node__init_have_children_rb_tree(node); @@ -276,7 +285,7 @@ static void callchain__init_have_children(struct rb_root *root) static void hist_entry__init_have_children(struct hist_entry *he) { if (!he->init_have_children) { - he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); + he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); callchain__init_have_children(&he->sorted_chain); he->init_have_children = true; } @@ -284,14 +293,25 @@ static void hist_entry__init_have_children(struct hist_entry *he) static bool hist_browser__toggle_fold(struct hist_browser *browser) { - if (map_symbol__toggle_fold(browser->selection)) { - struct hist_entry *he = browser->he_selection; + struct hist_entry *he = browser->he_selection; + struct map_symbol *ms = browser->selection; + struct callchain_list *cl = container_of(ms, struct callchain_list, ms); + bool has_children; + + if (!he || !ms) + return false; + if (ms == &he->ms) + has_children = hist_entry__toggle_fold(he); + else + has_children = callchain_list__toggle_fold(cl); + + if (has_children) { hist_entry__init_have_children(he); browser->b.nr_entries -= he->nr_rows; browser->nr_callchain_rows -= he->nr_rows; - if (he->ms.unfolded) + if (he->unfolded) he->nr_rows = callchain__count_rows(&he->sorted_chain); else he->nr_rows = 0; @@ -318,8 +338,8 @@ static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool list_for_each_entry(chain, &child->val, list) { ++n; - map_symbol__set_folding(&chain->ms, unfold); - has_children = chain->ms.has_children; + callchain_list__set_folding(chain, unfold); + has_children = chain->has_children; } if (has_children) @@ -337,8 +357,8 @@ static int callchain_node__set_folding(struct callchain_node *node, bool unfold) list_for_each_entry(chain, &node->val, list) { ++n; - map_symbol__set_folding(&chain->ms, unfold); - has_children = chain->ms.has_children; + callchain_list__set_folding(chain, unfold); + has_children = chain->has_children; } if (has_children) @@ -363,9 +383,9 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold) static void hist_entry__set_folding(struct hist_entry *he, bool unfold) { hist_entry__init_have_children(he); - map_symbol__set_folding(&he->ms, unfold); + he->unfolded = unfold ? he->has_children : false; - if (he->ms.has_children) { + if (he->has_children) { int n = callchain__set_folding(&he->sorted_chain, unfold); he->nr_rows = unfold ? n : 0; } else @@ -406,11 +426,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) "Or reduce the sampling frequency."); } -static int hist_browser__run(struct hist_browser *browser, - struct hist_browser_timer *hbt) +static int hist_browser__run(struct hist_browser *browser, const char *help) { int key; char title[160]; + struct hist_browser_timer *hbt = browser->hbt; int delay_secs = hbt ? hbt->refresh : 0; browser->b.entries = &browser->hists->entries; @@ -418,8 +438,7 @@ static int hist_browser__run(struct hist_browser *browser, hists__browser_title(browser->hists, hbt, title, sizeof(title)); - if (ui_browser__show(&browser->b, title, - "Press '?' for help on key bindings") < 0) + if (ui_browser__show(&browser->b, title, help) < 0) return -1; while (1) { @@ -523,10 +542,10 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser, ui_browser__set_color(&browser->b, color); hist_browser__gotorc(browser, row, 0); - slsmg_write_nstring(" ", offset); - slsmg_printf("%c", folded_sign); + ui_browser__write_nstring(&browser->b, " ", offset); + ui_browser__printf(&browser->b, "%c", folded_sign); ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' '); - slsmg_write_nstring(str, width); + ui_browser__write_nstring(&browser->b, str, width); } static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, @@ -663,7 +682,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) ui_browser__set_percent_color(arg->b, percent, arg->current_entry); ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); - slsmg_printf("%s", hpp->buf); + ui_browser__printf(arg->b, "%s", hpp->buf); advance_hpp(hpp, ret); return ret; @@ -696,10 +715,11 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ struct hist_entry *he) \ { \ if (!symbol_conf.cumulate_callchain) { \ + struct hpp_arg *arg = hpp->ptr; \ int len = fmt->user_len ?: fmt->len; \ int ret = scnprintf(hpp->buf, hpp->size, \ "%*s", len, "N/A"); \ - slsmg_printf("%s", hpp->buf); \ + ui_browser__printf(arg->b, "%s", hpp->buf); \ \ return ret; \ } \ @@ -767,11 +787,12 @@ static int hist_browser__show_entry(struct hist_browser *browser, .size = sizeof(s), .ptr = &arg, }; + int column = 0; hist_browser__gotorc(browser, row, 0); perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll) continue; if (current_entry && browser->b.navkeypressed) { @@ -784,12 +805,12 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (first) { if (symbol_conf.use_callchain) { - slsmg_printf("%c ", folded_sign); + ui_browser__printf(&browser->b, "%c ", folded_sign); width -= 2; } first = false; } else { - slsmg_printf(" "); + ui_browser__printf(&browser->b, " "); width -= 2; } @@ -797,7 +818,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, width -= fmt->color(fmt, &hpp, entry); } else { width -= fmt->entry(fmt, &hpp, entry); - slsmg_printf("%s", s); + ui_browser__printf(&browser->b, "%s", s); } } @@ -805,7 +826,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (!browser->b.navkeypressed) width += 1; - slsmg_write_nstring("", width); + ui_browser__write_nstring(&browser->b, "", width); ++row; ++printed; @@ -844,14 +865,16 @@ static int advance_hpp_check(struct perf_hpp *hpp, int inc) return hpp->size <= 0; } -static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) +static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size) { + struct hists *hists = browser->hists; struct perf_hpp dummy_hpp = { .buf = buf, .size = size, }; struct perf_hpp_fmt *fmt; size_t ret = 0; + int column = 0; if (symbol_conf.use_callchain) { ret = scnprintf(buf, size, " "); @@ -860,7 +883,7 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) } perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll) continue; ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); @@ -879,10 +902,10 @@ static void hist_browser__show_headers(struct hist_browser *browser) { char headers[1024]; - hists__scnprintf_headers(headers, sizeof(headers), browser->hists); + hists_browser__scnprintf_headers(browser, headers, sizeof(headers)); ui_browser__gotorc(&browser->b, 0, 0); ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); - slsmg_write_nstring(headers, browser->b.width + 1); + ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); } static void ui_browser__hists_init_top(struct ui_browser *browser) @@ -908,6 +931,8 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) } ui_browser__hists_init_top(browser); + hb->he_selection = NULL; + hb->selection = NULL; for (nd = browser->top; nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -1013,10 +1038,13 @@ static void ui_browser__hists_seek(struct ui_browser *browser, * and stop when we printed enough lines to fill the screen. */ do_offset: + if (!nd) + return; + if (offset > 0) { do { h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) { + if (h->unfolded) { u16 remaining = h->nr_rows - h->row_offset; if (offset > remaining) { offset -= remaining; @@ -1037,7 +1065,7 @@ do_offset: } else if (offset < 0) { while (1) { h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) { + if (h->unfolded) { if (first) { if (-offset > h->row_offset) { offset += h->row_offset; @@ -1074,7 +1102,7 @@ do_offset: * row_offset at its last entry. */ h = rb_entry(nd, struct hist_entry, rb_node); - if (h->ms.unfolded) + if (h->unfolded) h->row_offset = h->nr_rows; break; } @@ -1195,7 +1223,9 @@ static int hist_browser__dump(struct hist_browser *browser) return 0; } -static struct hist_browser *hist_browser__new(struct hists *hists) +static struct hist_browser *hist_browser__new(struct hists *hists, + struct hist_browser_timer *hbt, + struct perf_env *env) { struct hist_browser *browser = zalloc(sizeof(*browser)); @@ -1206,6 +1236,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) browser->b.seek = ui_browser__hists_seek; browser->b.use_navkeypressed = true; browser->show_headers = symbol_conf.show_hist_headers; + browser->hbt = hbt; + browser->env = env; } return browser; @@ -1240,12 +1272,15 @@ static int hists__browser_title(struct hists *hists, int printed; const struct dso *dso = hists->dso_filter; const struct thread *thread = hists->thread_filter; + int socket_id = hists->socket_filter; unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; u64 nr_events = hists->stats.total_period; struct perf_evsel *evsel = hists_to_evsel(hists); const char *ev_name = perf_evsel__name(evsel); char buf[512]; size_t buflen = sizeof(buf); + char ref[30] = " show reference callgraph, "; + bool enable_ref = false; if (symbol_conf.filter_relative) { nr_samples = hists->stats.nr_non_filtered_samples; @@ -1271,10 +1306,13 @@ static int hists__browser_title(struct hists *hists, } } + if (symbol_conf.show_ref_callgraph && + strstr(ev_name, "call-graph=no")) + enable_ref = true; nr_samples = convert_unit(nr_samples, &unit); printed = scnprintf(bf, size, - "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64, - nr_samples, unit, ev_name, nr_events); + "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, + nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); if (hists->uid_filter_str) @@ -1288,6 +1326,9 @@ static int hists__browser_title(struct hists *hists, if (dso) printed += scnprintf(bf + printed, size - printed, ", DSO: %s", dso->short_name); + if (socket_id > -1) + printed += scnprintf(bf + printed, size - printed, + ", Processor Socket: %d", socket_id); if (!is_report_browser(hbt)) { struct perf_top *top = hbt->arg; @@ -1395,6 +1436,292 @@ close_file_and_continue: return ret; } +struct popup_action { + struct thread *thread; + struct map_symbol ms; + int socket; + + int (*fn)(struct hist_browser *browser, struct popup_action *act); +}; + +static int +do_annotate(struct hist_browser *browser, struct popup_action *act) +{ + struct perf_evsel *evsel; + struct annotation *notes; + struct hist_entry *he; + int err; + + if (!objdump_path && perf_env__lookup_objdump(browser->env)) + return 0; + + notes = symbol__annotation(act->ms.sym); + if (!notes->src) + return 0; + + evsel = hists_to_evsel(browser->hists); + err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); + he = hist_browser__selected_entry(browser); + /* + * offer option to annotate the other branch source or target + * (if they exists) when returning from annotate + */ + if ((err == 'q' || err == CTRL('c')) && he->branch_info) + return 1; + + ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); + if (err) + ui_browser__handle_resize(&browser->b); + return 0; +} + +static int +add_annotate_opt(struct hist_browser *browser __maybe_unused, + struct popup_action *act, char **optstr, + struct map *map, struct symbol *sym) +{ + if (sym == NULL || map->dso->annotate_warned) + return 0; + + if (asprintf(optstr, "Annotate %s", sym->name) < 0) + return 0; + + act->ms.map = map; + act->ms.sym = sym; + act->fn = do_annotate; + return 1; +} + +static int +do_zoom_thread(struct hist_browser *browser, struct popup_action *act) +{ + struct thread *thread = act->thread; + + if (browser->hists->thread_filter) { + pstack__remove(browser->pstack, &browser->hists->thread_filter); + perf_hpp__set_elide(HISTC_THREAD, false); + thread__zput(browser->hists->thread_filter); + ui_helpline__pop(); + } else { + ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread__comm_str(thread) : "", + thread->tid); + browser->hists->thread_filter = thread__get(thread); + perf_hpp__set_elide(HISTC_THREAD, false); + pstack__push(browser->pstack, &browser->hists->thread_filter); + } + + hists__filter_by_thread(browser->hists); + hist_browser__reset(browser); + return 0; +} + +static int +add_thread_opt(struct hist_browser *browser, struct popup_action *act, + char **optstr, struct thread *thread) +{ + if (thread == NULL) + return 0; + + if (asprintf(optstr, "Zoom %s %s(%d) thread", + browser->hists->thread_filter ? "out of" : "into", + thread->comm_set ? thread__comm_str(thread) : "", + thread->tid) < 0) + return 0; + + act->thread = thread; + act->fn = do_zoom_thread; + return 1; +} + +static int +do_zoom_dso(struct hist_browser *browser, struct popup_action *act) +{ + struct map *map = act->ms.map; + + if (browser->hists->dso_filter) { + pstack__remove(browser->pstack, &browser->hists->dso_filter); + perf_hpp__set_elide(HISTC_DSO, false); + browser->hists->dso_filter = NULL; + ui_helpline__pop(); + } else { + if (map == NULL) + return 0; + ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", + __map__is_kernel(map) ? "the Kernel" : map->dso->short_name); + browser->hists->dso_filter = map->dso; + perf_hpp__set_elide(HISTC_DSO, true); + pstack__push(browser->pstack, &browser->hists->dso_filter); + } + + hists__filter_by_dso(browser->hists); + hist_browser__reset(browser); + return 0; +} + +static int +add_dso_opt(struct hist_browser *browser, struct popup_action *act, + char **optstr, struct map *map) +{ + if (map == NULL) + return 0; + + if (asprintf(optstr, "Zoom %s %s DSO", + browser->hists->dso_filter ? "out of" : "into", + __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) + return 0; + + act->ms.map = map; + act->fn = do_zoom_dso; + return 1; +} + +static int +do_browse_map(struct hist_browser *browser __maybe_unused, + struct popup_action *act) +{ + map__browse(act->ms.map); + return 0; +} + +static int +add_map_opt(struct hist_browser *browser __maybe_unused, + struct popup_action *act, char **optstr, struct map *map) +{ + if (map == NULL) + return 0; + + if (asprintf(optstr, "Browse map details") < 0) + return 0; + + act->ms.map = map; + act->fn = do_browse_map; + return 1; +} + +static int +do_run_script(struct hist_browser *browser __maybe_unused, + struct popup_action *act) +{ + char script_opt[64]; + memset(script_opt, 0, sizeof(script_opt)); + + if (act->thread) { + scnprintf(script_opt, sizeof(script_opt), " -c %s ", + thread__comm_str(act->thread)); + } else if (act->ms.sym) { + scnprintf(script_opt, sizeof(script_opt), " -S %s ", + act->ms.sym->name); + } + + script_browse(script_opt); + return 0; +} + +static int +add_script_opt(struct hist_browser *browser __maybe_unused, + struct popup_action *act, char **optstr, + struct thread *thread, struct symbol *sym) +{ + if (thread) { + if (asprintf(optstr, "Run scripts for samples of thread [%s]", + thread__comm_str(thread)) < 0) + return 0; + } else if (sym) { + if (asprintf(optstr, "Run scripts for samples of symbol [%s]", + sym->name) < 0) + return 0; + } else { + if (asprintf(optstr, "Run scripts for all samples") < 0) + return 0; + } + + act->thread = thread; + act->ms.sym = sym; + act->fn = do_run_script; + return 1; +} + +static int +do_switch_data(struct hist_browser *browser __maybe_unused, + struct popup_action *act __maybe_unused) +{ + if (switch_data_file()) { + ui__warning("Won't switch the data files due to\n" + "no valid data file get selected!\n"); + return 0; + } + + return K_SWITCH_INPUT_DATA; +} + +static int +add_switch_opt(struct hist_browser *browser, + struct popup_action *act, char **optstr) +{ + if (!is_report_browser(browser->hbt)) + return 0; + + if (asprintf(optstr, "Switch to another data file in PWD") < 0) + return 0; + + act->fn = do_switch_data; + return 1; +} + +static int +do_exit_browser(struct hist_browser *browser __maybe_unused, + struct popup_action *act __maybe_unused) +{ + return 0; +} + +static int +add_exit_opt(struct hist_browser *browser __maybe_unused, + struct popup_action *act, char **optstr) +{ + if (asprintf(optstr, "Exit") < 0) + return 0; + + act->fn = do_exit_browser; + return 1; +} + +static int +do_zoom_socket(struct hist_browser *browser, struct popup_action *act) +{ + if (browser->hists->socket_filter > -1) { + pstack__remove(browser->pstack, &browser->hists->socket_filter); + browser->hists->socket_filter = -1; + perf_hpp__set_elide(HISTC_SOCKET, false); + } else { + browser->hists->socket_filter = act->socket; + perf_hpp__set_elide(HISTC_SOCKET, true); + pstack__push(browser->pstack, &browser->hists->socket_filter); + } + + hists__filter_by_socket(browser->hists); + hist_browser__reset(browser); + return 0; +} + +static int +add_socket_opt(struct hist_browser *browser, struct popup_action *act, + char **optstr, int socket_id) +{ + if (socket_id < 0) + return 0; + + if (asprintf(optstr, "Zoom %s Processor Socket %d", + (browser->hists->socket_filter > -1) ? "out of" : "into", + socket_id) < 0) + return 0; + + act->socket = socket_id; + act->fn = do_zoom_socket; + return 1; +} + static void hist_browser__update_nr_entries(struct hist_browser *hb) { u64 nr_entries = 0; @@ -1418,17 +1745,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, bool left_exits, struct hist_browser_timer *hbt, float min_pcnt, - struct perf_session_env *env) + struct perf_env *env) { struct hists *hists = evsel__hists(evsel); - struct hist_browser *browser = hist_browser__new(hists); + struct hist_browser *browser = hist_browser__new(hists, hbt, env); struct branch_info *bi; - struct pstack *fstack; - char *options[16]; +#define MAX_OPTIONS 16 + char *options[MAX_OPTIONS]; + struct popup_action actions[MAX_OPTIONS]; int nr_options = 0; int key = -1; char buf[64]; - char script_opt[64]; int delay_secs = hbt ? hbt->refresh : 0; struct perf_hpp_fmt *fmt; @@ -1440,14 +1767,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "For multiple event sessions:\n\n" \ "TAB/UNTAB Switch events\n\n" \ "For symbolic views (--sort has sym):\n\n" \ - "-> Zoom into DSO/Threads & Annotate current symbol\n" \ - "<- Zoom out\n" \ + "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ + "ESC Zoom out\n" \ "a Annotate current symbol\n" \ "C Collapse all callchains\n" \ "d Zoom into current DSO\n" \ "E Expand all callchains\n" \ "F Toggle percentage of filtered entries\n" \ "H Display column headers\n" \ + "m Display context menu\n" \ + "S Zoom into current Processor Socket\n" \ /* help messages are sorted by lexical order of the hotkey */ const char report_help[] = HIST_BROWSER_HELP_COMMON @@ -1463,46 +1792,59 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "t Zoom into current Thread\n" "V Verbose (DSO names in callchains, etc)\n" "z Toggle zeroing of samples\n" + "f Enable/Disable events\n" "/ Filter symbol by name"; if (browser == NULL) return -1; + /* reset abort key so that it can get Ctrl-C as a key */ + SLang_reset_tty(); + SLang_init_tty(0, 0, 0); + if (min_pcnt) { browser->min_pcnt = min_pcnt; hist_browser__update_nr_entries(browser); } - fstack = pstack__new(2); - if (fstack == NULL) + browser->pstack = pstack__new(3); + if (browser->pstack == NULL) goto out; ui_helpline__push(helpline); memset(options, 0, sizeof(options)); + memset(actions, 0, sizeof(actions)); - perf_hpp__for_each_format(fmt) + perf_hpp__for_each_format(fmt) { perf_hpp__reset_width(fmt, hists); + /* + * This is done just once, and activates the horizontal scrolling + * code in the ui_browser code, it would be better to have a the + * counter in the perf_hpp code, but I couldn't find doing it here + * works, FIXME by setting this in hist_browser__new, for now, be + * clever 8-) + */ + ++browser->b.columns; + } if (symbol_conf.col_width_list_str) perf_hpp__set_user_width(symbol_conf.col_width_list_str); while (1) { struct thread *thread = NULL; - const struct dso *dso = NULL; - int choice = 0, - annotate = -2, zoom_dso = -2, zoom_thread = -2, - annotate_f = -2, annotate_t = -2, browse_map = -2; - int scripts_comm = -2, scripts_symbol = -2, - scripts_all = -2, switch_data = -2; + struct map *map = NULL; + int choice = 0; + int socked_id = -1; nr_options = 0; - key = hist_browser__run(browser, hbt); + key = hist_browser__run(browser, helpline); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); - dso = browser->selection->map ? browser->selection->map->dso : NULL; + map = browser->selection->map; + socked_id = browser->he_selection->socket; } switch (key) { case K_TAB: @@ -1526,20 +1868,33 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, browser->selection->sym == NULL || browser->selection->map->dso->annotate_warned) continue; - goto do_annotate; + + actions->ms.map = browser->selection->map; + actions->ms.sym = browser->selection->sym; + do_annotate(browser, actions); + continue; case 'P': hist_browser__dump(browser); continue; case 'd': - goto zoom_dso; + actions->ms.map = map; + do_zoom_dso(browser, actions); + continue; case 'V': browser->show_dso = !browser->show_dso; continue; case 't': - goto zoom_thread; + actions->thread = thread; + do_zoom_thread(browser, actions); + continue; + case 'S': + actions->socket = socked_id; + do_zoom_socket(browser, actions); + continue; case '/': if (ui_browser__input_window("Symbol to show", - "Please enter the name of symbol you want to see", + "Please enter the name of symbol you want to see.\n" + "To remove the filter later, press / + ENTER.", buf, "ENTER: OK, ESC: Cancel", delay_secs * 2) == K_ENTER) { hists->symbol_filter_str = *buf ? buf : NULL; @@ -1548,12 +1903,18 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, } continue; case 'r': - if (is_report_browser(hbt)) - goto do_scripts; + if (is_report_browser(hbt)) { + actions->thread = NULL; + actions->ms.sym = NULL; + do_run_script(browser, actions); + } continue; case 's': - if (is_report_browser(hbt)) - goto do_data_switch; + if (is_report_browser(hbt)) { + key = do_switch_data(browser, actions); + if (key == K_SWITCH_INPUT_DATA) + goto out_free_stack; + } continue; case 'i': /* env->arch is NULL for live-mode (i.e. perf top) */ @@ -1578,36 +1939,66 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, continue; case K_ENTER: case K_RIGHT: + case 'm': /* menu */ break; + case K_ESC: case K_LEFT: { const void *top; - if (pstack__empty(fstack)) { + if (pstack__empty(browser->pstack)) { /* * Go back to the perf_evsel_menu__run or other user */ if (left_exits) goto out_free_stack; + + if (key == K_ESC && + ui_browser__dialog_yesno(&browser->b, + "Do you really want to exit?")) + goto out_free_stack; + continue; } - top = pstack__pop(fstack); - if (top == &browser->hists->dso_filter) - goto zoom_out_dso; - if (top == &browser->hists->thread_filter) - goto zoom_out_thread; + top = pstack__peek(browser->pstack); + if (top == &browser->hists->dso_filter) { + /* + * No need to set actions->dso here since + * it's just to remove the current filter. + * Ditto for thread below. + */ + do_zoom_dso(browser, actions); + } else if (top == &browser->hists->thread_filter) { + do_zoom_thread(browser, actions); + } else if (top == &browser->hists->socket_filter) { + do_zoom_socket(browser, actions); + } continue; } - case K_ESC: - if (!left_exits && - !ui_browser__dialog_yesno(&browser->b, - "Do you really want to exit?")) - continue; - /* Fall thru */ case 'q': case CTRL('c'): goto out_free_stack; + case 'f': + if (!is_report_browser(hbt)) { + struct perf_top *top = hbt->arg; + + perf_evlist__toggle_enable(top->evlist); + /* + * No need to refresh, resort/decay histogram + * entries if we are not collecting samples: + */ + if (top->evlist->enabled) { + helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; + hbt->refresh = delay_secs; + } else { + helpline = "Press 'f' again to re-enable the events"; + hbt->refresh = 0; + } + continue; + } + /* Fall thru */ default: + helpline = "Press '?' for help on key bindings"; continue; } @@ -1623,196 +2014,83 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, if (bi == NULL) goto skip_annotation; - if (bi->from.sym != NULL && - !bi->from.map->dso->annotate_warned && - asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) { - annotate_f = nr_options++; - } - - if (bi->to.sym != NULL && - !bi->to.map->dso->annotate_warned && - (bi->to.sym != bi->from.sym || - bi->to.map->dso != bi->from.map->dso) && - asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) { - annotate_t = nr_options++; - } + nr_options += add_annotate_opt(browser, + &actions[nr_options], + &options[nr_options], + bi->from.map, + bi->from.sym); + if (bi->to.sym != bi->from.sym) + nr_options += add_annotate_opt(browser, + &actions[nr_options], + &options[nr_options], + bi->to.map, + bi->to.sym); } else { - if (browser->selection->sym != NULL && - !browser->selection->map->dso->annotate_warned) { - struct annotation *notes; - - notes = symbol__annotation(browser->selection->sym); - - if (notes->src && - asprintf(&options[nr_options], "Annotate %s", - browser->selection->sym->name) > 0) { - annotate = nr_options++; - } - } + nr_options += add_annotate_opt(browser, + &actions[nr_options], + &options[nr_options], + browser->selection->map, + browser->selection->sym); } skip_annotation: - if (thread != NULL && - asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (browser->hists->thread_filter ? "out of" : "into"), - (thread->comm_set ? thread__comm_str(thread) : ""), - thread->tid) > 0) - zoom_thread = nr_options++; - - if (dso != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - (browser->hists->dso_filter ? "out of" : "into"), - (dso->kernel ? "the Kernel" : dso->short_name)) > 0) - zoom_dso = nr_options++; - - if (browser->selection != NULL && - browser->selection->map != NULL && - asprintf(&options[nr_options], "Browse map details") > 0) - browse_map = nr_options++; - + nr_options += add_thread_opt(browser, &actions[nr_options], + &options[nr_options], thread); + nr_options += add_dso_opt(browser, &actions[nr_options], + &options[nr_options], map); + nr_options += add_map_opt(browser, &actions[nr_options], + &options[nr_options], + browser->selection ? + browser->selection->map : NULL); + nr_options += add_socket_opt(browser, &actions[nr_options], + &options[nr_options], + socked_id); /* perf script support */ if (browser->he_selection) { - struct symbol *sym; - - if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", - thread__comm_str(browser->he_selection->thread)) > 0) - scripts_comm = nr_options++; - - sym = browser->he_selection->ms.sym; - if (sym && sym->namelen && - asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", - sym->name) > 0) - scripts_symbol = nr_options++; - } - - if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) - scripts_all = nr_options++; - - if (is_report_browser(hbt) && asprintf(&options[nr_options], - "Switch to another data file in PWD") > 0) - switch_data = nr_options++; -add_exit_option: - options[nr_options++] = (char *)"Exit"; -retry_popup_menu: - choice = ui__popup_menu(nr_options, options); - - if (choice == nr_options - 1) - break; - - if (choice == -1) { - free_popup_options(options, nr_options - 1); - continue; - } - - if (choice == annotate || choice == annotate_t || choice == annotate_f) { - struct hist_entry *he; - struct annotation *notes; - struct map_symbol ms; - int err; -do_annotate: - if (!objdump_path && perf_session_env__lookup_objdump(env)) - continue; - - he = hist_browser__selected_entry(browser); - if (he == NULL) - continue; - - if (choice == annotate_f) { - ms.map = he->branch_info->from.map; - ms.sym = he->branch_info->from.sym; - } else if (choice == annotate_t) { - ms.map = he->branch_info->to.map; - ms.sym = he->branch_info->to.sym; - } else { - ms = *browser->selection; - } - - notes = symbol__annotation(ms.sym); - if (!notes->src) - continue; - - err = map_symbol__tui_annotate(&ms, evsel, hbt); + nr_options += add_script_opt(browser, + &actions[nr_options], + &options[nr_options], + thread, NULL); /* - * offer option to annotate the other branch source or target - * (if they exists) when returning from annotate + * Note that browser->selection != NULL + * when browser->he_selection is not NULL, + * so we don't need to check browser->selection + * before fetching browser->selection->sym like what + * we do before fetching browser->selection->map. + * + * See hist_browser__show_entry. */ - if ((err == 'q' || err == CTRL('c')) - && annotate_t != -2 && annotate_f != -2) - goto retry_popup_menu; - - ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); - if (err) - ui_browser__handle_resize(&browser->b); - - } else if (choice == browse_map) - map__browse(browser->selection->map); - else if (choice == zoom_dso) { -zoom_dso: - if (browser->hists->dso_filter) { - pstack__remove(fstack, &browser->hists->dso_filter); -zoom_out_dso: - ui_helpline__pop(); - browser->hists->dso_filter = NULL; - perf_hpp__set_elide(HISTC_DSO, false); - } else { - if (dso == NULL) - continue; - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - browser->hists->dso_filter = dso; - perf_hpp__set_elide(HISTC_DSO, true); - pstack__push(fstack, &browser->hists->dso_filter); - } - hists__filter_by_dso(hists); - hist_browser__reset(browser); - } else if (choice == zoom_thread) { -zoom_thread: - if (browser->hists->thread_filter) { - pstack__remove(fstack, &browser->hists->thread_filter); -zoom_out_thread: - ui_helpline__pop(); - thread__zput(browser->hists->thread_filter); - perf_hpp__set_elide(HISTC_THREAD, false); - } else { - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", - thread->comm_set ? thread__comm_str(thread) : "", - thread->tid); - browser->hists->thread_filter = thread__get(thread); - perf_hpp__set_elide(HISTC_THREAD, false); - pstack__push(fstack, &browser->hists->thread_filter); - } - hists__filter_by_thread(hists); - hist_browser__reset(browser); + nr_options += add_script_opt(browser, + &actions[nr_options], + &options[nr_options], + NULL, browser->selection->sym); } - /* perf scripts support */ - else if (choice == scripts_all || choice == scripts_comm || - choice == scripts_symbol) { -do_scripts: - memset(script_opt, 0, 64); - - if (choice == scripts_comm) - sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); + nr_options += add_script_opt(browser, &actions[nr_options], + &options[nr_options], NULL, NULL); + nr_options += add_switch_opt(browser, &actions[nr_options], + &options[nr_options]); +add_exit_option: + nr_options += add_exit_opt(browser, &actions[nr_options], + &options[nr_options]); - if (choice == scripts_symbol) - sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); + do { + struct popup_action *act; - script_browse(script_opt); - } - /* Switch to another data file */ - else if (choice == switch_data) { -do_data_switch: - if (!switch_data_file()) { - key = K_SWITCH_INPUT_DATA; + choice = ui__popup_menu(nr_options, options); + if (choice == -1 || choice >= nr_options) break; - } else - ui__warning("Won't switch the data files due to\n" - "no valid data file get selected!\n"); - } + + act = &actions[choice]; + key = act->fn(browser, act); + } while (key == 1); + + if (key == K_SWITCH_INPUT_DATA) + break; } out_free_stack: - pstack__delete(fstack); + pstack__delete(browser->pstack); out: hist_browser__delete(browser); - free_popup_options(options, nr_options - 1); + free_popup_options(options, MAX_OPTIONS); return key; } @@ -1821,7 +2099,7 @@ struct perf_evsel_menu { struct perf_evsel *selection; bool lost_events, lost_events_warned; float min_pcnt; - struct perf_session_env *env; + struct perf_env *env; }; static void perf_evsel_menu__write(struct ui_browser *browser, @@ -1855,7 +2133,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, nr_events = convert_unit(nr_events, &unit); printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, unit, unit == ' ' ? "" : " ", ev_name); - slsmg_printf("%s", bf); + ui_browser__printf(browser, "%s", bf); nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; if (nr_events != 0) { @@ -1868,7 +2146,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, warn = bf; } - slsmg_write_nstring(warn, browser->width - printed); + ui_browser__write_nstring(browser, warn, browser->width - printed); if (current_entry) menu->selection = evsel; @@ -1931,15 +2209,11 @@ browse_hists: else pos = perf_evsel__prev(pos); goto browse_hists; - case K_ESC: - if (!ui_browser__dialog_yesno(&menu->b, - "Do you really want to exit?")) - continue; - /* Fall thru */ case K_SWITCH_INPUT_DATA: case 'q': case CTRL('c'): goto out; + case K_ESC: default: continue; } @@ -1978,7 +2252,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, int nr_entries, const char *help, struct hist_browser_timer *hbt, float min_pcnt, - struct perf_session_env *env) + struct perf_env *env) { struct perf_evsel *pos; struct perf_evsel_menu menu = { @@ -2011,7 +2285,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, struct hist_browser_timer *hbt, float min_pcnt, - struct perf_session_env *env) + struct perf_env *env) { int nr_entries = evlist->nr_entries; |