diff options
Diffstat (limited to 'tools/perf/ui')
-rw-r--r-- | tools/perf/ui/Build | 1 | ||||
-rw-r--r-- | tools/perf/ui/browser.c | 10 | ||||
-rw-r--r-- | tools/perf/ui/browser.h | 1 | ||||
-rw-r--r-- | tools/perf/ui/browsers/annotate-data.c | 1 | ||||
-rw-r--r-- | tools/perf/ui/browsers/annotate.c | 24 | ||||
-rw-r--r-- | tools/perf/ui/browsers/header.c | 1 | ||||
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 123 | ||||
-rw-r--r-- | tools/perf/ui/browsers/map.c | 4 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 307 | ||||
-rw-r--r-- | tools/perf/ui/keysyms.c | 44 | ||||
-rw-r--r-- | tools/perf/ui/keysyms.h | 2 | ||||
-rw-r--r-- | tools/perf/ui/stdio/hist.c | 57 |
12 files changed, 486 insertions, 89 deletions
diff --git a/tools/perf/ui/Build b/tools/perf/ui/Build index d2ecd9290600..6005f813c9e3 100644 --- a/tools/perf/ui/Build +++ b/tools/perf/ui/Build @@ -8,5 +8,6 @@ perf-ui-y += stdio/hist.o CFLAGS_setup.o += -DLIBDIR="BUILD_STR($(LIBDIR))" perf-ui-$(CONFIG_SLANG) += browser.o +perf-ui-$(CONFIG_SLANG) += keysyms.o perf-ui-$(CONFIG_SLANG) += browsers/ perf-ui-$(CONFIG_SLANG) += tui/ diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 19503e838738..dc88427b4ae5 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -233,6 +233,14 @@ int ui_browser__warning(struct ui_browser *browser, int timeout, return key; } +int ui_browser__warn_unhandled_hotkey(struct ui_browser *browser, int key, int timeout, const char *help) +{ + char kname[32]; + + key_name(key, kname, sizeof(kname)); + return ui_browser__warning(browser, timeout, "\n'%s' key not associated%s!\n", kname, help ?: ""); +} + int ui_browser__help_window(struct ui_browser *browser, const char *text) { int key; @@ -451,6 +459,8 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs) goto out; if (browser->horiz_scroll != 0) --browser->horiz_scroll; + else + goto out; break; case K_PGDN: case ' ': diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 6e98d5f8f71c..f59ad4f14d33 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -66,6 +66,7 @@ void __ui_browser__vline(struct ui_browser *browser, unsigned int column, int ui_browser__warning(struct ui_browser *browser, int timeout, const char *format, ...); +int ui_browser__warn_unhandled_hotkey(struct ui_browser *browser, int key, int timeout, const char *help); int ui_browser__help_window(struct ui_browser *browser, const char *text); bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); int ui_browser__input_window(const char *title, const char *text, char *input, diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index cd562a8822b7..aa8c89fe2e82 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -558,6 +558,7 @@ static int annotated_data_browser__run(struct annotated_data_browser *browser, case CTRL('c'): goto out; default: + ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); continue; } } diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 135d6ce88fb3..ab776b1ed2d5 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -406,6 +406,9 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser) browser->b.index = al->idx_asm; } + if (annotate_opts.hide_src_code_on_title) + annotate_opts.hide_src_code_on_title = false; + return true; } @@ -704,6 +707,18 @@ switch_percent_type(struct annotation_options *opts, bool base) } } +static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size) +{ + int printed = hists__scnprintf_title(hists, bf, size); + + if (!annotate_opts.hide_src_code_on_title) { + printed += scnprintf(bf + printed, size - printed, " [source: %s]", + annotate_opts.hide_src_code ? "OFF" : "On"); + } + + return printed; +} + static int annotate_browser__run(struct annotate_browser *browser, struct evsel *evsel, struct hist_browser_timer *hbt) @@ -719,7 +734,7 @@ static int annotate_browser__run(struct annotate_browser *browser, char title[256]; int key; - hists__scnprintf_title(hists, title, sizeof(title)); + annotate__scnprintf_title(hists, title, sizeof(title)); if (annotate_browser__show(&browser->b, title, help) < 0) return -1; @@ -755,7 +770,7 @@ static int annotate_browser__run(struct annotate_browser *browser, if (delay_secs != 0) { symbol__annotate_decay_histogram(sym, evsel); - hists__scnprintf_title(hists, title, sizeof(title)); + annotate__scnprintf_title(hists, title, sizeof(title)); annotate_browser__show(&browser->b, title, help); } continue; @@ -820,6 +835,8 @@ static int annotate_browser__run(struct annotate_browser *browser, case 's': if (annotate_browser__toggle_source(browser)) ui_helpline__puts(help); + annotate__scnprintf_title(hists, title, sizeof(title)); + annotate_browser__show(&browser->b, title, help); continue; case 'o': annotate_opts.use_offset = !annotate_opts.use_offset; @@ -906,7 +923,7 @@ show_sup_ins: case 'p': case 'b': switch_percent_type(&annotate_opts, key == 'b'); - hists__scnprintf_title(hists, title, sizeof(title)); + annotate__scnprintf_title(hists, title, sizeof(title)); annotate_browser__show(&browser->b, title, help); continue; case 'B': @@ -928,6 +945,7 @@ show_sup_ins: case CTRL('c'): goto out; default: + ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); continue; } diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c index 57e6e4332f74..2213b4661600 100644 --- a/tools/perf/ui/browsers/header.c +++ b/tools/perf/ui/browsers/header.c @@ -69,6 +69,7 @@ static int list_menu__run(struct ui_browser *menu) key = -1; break; default: + ui_browser__warn_unhandled_hotkey(menu, key, 0, ", use 'h'/'?'/F1 to see actions"); continue; } diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 35c10509b797..d26b925e3d7f 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1266,6 +1266,16 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ _fmttype); \ } +#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \ +static int \ +hist_browser__hpp_color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, \ + struct hist_entry *he) \ +{ \ + return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ + " %5.1f%%", __hpp__slsmg_color_printf);\ +} + __HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) __HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) @@ -1274,9 +1284,15 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT) __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT) __HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY) +__HPP_COLOR_MEM_STAT_FN(op, OP) +__HPP_COLOR_MEM_STAT_FN(cache, CACHE) +__HPP_COLOR_MEM_STAT_FN(memory, MEMORY) +__HPP_COLOR_MEM_STAT_FN(snoop, SNOOP) +__HPP_COLOR_MEM_STAT_FN(dtlb, DTLB) #undef __HPP_COLOR_PERCENT_FN #undef __HPP_COLOR_ACC_PERCENT_FN +#undef __HPP_COLOR_MEM_STAT_FN void hist_browser__init_hpp(void) { @@ -1296,6 +1312,16 @@ void hist_browser__init_hpp(void) hist_browser__hpp_color_overhead_acc; perf_hpp__format[PERF_HPP__LATENCY_ACC].color = hist_browser__hpp_color_latency_acc; + perf_hpp__format[PERF_HPP__MEM_STAT_OP].color = + hist_browser__hpp_color_mem_stat_op; + perf_hpp__format[PERF_HPP__MEM_STAT_CACHE].color = + hist_browser__hpp_color_mem_stat_cache; + perf_hpp__format[PERF_HPP__MEM_STAT_MEMORY].color = + hist_browser__hpp_color_mem_stat_memory; + perf_hpp__format[PERF_HPP__MEM_STAT_SNOOP].color = + hist_browser__hpp_color_mem_stat_snoop; + perf_hpp__format[PERF_HPP__MEM_STAT_DTLB].color = + hist_browser__hpp_color_mem_stat_dtlb; res_sample_init(); } @@ -1686,7 +1712,8 @@ hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, return ret; } -static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) +static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, + char *buf, size_t size, int line) { struct hists *hists = browser->hists; struct perf_hpp dummy_hpp = { @@ -1712,7 +1739,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows if (column++ < browser->b.horiz_scroll) continue; - ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); + ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL); if (advance_hpp_check(&dummy_hpp, ret)) break; @@ -1723,6 +1750,9 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows first_node = false; } + if (line < hists->hpp_list->nr_header_lines - 1) + return ret; + if (!first_node) { ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", indent * HIERARCHY_INDENT, ""); @@ -1753,7 +1783,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows } first_col = false; - ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); + ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL); dummy_hpp.buf[ret] = '\0'; start = strim(dummy_hpp.buf); @@ -1772,14 +1802,18 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows static void hists_browser__hierarchy_headers(struct hist_browser *browser) { + struct perf_hpp_list *hpp_list = browser->hists->hpp_list; char headers[1024]; + int line; - hists_browser__scnprintf_hierarchy_headers(browser, headers, - sizeof(headers)); + for (line = 0; line < hpp_list->nr_header_lines; line++) { + hists_browser__scnprintf_hierarchy_headers(browser, headers, + sizeof(headers), line); - ui_browser__gotorc_title(&browser->b, 0, 0); - ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); - ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); + ui_browser__gotorc_title(&browser->b, line, 0); + ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); + ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); + } } static void hists_browser__headers(struct hist_browser *browser) @@ -2422,7 +2456,6 @@ close_file_and_continue: struct popup_action { unsigned long time; struct thread *thread; - struct evsel *evsel; int (*fn)(struct hist_browser *browser, struct popup_action *act); struct map_symbol ms; int socket; @@ -2489,8 +2522,7 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map) } static int -add_annotate_opt(struct hist_browser *browser __maybe_unused, - struct popup_action *act, char **optstr, +add_annotate_opt(struct popup_action *act, char **optstr, struct map_symbol *ms, u64 addr) { @@ -2514,18 +2546,17 @@ add_annotate_opt(struct hist_browser *browser __maybe_unused, } static int -do_annotate_type(struct hist_browser *browser, struct popup_action *act) +do_annotate_type(struct hist_browser *browser, struct popup_action *act __maybe_unused) { struct hist_entry *he = browser->he_selection; - hist_entry__annotate_data_tui(he, act->evsel, browser->hbt); + hist_entry__annotate_data_tui(he, hists_to_evsel(browser->hists), browser->hbt); ui_browser__handle_resize(&browser->b); return 0; } static int -add_annotate_type_opt(struct hist_browser *browser, - struct popup_action *act, char **optstr, +add_annotate_type_opt(struct popup_action *act, char **optstr, struct hist_entry *he) { if (he == NULL || he->mem_type == NULL || he->mem_type->histograms == NULL) @@ -2534,7 +2565,6 @@ add_annotate_type_opt(struct hist_browser *browser, if (asprintf(optstr, "Annotate type %s", he->mem_type->self.type_name) < 0) return 0; - act->evsel = hists_to_evsel(browser->hists); act->fn = do_annotate_type; return 1; } @@ -2695,7 +2725,7 @@ add_map_opt(struct hist_browser *browser, } static int -do_run_script(struct hist_browser *browser __maybe_unused, +do_run_script(struct hist_browser *browser, struct popup_action *act) { char *script_opt; @@ -2734,27 +2764,26 @@ do_run_script(struct hist_browser *browser __maybe_unused, n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); } - script_browse(script_opt, act->evsel); + script_browse(script_opt, hists_to_evsel(browser->hists)); free(script_opt); return 0; } static int -do_res_sample_script(struct hist_browser *browser __maybe_unused, +do_res_sample_script(struct hist_browser *browser, struct popup_action *act) { struct hist_entry *he; he = hist_browser__selected_entry(browser); - res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); + res_sample_browse(he->res_samples, he->num_res, hists_to_evsel(browser->hists), act->rstype); return 0; } static int -add_script_opt_2(struct hist_browser *browser __maybe_unused, - struct popup_action *act, char **optstr, +add_script_opt_2(struct popup_action *act, char **optstr, struct thread *thread, struct symbol *sym, - struct evsel *evsel, const char *tstr) + const char *tstr) { if (thread) { @@ -2772,7 +2801,6 @@ add_script_opt_2(struct hist_browser *browser __maybe_unused, act->thread = thread; act->ms.sym = sym; - act->evsel = evsel; act->fn = do_run_script; return 1; } @@ -2780,13 +2808,12 @@ add_script_opt_2(struct hist_browser *browser __maybe_unused, static int add_script_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, - struct thread *thread, struct symbol *sym, - struct evsel *evsel) + struct thread *thread, struct symbol *sym) { int n, j; struct hist_entry *he; - n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); + n = add_script_opt_2(act, optstr, thread, sym, ""); he = hist_browser__selected_entry(browser); if (sort_order && strstr(sort_order, "time")) { @@ -2800,8 +2827,7 @@ add_script_opt(struct hist_browser *browser, j += sprintf(tstr + j, "-"); timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, tstr + j, sizeof tstr - j); - n += add_script_opt_2(browser, act, optstr, thread, sym, - evsel, tstr); + n += add_script_opt_2(act, optstr, thread, sym, tstr); act->time = he->time; } return n; @@ -2811,7 +2837,6 @@ static int add_res_sample_opt(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr, struct res_sample *res_sample, - struct evsel *evsel, enum rstype type) { if (!res_sample) @@ -2823,7 +2848,6 @@ add_res_sample_opt(struct hist_browser *browser __maybe_unused, return 0; act->fn = do_res_sample_script; - act->evsel = evsel; act->rstype = type; return 1; } @@ -3274,10 +3298,10 @@ do_hotkey: // key came straight from options ui__popup_menu() /* * 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) { + actions->thread = thread; do_zoom_thread(browser, actions); } else if (top == &browser->hists->socket_filter) { do_zoom_socket(browser, actions); @@ -3308,6 +3332,8 @@ do_hotkey: // key came straight from options ui__popup_menu() /* Fall thru */ default: helpline = "Press '?' for help on key bindings"; + ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, + ", use 'h'/'?'/F1 to see actions"); continue; } @@ -3322,27 +3348,23 @@ do_hotkey: // key came straight from options ui__popup_menu() if (bi == NULL) goto skip_annotation; - nr_options += add_annotate_opt(browser, - &actions[nr_options], + nr_options += add_annotate_opt(&actions[nr_options], &options[nr_options], &bi->from.ms, bi->from.al_addr); if (bi->to.ms.sym != bi->from.ms.sym) - nr_options += add_annotate_opt(browser, - &actions[nr_options], + nr_options += add_annotate_opt(&actions[nr_options], &options[nr_options], &bi->to.ms, bi->to.al_addr); } else if (browser->he_selection) { - nr_options += add_annotate_opt(browser, - &actions[nr_options], + nr_options += add_annotate_opt(&actions[nr_options], &options[nr_options], browser->selection, browser->he_selection->ip); } skip_annotation: - nr_options += add_annotate_type_opt(browser, - &actions[nr_options], + nr_options += add_annotate_type_opt(&actions[nr_options], &options[nr_options], browser->he_selection); nr_options += add_thread_opt(browser, &actions[nr_options], @@ -3366,7 +3388,7 @@ skip_annotation: nr_options += add_script_opt(browser, &actions[nr_options], &options[nr_options], - thread, NULL, evsel); + thread, NULL); } /* * Note that browser->selection != NULL @@ -3381,24 +3403,23 @@ skip_annotation: nr_options += add_script_opt(browser, &actions[nr_options], &options[nr_options], - NULL, browser->selection->sym, - evsel); + NULL, browser->selection->sym); } } nr_options += add_script_opt(browser, &actions[nr_options], - &options[nr_options], NULL, NULL, evsel); + &options[nr_options], NULL, NULL); nr_options += add_res_sample_opt(browser, &actions[nr_options], &options[nr_options], hist_browser__selected_res_sample(browser), - evsel, A_NORMAL); + A_NORMAL); nr_options += add_res_sample_opt(browser, &actions[nr_options], &options[nr_options], hist_browser__selected_res_sample(browser), - evsel, A_ASM); + A_ASM); nr_options += add_res_sample_opt(browser, &actions[nr_options], &options[nr_options], hist_browser__selected_res_sample(browser), - evsel, A_SOURCE); + A_SOURCE); nr_options += add_switch_opt(browser, &actions[nr_options], &options[nr_options]); skip_scripting: @@ -3568,6 +3589,7 @@ browse_hists: case CTRL('c'): goto out; default: + ui_browser__warn_unhandled_hotkey(&menu->b, key, delay_secs, NULL); continue; } } @@ -3693,7 +3715,7 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, struct popup_action action; char *br_cntr_text = NULL; static const char help[] = - " q Quit \n" + " q/ESC Quit \n" " B Branch counter abbr list (Optional)\n"; browser = hist_browser__new(hists); @@ -3720,6 +3742,7 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, switch (key) { case 'q': + case K_ESC: goto out; case '?': ui_browser__help_window(&browser->b, help); @@ -3746,7 +3769,9 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, } continue; default: - break; + ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, + ", use '?' to see actions"); + continue; } } diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index fba55175a935..c61ba3174a24 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -88,8 +88,10 @@ static int map_browser__run(struct map_browser *browser) case '/': if (verbose > 0) map_browser__search(browser); + /* fall thru */ default: - break; + ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, NULL); + continue; case K_LEFT: case K_ESC: case 'q': diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index ae3b7fe1dadc..b085eb0de849 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -11,6 +11,8 @@ #include "../util/sort.h" #include "../util/evsel.h" #include "../util/evlist.h" +#include "../util/mem-events.h" +#include "../util/string2.h" #include "../util/thread.h" #include "../util/util.h" @@ -150,6 +152,48 @@ int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmtype); } +int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp, + struct hist_entry *he, enum mem_stat_type mst, + const char *fmtstr, hpp_snprint_fn print_fn) +{ + struct hists *hists = he->hists; + int mem_stat_idx = -1; + char *buf = hpp->buf; + size_t size = hpp->size; + u64 total = 0; + int ret = 0; + + for (int i = 0; i < hists->nr_mem_stats; i++) { + if (hists->mem_stat_types[i] == mst) { + mem_stat_idx = i; + break; + } + } + assert(mem_stat_idx != -1); + + for (int i = 0; i < MEM_STAT_LEN; i++) + total += hists->mem_stat_total[mem_stat_idx].entries[i]; + assert(total != 0); + + for (int i = 0; i < MEM_STAT_LEN; i++) { + u64 val = he->mem_stat[mem_stat_idx].entries[i]; + + if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) + continue; + + ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total); + } + + /* + * Restore original buf and size as it's where caller expects + * the result will be saved. + */ + hpp->buf = buf; + hpp->size = size; + + return ret; +} + static int field_cmp(u64 field_a, u64 field_b) { if (field_a > field_b) @@ -294,6 +338,37 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, return ret; } +static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt); + +static enum mem_stat_type hpp__mem_stat_type(struct perf_hpp_fmt *fmt) +{ + if (!perf_hpp__is_mem_stat_entry(fmt)) + return -1; + + switch (fmt->idx) { + case PERF_HPP__MEM_STAT_OP: + return PERF_MEM_STAT_OP; + case PERF_HPP__MEM_STAT_CACHE: + return PERF_MEM_STAT_CACHE; + case PERF_HPP__MEM_STAT_MEMORY: + return PERF_MEM_STAT_MEMORY; + case PERF_HPP__MEM_STAT_SNOOP: + return PERF_MEM_STAT_SNOOP; + case PERF_HPP__MEM_STAT_DTLB: + return PERF_MEM_STAT_DTLB; + default: + break; + } + pr_debug("Should not reach here\n"); + return -1; +} + +static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *a, struct hist_entry *b) +{ + return a->stat.period - b->stat.period; +} + static int hpp__width_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, struct hists *hists) @@ -321,11 +396,78 @@ static int hpp__width_fn(struct perf_hpp_fmt *fmt, } static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct hists *hists, int line __maybe_unused, + struct hists *hists, int line, int *span __maybe_unused) { int len = hpp__width_fn(fmt, hpp, hists); - return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); + const char *hdr = ""; + + if (line == hists->hpp_list->nr_header_lines - 1) + hdr = fmt->name; + + return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr); +} + +static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hists *hists, int line, + int *span __maybe_unused) +{ + char *buf = hpp->buf; + int ret = 0; + int len; + enum mem_stat_type mst = hpp__mem_stat_type(fmt); + int mem_stat_idx = -1; + + for (int i = 0; i < hists->nr_mem_stats; i++) { + if (hists->mem_stat_types[i] == mst) { + mem_stat_idx = i; + break; + } + } + assert(mem_stat_idx != -1); + + if (line == 0) { + int left, right; + + len = 0; + /* update fmt->len for acutally used columns only */ + for (int i = 0; i < MEM_STAT_LEN; i++) { + if (hists->mem_stat_total[mem_stat_idx].entries[i]) + len += MEM_STAT_PRINT_LEN; + } + fmt->len = len; + + /* print header directly if single column only */ + if (len == MEM_STAT_PRINT_LEN) + return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); + + left = (len - strlen(fmt->name)) / 2 - 1; + right = len - left - strlen(fmt->name) - 2; + + if (left < 0) + left = 0; + if (right < 0) + right = 0; + + return scnprintf(hpp->buf, hpp->size, "%.*s %s %.*s", + left, graph_dotted_line, fmt->name, right, graph_dotted_line); + } + + + len = hpp->size; + for (int i = 0; i < MEM_STAT_LEN; i++) { + int printed; + + if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) + continue; + + printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN, + mem_stat_name(mst, i)); + ret += printed; + buf += printed; + len -= printed; + } + return ret; } int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) @@ -453,6 +595,23 @@ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ return __hpp__sort(a, b, he_get_##_field); \ } +#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \ +static int hpp__color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, \ + struct hist_entry *he) \ +{ \ + return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ + " %5.1f%%", hpp_color_scnprintf); \ +} + +#define __HPP_ENTRY_MEM_STAT_FN(_name, _type) \ +static int hpp__entry_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, \ + struct hist_entry *he) \ +{ \ + return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ + " %5.1f%%", hpp_entry_scnprintf); \ +} #define HPP_PERCENT_FNS(_type, _field, _fmttype) \ __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ @@ -472,6 +631,10 @@ __HPP_SORT_RAW_FN(_type, _field) __HPP_ENTRY_AVERAGE_FN(_type, _field) \ __HPP_SORT_AVERAGE_FN(_type, _field) +#define HPP_MEM_STAT_FNS(_name, _type) \ +__HPP_COLOR_MEM_STAT_FN(_name, _type) \ +__HPP_ENTRY_MEM_STAT_FN(_name, _type) + HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) @@ -488,6 +651,12 @@ HPP_AVERAGE_FNS(weight1, weight1) HPP_AVERAGE_FNS(weight2, weight2) HPP_AVERAGE_FNS(weight3, weight3) +HPP_MEM_STAT_FNS(op, OP) +HPP_MEM_STAT_FNS(cache, CACHE) +HPP_MEM_STAT_FNS(memory, MEMORY) +HPP_MEM_STAT_FNS(snoop, SNOOP) +HPP_MEM_STAT_FNS(dtlb, DTLB) + static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *a __maybe_unused, struct hist_entry *b __maybe_unused) @@ -495,6 +664,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, return 0; } +static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) +{ + return fmt->sort == hpp__sort_mem_stat; +} + static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) { return a->header == hpp__header_fn; @@ -508,6 +682,14 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) return a->idx == b->idx; } +static bool hpp__equal_mem_stat(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) +{ + if (!perf_hpp__is_mem_stat_entry(a) || !perf_hpp__is_mem_stat_entry(b)) + return false; + + return a->entry == b->entry; +} + #define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ { \ .name = _name, \ @@ -549,6 +731,20 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) .equal = hpp__equal, \ } +#define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type) \ + { \ + .name = _name, \ + .header = hpp__header_mem_stat_fn, \ + .width = hpp__width_fn, \ + .color = hpp__color_mem_stat_ ## _fn, \ + .entry = hpp__entry_mem_stat_ ## _fn, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_mem_stat, \ + .idx = PERF_HPP__MEM_STAT_ ## _type, \ + .equal = hpp__equal_mem_stat, \ + } + struct perf_hpp_fmt perf_hpp__format[] = { HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY), @@ -563,6 +759,11 @@ struct perf_hpp_fmt perf_hpp__format[] = { HPP__PRINT_FNS("Weight1", weight1, WEIGHT1), HPP__PRINT_FNS("Weight2", weight2, WEIGHT2), HPP__PRINT_FNS("Weight3", weight3, WEIGHT3), + HPP__MEM_STAT_PRINT_FNS("Mem Op", op, OP), + HPP__MEM_STAT_PRINT_FNS("Cache", cache, CACHE), + HPP__MEM_STAT_PRINT_FNS("Memory", memory, MEMORY), + HPP__MEM_STAT_PRINT_FNS("Snoop", snoop, SNOOP), + HPP__MEM_STAT_PRINT_FNS("D-TLB", dtlb, DTLB), }; struct perf_hpp_list perf_hpp_list = { @@ -574,11 +775,13 @@ struct perf_hpp_list perf_hpp_list = { #undef HPP__COLOR_PRINT_FNS #undef HPP__COLOR_ACC_PRINT_FNS #undef HPP__PRINT_FNS +#undef HPP__MEM_STAT_PRINT_FNS #undef HPP_PERCENT_FNS #undef HPP_PERCENT_ACC_FNS #undef HPP_RAW_FNS #undef HPP_AVERAGE_FNS +#undef HPP_MEM_STAT_FNS #undef __HPP_HEADER_FN #undef __HPP_WIDTH_FN @@ -588,6 +791,9 @@ struct perf_hpp_list perf_hpp_list = { #undef __HPP_ENTRY_ACC_PERCENT_FN #undef __HPP_ENTRY_RAW_FN #undef __HPP_ENTRY_AVERAGE_FN +#undef __HPP_COLOR_MEM_STAT_FN +#undef __HPP_ENTRY_MEM_STAT_FN + #undef __HPP_SORT_FN #undef __HPP_SORT_ACC_FN #undef __HPP_SORT_RAW_FN @@ -696,12 +902,14 @@ void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list, static void perf_hpp__column_unregister(struct perf_hpp_fmt *format) { list_del_init(&format->list); + list_del_init(&format->sort_list); fmt_free(format); } -void perf_hpp__cancel_cumulate(void) +void perf_hpp__cancel_cumulate(struct evlist *evlist) { struct perf_hpp_fmt *fmt, *acc, *ovh, *acc_lat, *tmp; + struct evsel *evsel; if (is_strict_order(field_order)) return; @@ -719,11 +927,29 @@ void perf_hpp__cancel_cumulate(void) if (fmt_equal(ovh, fmt)) fmt->name = "Overhead"; } + + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + struct perf_hpp_list_node *node; + + list_for_each_entry(node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { + if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) { + perf_hpp__column_unregister(fmt); + continue; + } + + if (fmt_equal(ovh, fmt)) + fmt->name = "Overhead"; + } + } + } } -void perf_hpp__cancel_latency(void) +void perf_hpp__cancel_latency(struct evlist *evlist) { struct perf_hpp_fmt *fmt, *lat, *acc, *tmp; + struct evsel *evsel; if (is_strict_order(field_order)) return; @@ -737,6 +963,18 @@ void perf_hpp__cancel_latency(void) if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) perf_hpp__column_unregister(fmt); } + + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + struct perf_hpp_list_node *node; + + list_for_each_entry(node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { + if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) + perf_hpp__column_unregister(fmt); + } + } + } } void perf_hpp__setup_output_field(struct perf_hpp_list *list) @@ -787,18 +1025,12 @@ void perf_hpp__reset_output_field(struct perf_hpp_list *list) struct perf_hpp_fmt *fmt, *tmp; /* reset output fields */ - perf_hpp_list__for_each_format_safe(list, fmt, tmp) { - list_del_init(&fmt->list); - list_del_init(&fmt->sort_list); - fmt_free(fmt); - } + perf_hpp_list__for_each_format_safe(list, fmt, tmp) + perf_hpp__column_unregister(fmt); /* reset sort keys */ - perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) { - list_del_init(&fmt->list); - list_del_init(&fmt->sort_list); - fmt_free(fmt); - } + perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) + perf_hpp__column_unregister(fmt); } /* @@ -886,6 +1118,14 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) fmt->len = 8; break; + case PERF_HPP__MEM_STAT_OP: + case PERF_HPP__MEM_STAT_CACHE: + case PERF_HPP__MEM_STAT_MEMORY: + case PERF_HPP__MEM_STAT_SNOOP: + case PERF_HPP__MEM_STAT_DTLB: + fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN; + break; + default: break; } @@ -991,3 +1231,42 @@ int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, return 0; } + +int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist) +{ + struct perf_hpp_fmt *fmt; + struct evsel *evsel; + enum mem_stat_type mst[16]; + unsigned nr_mem_stats = 0; + + perf_hpp_list__for_each_format(list, fmt) { + if (!perf_hpp__is_mem_stat_entry(fmt)) + continue; + + assert(nr_mem_stats < ARRAY_SIZE(mst)); + mst[nr_mem_stats++] = hpp__mem_stat_type(fmt); + } + + if (nr_mem_stats == 0) + return 0; + + list->nr_header_lines = 2; + + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + + hists->mem_stat_types = calloc(nr_mem_stats, + sizeof(*hists->mem_stat_types)); + if (hists->mem_stat_types == NULL) + return -ENOMEM; + + hists->mem_stat_total = calloc(nr_mem_stats, + sizeof(*hists->mem_stat_total)); + if (hists->mem_stat_total == NULL) + return -ENOMEM; + + memcpy(hists->mem_stat_types, mst, nr_mem_stats * sizeof(*mst)); + hists->nr_mem_stats = nr_mem_stats; + } + return 0; +} diff --git a/tools/perf/ui/keysyms.c b/tools/perf/ui/keysyms.c new file mode 100644 index 000000000000..b64564b07f2f --- /dev/null +++ b/tools/perf/ui/keysyms.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include "keysyms.h" +#include <linux/ctype.h> +#include <linux/kernel.h> + +const char *key_name(int key, char *bf, size_t size) +{ + if (isprint(key)) { + scnprintf(bf, size, "%c", key); + } else if (key < 32) { + scnprintf(bf, size, "Ctrl+%c", key + '@'); + } else { + const char *name = NULL; + + switch (key) { + case K_DOWN: name = "Down"; break; + case K_END: name = "End"; break; + case K_ENTER: name = "Enter"; break; + case K_ESC: name = "ESC"; break; + case K_F1: name = "F1"; break; + case K_HOME: name = "Home"; break; + case K_LEFT: name = "Left"; break; + case K_PGDN: name = "PgDown"; break; + case K_PGUP: name = "PgUp"; break; + case K_RIGHT: name = "Right"; break; + case K_TAB: name = "Tab"; break; + case K_UNTAB: name = "Untab"; break; + case K_UP: name = "Up"; break; + case K_BKSPC: name = "Backspace"; break; + case K_DEL: name = "Del"; break; + default: + if (key >= SL_KEY_F(1) && key <= SL_KEY_F(63)) + scnprintf(bf, size, "F%d", key - SL_KEY_F(0)); + else + scnprintf(bf, size, "Unknown (%d)", key); + } + + if (name) + scnprintf(bf, size, "%s", name); + } + + return bf; +} diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h index 04cc4e5c031f..969060edc362 100644 --- a/tools/perf/ui/keysyms.h +++ b/tools/perf/ui/keysyms.h @@ -27,4 +27,6 @@ #define K_SWITCH_INPUT_DATA -4 #define K_RELOAD -5 +const char *key_name(int key, char *bf, size_t size); + #endif /* _PERF_KEYSYMS_H_ */ diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 7ac4b98e28bc..8c4c8925df2c 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -643,45 +643,58 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists, unsigned header_width = 0; struct perf_hpp_fmt *fmt; struct perf_hpp_list_node *fmt_node; + struct perf_hpp_list *hpp_list = hists->hpp_list; const char *sep = symbol_conf.field_sep; indent = hists->nr_hpp_node; - /* preserve max indent depth for column headers */ - print_hierarchy_indent(sep, indent, " ", fp); - /* the first hpp_list_node is for overhead columns */ fmt_node = list_first_entry(&hists->hpp_formats, struct perf_hpp_list_node, list); - perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { - fmt->header(fmt, hpp, hists, 0, NULL); - fprintf(fp, "%s%s", hpp->buf, sep ?: " "); - } + for (int line = 0; line < hpp_list->nr_header_lines; line++) { + /* first # is displayed one level up */ + if (line) + fprintf(fp, "# "); - /* combine sort headers with ' / ' */ - first_node = true; - list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { - if (!first_node) - header_width += fprintf(fp, " / "); - first_node = false; + /* preserve max indent depth for column headers */ + print_hierarchy_indent(sep, indent, " ", fp); - first_col = true; perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { - if (perf_hpp__should_skip(fmt, hists)) - continue; + fmt->header(fmt, hpp, hists, line, NULL); + fprintf(fp, "%s%s", hpp->buf, sep ?: " "); + } - if (!first_col) - header_width += fprintf(fp, "+"); - first_col = false; + if (line < hpp_list->nr_header_lines - 1) + goto next_line; + + /* combine sort headers with ' / ' */ + first_node = true; + list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { + if (!first_node) + header_width += fprintf(fp, " / "); + first_node = false; - fmt->header(fmt, hpp, hists, 0, NULL); + first_col = true; + perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { + if (perf_hpp__should_skip(fmt, hists)) + continue; - header_width += fprintf(fp, "%s", strim(hpp->buf)); + if (!first_col) + header_width += fprintf(fp, "+"); + first_col = false; + + fmt->header(fmt, hpp, hists, line, NULL); + + header_width += fprintf(fp, "%s", strim(hpp->buf)); + } } + +next_line: + fprintf(fp, "\n"); } - fprintf(fp, "\n# "); + fprintf(fp, "# "); /* preserve max indent depth for initial dots */ print_hierarchy_indent(sep, indent, dots, fp); |