perf/core improvements and fixes:
User visible: - Add --ldlat option to 'perf mem' to specify load latency for loads event (e.g. cpu/mem-loads/ ) (Jiri Olsa) Build fixes: - Fix libunwind related compile error for static cross build (He Kuang) Infrastructure: - UI refactorings to support headers with multiple lines, non-evsel hists browsers, toggle showing callchains, etc (Jiri Olsa) - More prep work for caching probe definitions, paving the way for supporting SDT (Statically Defined Traces) userspace probes (Masami Hiramatsu) - Handle NULL at perf_config_set__delete() (Taeung Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXYZZJAAoJENZQFvNTUqpAJ3kP/27mhu32CYAWsevtXy2fGb8J UfcCH8/qRcU6qk6qGYWZfbJj+bx59qutGo9n6nUsTMhrptdxsQNL//qZxc49xjIM NwdDJn7M/Oq+8NKblf33z293EQh2rG9XMw0gtMZ6Buoptf3UW3BIVuo7Gix3xJEg JnaLXeofkcR+vmUxeRuAv9cm5n2FMTOc0FUzyL51GYL2/7hhKFhAyEjImEnI3ogY DqgibZrOFnc6q2HHMQSBiDSCthQj+NcLSLAqPK97D6FzxV5sIKBiLov9pQKsXkwH cF215v7gOhx9yhye9OcHBxjK7FAGAG4hAQzGOYzI5qA/FNfBQo1AaKdr5KwPyvqm Xr1/R74/ggkOJhae2FEs7KqolySfmM6ZJiRRFqT09rTf2F1L28CfliRIGxme7zF1 XCZMu1cxSP2z/s8T+yN9O9UUBA7PyeXP6AggOgE/0ScxQ2sI12b32pEfKMVsOoh4 osNdmzRKeEEsiL+L5NtQn9PybJNUedZTYf8QML//UEZkX7T+o+qJZiNfjq1SHuHp lfqf4JNo5a017CGVCx24mba5EI97dcy5sWdiEc3TmQdeP1yV9uo8H8B3TvSbz7Pm maUToYeemaKtRGyH6orbHm9N+lJ7Z4+YJoabPZNwxOYciFowuw8fJU0tvOKLj6SB GqgBRe/EJl1HwrdS3lHm =btFF -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20160615' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Add --ldlat option to 'perf mem' to specify load latency for loads event (e.g. cpu/mem-loads/ ) (Jiri Olsa) Build fixes: - Fix libunwind related compile error for static cross build (He Kuang) Infrastructure changes: - UI refactorings to support headers with multiple lines, non-evsel hists browsers, toggle showing callchains, etc (Jiri Olsa) - More prep work for caching probe definitions, paving the way for supporting SDT (Statically Defined Traces) userspace probes (Masami Hiramatsu) - Handle NULL at perf_config_set__delete() (Taeung Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
02469a9509
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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, " ");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue