diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 1d6092c460dd..73496320fca3 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt @@ -56,6 +56,9 @@ OPTIONS --all-user:: Configure all used events to run in user space. +--ldload:: + Specify desired latency for loads event. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 3a8a9ba2b041..947db6fe512c 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -109,6 +109,10 @@ OPTIONS Dry run. With this option, --add and --del doesn't execute actual adding and removal operations. +--cache:: + Cache the probes (with --add option). Any events which successfully added + are also stored in the cache file. + --max-probes=NUM:: Set the maximum number of probe points for an event. Default is 128. diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index f7645a42708e..7f628f9c2fb4 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -666,7 +666,8 @@ static void hists__process(struct hists *hists) hists__precompute(hists); hists__output_resort(hists, NULL); - hists__fprintf(hists, true, 0, 0, 0, stdout); + hists__fprintf(hists, true, 0, 0, 0, stdout, + symbol_conf.use_callchain); } static void data__fprintf(void) @@ -1044,7 +1045,7 @@ static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, } static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct perf_evsel *evsel __maybe_unused) + struct hists *hists __maybe_unused) { struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt); @@ -1055,7 +1056,7 @@ static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, static int hpp__width(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, - struct perf_evsel *evsel __maybe_unused) + struct hists *hists __maybe_unused) { struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt); diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 1dc140c5481d..d608a2c9e48c 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -67,6 +67,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) OPT_CALLBACK('e', "event", &mem, "event", "event selector. use 'perf mem record -e list' to list available events", parse_record_events), + OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"), diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 9af859b28b15..6d7ab4316449 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -512,6 +512,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "Enable symbol demangling"), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), + OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"), OPT_END() }; int ret; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index a87cb338bdf1..9f36b236f0f9 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -370,7 +370,8 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, continue; hists__fprintf_nr_sample_events(hists, rep, evname, stdout); - hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout); + hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout, + symbol_conf.use_callchain); fprintf(stdout, "\n\n"); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 2a6cc254ad0c..81dba80a42b5 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -295,7 +295,7 @@ static void perf_top__print_sym_table(struct perf_top *top) hists__output_recalc_col_len(hists, top->print_entries - printed); putchar('\n'); hists__fprintf(hists, false, top->print_entries - printed, win_width, - top->min_percent, stdout); + top->min_percent, stdout, symbol_conf.use_callchain); } static void prompt_integer(int *target, const char *msg) diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 098874b99981..80018feb99c0 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -365,6 +365,7 @@ ifndef NO_LIBUNWIND $(call detected,CONFIG_LIBUNWIND_X86) CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT LDFLAGS += -lunwind-x86 + EXTLIBS_LIBUNWIND += -lunwind-x86 have_libunwind = 1 endif @@ -372,6 +373,7 @@ ifndef NO_LIBUNWIND $(call detected,CONFIG_LIBUNWIND_AARCH64) CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT LDFLAGS += -lunwind-aarch64 + EXTLIBS_LIBUNWIND += -lunwind-aarch64 have_libunwind = 1 $(call feature_check,libunwind-debug-frame-aarch64) ifneq ($(feature-libunwind-debug-frame-aarch64), 1) @@ -449,6 +451,7 @@ ifndef NO_LIBUNWIND CFLAGS += -DHAVE_LIBUNWIND_SUPPORT CFLAGS += $(LIBUNWIND_CFLAGS) LDFLAGS += $(LIBUNWIND_LDFLAGS) + EXTLIBS += $(EXTLIBS_LIBUNWIND) endif ifndef NO_LIBAUDIT diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 538bae880bfe..b1b60544a545 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1470,7 +1470,7 @@ static int hist_browser__show_no_entry(struct hist_browser *browser, column++ < browser->b.horiz_scroll) continue; - ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists)); + ret = fmt->width(fmt, NULL, browser->hists); if (first) { /* for folded sign */ @@ -1531,7 +1531,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char * if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) continue; - ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); + ret = fmt->header(fmt, &dummy_hpp, hists); if (advance_hpp_check(&dummy_hpp, ret)) break; @@ -1568,7 +1568,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_to_evsel(hists)); + ret = fmt->header(fmt, &dummy_hpp, hists); if (advance_hpp_check(&dummy_hpp, ret)) break; @@ -1605,7 +1605,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows } first_col = false; - ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); + ret = fmt->header(fmt, &dummy_hpp, hists); dummy_hpp.buf[ret] = '\0'; start = trim(dummy_hpp.buf); @@ -1622,21 +1622,38 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows return ret; } -static void hist_browser__show_headers(struct hist_browser *browser) +static void hists_browser__hierarchy_headers(struct hist_browser *browser) { char headers[1024]; - if (symbol_conf.report_hierarchy) - hists_browser__scnprintf_hierarchy_headers(browser, headers, - sizeof(headers)); - else - hists_browser__scnprintf_headers(browser, headers, - sizeof(headers)); + hists_browser__scnprintf_hierarchy_headers(browser, headers, + sizeof(headers)); + ui_browser__gotorc(&browser->b, 0, 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) +{ + char headers[1024]; + + hists_browser__scnprintf_headers(browser, headers, + sizeof(headers)); + + ui_browser__gotorc(&browser->b, 0, 0); + ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); + ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); +} + +static void hist_browser__show_headers(struct hist_browser *browser) +{ + if (symbol_conf.report_hierarchy) + hists_browser__hierarchy_headers(browser); + else + hists_browser__headers(browser); +} + static void ui_browser__hists_init_top(struct ui_browser *browser) { if (browser->top == NULL) { diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 932adfaa05af..e5c1325b0340 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -549,7 +549,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists, strcat(buf, "+"); first_col = false; - fmt->header(fmt, &hpp, hists_to_evsel(hists)); + fmt->header(fmt, &hpp, hists); strcat(buf, ltrim(rtrim(hpp.buf))); } } diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index af07ffb129ca..6940745aa77c 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -215,9 +215,10 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, static int hpp__width_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, - struct perf_evsel *evsel) + struct hists *hists) { int len = fmt->user_len ?: fmt->len; + struct perf_evsel *evsel = hists_to_evsel(hists); if (symbol_conf.event_group) len = max(len, evsel->nr_members * fmt->len); @@ -229,9 +230,9 @@ 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 perf_evsel *evsel) + struct hists *hists) { - int len = hpp__width_fn(fmt, hpp, evsel); + int len = hpp__width_fn(fmt, hpp, hists); return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); } @@ -632,7 +633,7 @@ unsigned int hists__sort_list_width(struct hists *hists) else ret += 2; - ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); + ret += fmt->width(fmt, &dummy_hpp, hists); } if (verbose && hists__has(hists, sym)) /* Addr + origin */ @@ -657,7 +658,7 @@ unsigned int hists__overhead_width(struct hists *hists) else ret += 2; - ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); + ret += fmt->width(fmt, &dummy_hpp, hists); } return ret; diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 560eb47d56f9..f04a63112079 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -492,14 +492,15 @@ out: } static int hist_entry__fprintf(struct hist_entry *he, size_t size, - struct hists *hists, - char *bf, size_t bfsz, FILE *fp) + char *bf, size_t bfsz, FILE *fp, + bool use_callchain) { int ret; struct perf_hpp hpp = { .buf = bf, .size = size, }; + struct hists *hists = he->hists; u64 total_period = hists->stats.total_period; if (size == 0 || size > bfsz) @@ -512,7 +513,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, ret = fprintf(fp, "%s\n", bf); - if (symbol_conf.use_callchain) + if (use_callchain) ret += hist_entry_callchain__fprintf(he, total_period, 0, fp); return ret; @@ -548,7 +549,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, struct perf_hpp_list_node, list); perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { - fmt->header(fmt, hpp, hists_to_evsel(hists)); + fmt->header(fmt, hpp, hists); fprintf(fp, "%s%s", hpp->buf, sep ?: " "); } @@ -568,7 +569,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, header_width += fprintf(fp, "+"); first_col = false; - fmt->header(fmt, hpp, hists_to_evsel(hists)); + fmt->header(fmt, hpp, hists); header_width += fprintf(fp, "%s", trim(hpp->buf)); } @@ -589,7 +590,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, fprintf(fp, "%s", sep ?: ".."); first_col = false; - width = fmt->width(fmt, hpp, hists_to_evsel(hists)); + width = fmt->width(fmt, hpp, hists); fprintf(fp, "%.*s", width, dots); } @@ -606,7 +607,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, width++; /* for '+' sign between column header */ first_col = false; - width += fmt->width(fmt, hpp, hists_to_evsel(hists)); + width += fmt->width(fmt, hpp, hists); } if (width > header_width) @@ -622,47 +623,31 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, return 2; } -size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, - int max_cols, float min_pcnt, FILE *fp) +static int +hists__fprintf_hierarchy_headers(struct hists *hists, + struct perf_hpp *hpp, + FILE *fp) +{ + struct perf_hpp_list_node *fmt_node; + struct perf_hpp_fmt *fmt; + + list_for_each_entry(fmt_node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) + perf_hpp__reset_width(fmt, hists); + } + + return print_hierarchy_header(hists, hpp, symbol_conf.field_sep, fp); +} + +static int +hists__fprintf_standard_headers(struct hists *hists, + struct perf_hpp *hpp, + FILE *fp) { struct perf_hpp_fmt *fmt; - struct perf_hpp_list_node *fmt_node; - struct rb_node *nd; - size_t ret = 0; unsigned int width; const char *sep = symbol_conf.field_sep; - int nr_rows = 0; - char bf[96]; - struct perf_hpp dummy_hpp = { - .buf = bf, - .size = sizeof(bf), - }; bool first = true; - size_t linesz; - char *line = NULL; - unsigned indent; - - init_rem_hits(); - - hists__for_each_format(hists, fmt) - perf_hpp__reset_width(fmt, hists); - - if (symbol_conf.col_width_list_str) - perf_hpp__set_user_width(symbol_conf.col_width_list_str); - - if (!show_header) - goto print_entries; - - fprintf(fp, "# "); - - if (symbol_conf.report_hierarchy) { - list_for_each_entry(fmt_node, &hists->hpp_formats, list) { - perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) - perf_hpp__reset_width(fmt, hists); - } - nr_rows += print_hierarchy_header(hists, &dummy_hpp, sep, fp); - goto print_entries; - } hists__for_each_format(hists, fmt) { if (perf_hpp__should_skip(fmt, hists)) @@ -673,16 +658,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, else first = false; - fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); - fprintf(fp, "%s", bf); + fmt->header(fmt, hpp, hists); + fprintf(fp, "%s", hpp->buf); } fprintf(fp, "\n"); - if (max_rows && ++nr_rows >= max_rows) - goto out; if (sep) - goto print_entries; + return 1; first = true; @@ -699,20 +682,60 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, else first = false; - width = fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); + width = fmt->width(fmt, hpp, hists); for (i = 0; i < width; i++) fprintf(fp, "."); } fprintf(fp, "\n"); - if (max_rows && ++nr_rows >= max_rows) - goto out; - fprintf(fp, "#\n"); - if (max_rows && ++nr_rows >= max_rows) + return 3; +} + +static int hists__fprintf_headers(struct hists *hists, FILE *fp) +{ + char bf[96]; + struct perf_hpp dummy_hpp = { + .buf = bf, + .size = sizeof(bf), + }; + + fprintf(fp, "# "); + + if (symbol_conf.report_hierarchy) + return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp); + else + return hists__fprintf_standard_headers(hists, &dummy_hpp, fp); + +} + +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, + int max_cols, float min_pcnt, FILE *fp, + bool use_callchain) +{ + struct perf_hpp_fmt *fmt; + struct rb_node *nd; + size_t ret = 0; + const char *sep = symbol_conf.field_sep; + int nr_rows = 0; + size_t linesz; + char *line = NULL; + unsigned indent; + + init_rem_hits(); + + hists__for_each_format(hists, fmt) + perf_hpp__reset_width(fmt, hists); + + if (symbol_conf.col_width_list_str) + perf_hpp__set_user_width(symbol_conf.col_width_list_str); + + if (show_header) + nr_rows += hists__fprintf_headers(hists, fp); + + if (max_rows && nr_rows >= max_rows) goto out; -print_entries: linesz = hists__sort_list_width(hists) + 3 + 1; linesz += perf_hpp__color_overhead(); line = malloc(linesz); @@ -734,7 +757,7 @@ print_entries: if (percent < min_pcnt) continue; - ret += hist_entry__fprintf(h, max_cols, hists, line, linesz, fp); + ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, use_callchain); if (max_rows && ++nr_rows >= max_rows) break; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 20aef90bf194..62b147366d01 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -387,9 +387,8 @@ void disable_buildid_cache(void) no_buildid_cache = true; } -static char *build_id_cache__dirname_from_path(const char *name, - bool is_kallsyms, bool is_vdso, - const char *sbuild_id) +char *build_id_cache__cachedir(const char *sbuild_id, const char *name, + bool is_kallsyms, bool is_vdso) { char *realname = (char *)name, *filename; bool slash = is_kallsyms || is_vdso; @@ -417,8 +416,7 @@ int build_id_cache__list_build_ids(const char *pathname, char *dir_name; int ret = 0; - dir_name = build_id_cache__dirname_from_path(pathname, false, false, - NULL); + dir_name = build_id_cache__cachedir(NULL, pathname, false, false); if (!dir_name) return -ENOMEM; @@ -444,8 +442,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, goto out_free; } - dir_name = build_id_cache__dirname_from_path(name, is_kallsyms, - is_vdso, sbuild_id); + dir_name = build_id_cache__cachedir(sbuild_id, name, + is_kallsyms, is_vdso); if (!dir_name) goto out_free; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index e5435f46e48e..d8c7f2fc6a87 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -30,6 +30,8 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); int perf_session__write_buildid_table(struct perf_session *session, int fd); int perf_session__cache_build_ids(struct perf_session *session); +char *build_id_cache__cachedir(const char *sbuild_id, const char *name, + bool is_kallsyms, bool is_vdso); int build_id_cache__list_build_ids(const char *pathname, struct strlist **result); bool build_id_cache__cached(const char *sbuild_id); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 8749eca3055f..31e09a4e8862 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -742,6 +742,9 @@ static void perf_config_set__purge(struct perf_config_set *set) void perf_config_set__delete(struct perf_config_set *set) { + if (set == NULL) + return; + perf_config_set__purge(set); free(set); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d1f19e0012d4..2515cfdb7365 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1081,7 +1081,7 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, struct perf_hpp_fmt *fmt, int printed) { if (!list_is_last(&fmt->list, &he->hists->hpp_list->fields)) { - const int width = fmt->width(fmt, hpp, hists_to_evsel(he->hists)); + const int width = fmt->width(fmt, hpp, he->hists); if (printed < width) { advance_hpp(hpp, printed); printed = scnprintf(hpp->buf, hpp->size, "%-*s", width - printed, " "); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 7b54ccf1b737..a19112872ff9 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -159,7 +159,8 @@ void events_stats__inc(struct events_stats *stats, u32 type); size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, - int max_cols, float min_pcnt, FILE *fp); + int max_cols, float min_pcnt, FILE *fp, + bool use_callchain); size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp); void hists__filter_by_dso(struct hists *hists); @@ -214,9 +215,9 @@ struct perf_hpp { struct perf_hpp_fmt { const char *name; int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct perf_evsel *evsel); + struct hists *hists); int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct perf_evsel *evsel); + struct hists *hists); int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he); int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index 75465f89a413..bbc368e7d1e4 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -10,18 +10,33 @@ #include "debug.h" #include "symbol.h" +unsigned int perf_mem_events__loads_ldlat = 30; + #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s } struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = { - E("ldlat-loads", "cpu/mem-loads,ldlat=30/P", "mem-loads"), + E("ldlat-loads", "cpu/mem-loads,ldlat=%u/P", "mem-loads"), E("ldlat-stores", "cpu/mem-stores/P", "mem-stores"), }; #undef E #undef E +static char mem_loads_name[100]; +static bool mem_loads_name__init; + char *perf_mem_events__name(int i) { + if (i == PERF_MEM_EVENTS__LOAD) { + if (!mem_loads_name__init) { + mem_loads_name__init = true; + scnprintf(mem_loads_name, sizeof(mem_loads_name), + perf_mem_events[i].name, + perf_mem_events__loads_ldlat); + } + return mem_loads_name; + } + return (char *)perf_mem_events[i].name; } diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index 5d6d93066a6e..7f69bf9d789d 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -18,6 +18,7 @@ enum { }; extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX]; +extern unsigned int perf_mem_events__loads_ldlat; int perf_mem_events__parse(const char *str); int perf_mem_events__init(void); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 74401a20106d..084756c17309 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -67,7 +67,6 @@ int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } -static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static struct machine *host_machine; /* Initialize symbol maps and path of vmlinux/modules */ @@ -1603,6 +1602,10 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev) p = strchr(argv[1], ':'); if (p) { tp->module = strndup(argv[1], p - argv[1]); + if (!tp->module) { + ret = -ENOMEM; + goto out; + } p++; } else p = argv[1]; @@ -1712,7 +1715,7 @@ out: } /* Compose only probe point (not argument) */ -static char *synthesize_perf_probe_point(struct perf_probe_point *pp) +char *synthesize_perf_probe_point(struct perf_probe_point *pp) { struct strbuf buf; char *tmp, *ret = NULL; @@ -1751,30 +1754,36 @@ out: return ret; } -#if 0 char *synthesize_perf_probe_command(struct perf_probe_event *pev) { - char *buf; - int i, len, ret; + struct strbuf buf; + char *tmp, *ret = NULL; + int i; - buf = synthesize_perf_probe_point(&pev->point); - if (!buf) + if (strbuf_init(&buf, 64)) return NULL; + if (pev->event) + if (strbuf_addf(&buf, "%s:%s=", pev->group ?: PERFPROBE_GROUP, + pev->event) < 0) + goto out; + + tmp = synthesize_perf_probe_point(&pev->point); + if (!tmp || strbuf_addstr(&buf, tmp) < 0) + goto out; + free(tmp); - len = strlen(buf); for (i = 0; i < pev->nargs; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pev->args[i].name); - if (ret <= 0) { - free(buf); - return NULL; - } - len += ret; + tmp = synthesize_perf_probe_arg(pev->args + i); + if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0) + goto out; + free(tmp); } - return buf; + ret = strbuf_detach(&buf, NULL); +out: + strbuf_release(&buf); + return ret; } -#endif static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, struct strbuf *buf, int depth) @@ -2026,6 +2035,79 @@ void clear_perf_probe_event(struct perf_probe_event *pev) memset(pev, 0, sizeof(*pev)); } +#define strdup_or_goto(str, label) \ +({ char *__p = NULL; if (str && !(__p = strdup(str))) goto label; __p; }) + +static int perf_probe_point__copy(struct perf_probe_point *dst, + struct perf_probe_point *src) +{ + dst->file = strdup_or_goto(src->file, out_err); + dst->function = strdup_or_goto(src->function, out_err); + dst->lazy_line = strdup_or_goto(src->lazy_line, out_err); + dst->line = src->line; + dst->retprobe = src->retprobe; + dst->offset = src->offset; + return 0; + +out_err: + clear_perf_probe_point(dst); + return -ENOMEM; +} + +static int perf_probe_arg__copy(struct perf_probe_arg *dst, + struct perf_probe_arg *src) +{ + struct perf_probe_arg_field *field, **ppfield; + + dst->name = strdup_or_goto(src->name, out_err); + dst->var = strdup_or_goto(src->var, out_err); + dst->type = strdup_or_goto(src->type, out_err); + + field = src->field; + ppfield = &(dst->field); + while (field) { + *ppfield = zalloc(sizeof(*field)); + if (!*ppfield) + goto out_err; + (*ppfield)->name = strdup_or_goto(field->name, out_err); + (*ppfield)->index = field->index; + (*ppfield)->ref = field->ref; + field = field->next; + ppfield = &((*ppfield)->next); + } + return 0; +out_err: + return -ENOMEM; +} + +int perf_probe_event__copy(struct perf_probe_event *dst, + struct perf_probe_event *src) +{ + int i; + + dst->event = strdup_or_goto(src->event, out_err); + dst->group = strdup_or_goto(src->group, out_err); + dst->target = strdup_or_goto(src->target, out_err); + dst->uprobes = src->uprobes; + + if (perf_probe_point__copy(&dst->point, &src->point) < 0) + goto out_err; + + dst->args = zalloc(sizeof(struct perf_probe_arg) * src->nargs); + if (!dst->args) + goto out_err; + dst->nargs = src->nargs; + + for (i = 0; i < src->nargs; i++) + if (perf_probe_arg__copy(&dst->args[i], &src->args[i]) < 0) + goto out_err; + return 0; + +out_err: + clear_perf_probe_event(dst); + return -ENOMEM; +} + void clear_probe_trace_event(struct probe_trace_event *tev) { struct probe_trace_arg_ref *ref, *next; @@ -2432,6 +2514,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, { int i, fd, ret; struct probe_trace_event *tev = NULL; + struct probe_cache *cache = NULL; struct strlist *namelist; fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0)); @@ -2473,6 +2556,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, } if (ret == -EINVAL && pev->uprobes) warn_uprobe_event_compat(tev); + if (ret == 0 && probe_conf.cache) { + cache = probe_cache__new(pev->target); + if (!cache || + probe_cache__add_entry(cache, pev, tevs, ntevs) < 0 || + probe_cache__commit(cache) < 0) + pr_warning("Failed to add event to probe cache\n"); + probe_cache__delete(cache); + } strlist__delete(namelist); close_out: @@ -2501,9 +2592,6 @@ static int find_probe_functions(struct map *map, char *name, return found; } -#define strdup_or_goto(str, label) \ - ({ char *__p = strdup(str); if (!__p) goto label; __p; }) - void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, struct probe_trace_event *tev __maybe_unused, struct map *map __maybe_unused, diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5a27eb4fad05..432b690d3f17 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -12,6 +12,7 @@ struct probe_conf { bool show_location_range; bool force_add; bool no_inlines; + bool cache; int max_probes; }; extern struct probe_conf probe_conf; @@ -121,6 +122,10 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev); char *synthesize_perf_probe_command(struct perf_probe_event *pev); char *synthesize_probe_trace_command(struct probe_trace_event *tev); char *synthesize_perf_probe_arg(struct perf_probe_arg *pa); +char *synthesize_perf_probe_point(struct perf_probe_point *pp); + +int perf_probe_event__copy(struct perf_probe_event *dst, + struct perf_probe_event *src); /* Check the perf_probe_event needs debuginfo */ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 3fe6214970e6..25a40427003e 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. * */ +#include #include "util.h" #include "event.h" #include "strlist.h" @@ -324,3 +325,333 @@ int probe_file__del_events(int fd, struct strfilter *filter) return ret; } + +/* Caller must ensure to remove this entry from list */ +static void probe_cache_entry__delete(struct probe_cache_entry *entry) +{ + if (entry) { + BUG_ON(!list_empty(&entry->node)); + + strlist__delete(entry->tevlist); + clear_perf_probe_event(&entry->pev); + zfree(&entry->spev); + free(entry); + } +} + +static struct probe_cache_entry * +probe_cache_entry__new(struct perf_probe_event *pev) +{ + struct probe_cache_entry *entry = zalloc(sizeof(*entry)); + + if (entry) { + INIT_LIST_HEAD(&entry->node); + entry->tevlist = strlist__new(NULL, NULL); + if (!entry->tevlist) + zfree(&entry); + else if (pev) { + entry->spev = synthesize_perf_probe_command(pev); + if (!entry->spev || + perf_probe_event__copy(&entry->pev, pev) < 0) { + probe_cache_entry__delete(entry); + return NULL; + } + } + } + + return entry; +} + +/* For the kernel probe caches, pass target = NULL */ +static int probe_cache__open(struct probe_cache *pcache, const char *target) +{ + char cpath[PATH_MAX]; + char sbuildid[SBUILD_ID_SIZE]; + char *dir_name; + bool is_kallsyms = !target; + int ret, fd; + + if (target) + ret = filename__sprintf_build_id(target, sbuildid); + else { + target = DSO__NAME_KALLSYMS; + ret = sysfs__sprintf_build_id("/", sbuildid); + } + if (ret < 0) { + pr_debug("Failed to get build-id from %s.\n", target); + return ret; + } + + /* If we have no buildid cache, make it */ + if (!build_id_cache__cached(sbuildid)) { + ret = build_id_cache__add_s(sbuildid, target, + is_kallsyms, NULL); + if (ret < 0) { + pr_debug("Failed to add build-id cache: %s\n", target); + return ret; + } + } + + dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms, + false); + if (!dir_name) + return -ENOMEM; + + snprintf(cpath, PATH_MAX, "%s/probes", dir_name); + fd = open(cpath, O_CREAT | O_RDWR, 0644); + if (fd < 0) + pr_debug("Failed to open cache(%d): %s\n", fd, cpath); + free(dir_name); + pcache->fd = fd; + + return fd; +} + +static int probe_cache__load(struct probe_cache *pcache) +{ + struct probe_cache_entry *entry = NULL; + char buf[MAX_CMDLEN], *p; + int ret = 0; + FILE *fp; + + fp = fdopen(dup(pcache->fd), "r"); + if (!fp) + return -EINVAL; + + while (!feof(fp)) { + if (!fgets(buf, MAX_CMDLEN, fp)) + break; + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + if (buf[0] == '#') { /* #perf_probe_event */ + entry = probe_cache_entry__new(NULL); + if (!entry) { + ret = -ENOMEM; + goto out; + } + entry->spev = strdup(buf + 1); + if (entry->spev) + ret = parse_perf_probe_command(buf + 1, + &entry->pev); + else + ret = -ENOMEM; + if (ret < 0) { + probe_cache_entry__delete(entry); + goto out; + } + list_add_tail(&entry->node, &pcache->entries); + } else { /* trace_probe_event */ + if (!entry) { + ret = -EINVAL; + goto out; + } + strlist__add(entry->tevlist, buf); + } + } +out: + fclose(fp); + return ret; +} + +static struct probe_cache *probe_cache__alloc(void) +{ + struct probe_cache *pcache = zalloc(sizeof(*pcache)); + + if (pcache) { + INIT_LIST_HEAD(&pcache->entries); + pcache->fd = -EINVAL; + } + return pcache; +} + +void probe_cache__purge(struct probe_cache *pcache) +{ + struct probe_cache_entry *entry, *n; + + list_for_each_entry_safe(entry, n, &pcache->entries, node) { + list_del_init(&entry->node); + probe_cache_entry__delete(entry); + } +} + +void probe_cache__delete(struct probe_cache *pcache) +{ + if (!pcache) + return; + + probe_cache__purge(pcache); + if (pcache->fd > 0) + close(pcache->fd); + free(pcache); +} + +struct probe_cache *probe_cache__new(const char *target) +{ + struct probe_cache *pcache = probe_cache__alloc(); + int ret; + + if (!pcache) + return NULL; + + ret = probe_cache__open(pcache, target); + if (ret < 0) { + pr_debug("Cache open error: %d\n", ret); + goto out_err; + } + + ret = probe_cache__load(pcache); + if (ret < 0) { + pr_debug("Cache read error: %d\n", ret); + goto out_err; + } + + return pcache; + +out_err: + probe_cache__delete(pcache); + return NULL; +} + +static bool streql(const char *a, const char *b) +{ + if (a == b) + return true; + + if (!a || !b) + return false; + + return !strcmp(a, b); +} + +static struct probe_cache_entry * +probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev) +{ + struct probe_cache_entry *entry = NULL; + char *cmd = synthesize_perf_probe_command(pev); + + if (!cmd) + return NULL; + + list_for_each_entry(entry, &pcache->entries, node) { + /* Hit if same event name or same command-string */ + if ((pev->event && + (streql(entry->pev.group, pev->group) && + streql(entry->pev.event, pev->event))) || + (!strcmp(entry->spev, cmd))) + goto found; + } + entry = NULL; + +found: + free(cmd); + return entry; +} + +int probe_cache__add_entry(struct probe_cache *pcache, + struct perf_probe_event *pev, + struct probe_trace_event *tevs, int ntevs) +{ + struct probe_cache_entry *entry = NULL; + char *command; + int i, ret = 0; + + if (!pcache || !pev || !tevs || ntevs <= 0) { + ret = -EINVAL; + goto out_err; + } + + /* Remove old cache entry */ + entry = probe_cache__find(pcache, pev); + if (entry) { + list_del_init(&entry->node); + probe_cache_entry__delete(entry); + } + + ret = -ENOMEM; + entry = probe_cache_entry__new(pev); + if (!entry) + goto out_err; + + for (i = 0; i < ntevs; i++) { + if (!tevs[i].point.symbol) + continue; + + command = synthesize_probe_trace_command(&tevs[i]); + if (!command) + goto out_err; + strlist__add(entry->tevlist, command); + free(command); + } + list_add_tail(&entry->node, &pcache->entries); + pr_debug("Added probe cache: %d\n", ntevs); + return 0; + +out_err: + pr_debug("Failed to add probe caches\n"); + probe_cache_entry__delete(entry); + return ret; +} + +static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) +{ + struct str_node *snode; + struct stat st; + struct iovec iov[3]; + int ret; + /* Save stat for rollback */ + ret = fstat(fd, &st); + if (ret < 0) + return ret; + + pr_debug("Writing cache: #%s\n", entry->spev); + iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1; + iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev); + iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1; + ret = writev(fd, iov, 3); + if (ret < (int)iov[1].iov_len + 2) + goto rollback; + + strlist__for_each(snode, entry->tevlist) { + iov[0].iov_base = (void *)snode->s; + iov[0].iov_len = strlen(snode->s); + iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1; + ret = writev(fd, iov, 2); + if (ret < (int)iov[0].iov_len + 1) + goto rollback; + } + return 0; + +rollback: + /* Rollback to avoid cache file corruption */ + if (ret > 0) + ret = -1; + if (ftruncate(fd, st.st_size) < 0) + ret = -2; + + return ret; +} + +int probe_cache__commit(struct probe_cache *pcache) +{ + struct probe_cache_entry *entry; + int ret = 0; + + /* TBD: if we do not update existing entries, skip it */ + ret = lseek(pcache->fd, 0, SEEK_SET); + if (ret < 0) + goto out; + + ret = ftruncate(pcache->fd, 0); + if (ret < 0) + goto out; + + list_for_each_entry(entry, &pcache->entries, node) { + ret = probe_cache_entry__write(entry, pcache->fd); + pr_debug("Cache committed: %d\n", ret); + if (ret < 0) + break; + } +out: + return ret; +} diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 18ac9cf51c34..d872e3df7e59 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h @@ -5,6 +5,19 @@ #include "strfilter.h" #include "probe-event.h" +/* Cache of probe definitions */ +struct probe_cache_entry { + struct list_head node; + struct perf_probe_event pev; + char *spev; + struct strlist *tevlist; +}; + +struct probe_cache { + int fd; + struct list_head entries; +}; + #define PF_FL_UPROBE 1 #define PF_FL_RW 2 @@ -18,5 +31,12 @@ int probe_file__get_events(int fd, struct strfilter *filter, struct strlist *plist); int probe_file__del_strlist(int fd, struct strlist *namelist); +struct probe_cache *probe_cache__new(const char *target); +int probe_cache__add_entry(struct probe_cache *pcache, + struct perf_probe_event *pev, + struct probe_trace_event *tevs, int ntevs); +int probe_cache__commit(struct probe_cache *pcache); +void probe_cache__purge(struct probe_cache *pcache); +void probe_cache__delete(struct probe_cache *pcache); #endif diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c4e9bd70723c..896d34ebcc1e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1218,7 +1218,7 @@ struct sort_entry sort_mem_daddr_dso = { .se_header = "Data Object", .se_cmp = sort__dso_daddr_cmp, .se_snprintf = hist_entry__dso_daddr_snprintf, - .se_width_idx = HISTC_MEM_DADDR_SYMBOL, + .se_width_idx = HISTC_MEM_DADDR_DSO, }; struct sort_entry sort_mem_locked = { @@ -1488,7 +1488,7 @@ void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) } static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct perf_evsel *evsel) + struct hists *hists) { struct hpp_sort_entry *hse; size_t len = fmt->user_len; @@ -1496,14 +1496,14 @@ static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, hse = container_of(fmt, struct hpp_sort_entry, hpp); if (!len) - len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); + len = hists__col_len(hists, hse->se->se_width_idx); return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); } static int __sort__hpp_width(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, - struct perf_evsel *evsel) + struct hists *hists) { struct hpp_sort_entry *hse; size_t len = fmt->user_len; @@ -1511,7 +1511,7 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, hse = container_of(fmt, struct hpp_sort_entry, hpp); if (!len) - len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); + len = hists__col_len(hists, hse->se->se_width_idx); return len; } @@ -1793,7 +1793,7 @@ static void update_dynamic_len(struct hpp_dynamic_entry *hde, } static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct perf_evsel *evsel __maybe_unused) + struct hists *hists __maybe_unused) { struct hpp_dynamic_entry *hde; size_t len = fmt->user_len; @@ -1808,7 +1808,7 @@ static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, static int __sort__hde_width(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, - struct perf_evsel *evsel __maybe_unused) + struct hists *hists __maybe_unused) { struct hpp_dynamic_entry *hde; size_t len = fmt->user_len; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 23504ad5d6dd..e08b9a092a23 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -97,20 +97,17 @@ int rm_rf(char *path) scnprintf(namebuf, sizeof(namebuf), "%s/%s", path, d->d_name); - ret = stat(namebuf, &statbuf); + /* We have to check symbolic link itself */ + ret = lstat(namebuf, &statbuf); if (ret < 0) { pr_debug("stat failed: %s\n", namebuf); break; } - if (S_ISREG(statbuf.st_mode)) - ret = unlink(namebuf); - else if (S_ISDIR(statbuf.st_mode)) + if (S_ISDIR(statbuf.st_mode)) ret = rm_rf(namebuf); - else { - pr_debug("unknown file: %s\n", namebuf); - ret = -1; - } + else + ret = unlink(namebuf); } closedir(dir);