forked from Minki/linux
perf/core improvements and fixes:
. Add a 'link' method for hists, so that we can have the leader with buckets for all the entries in all the hists. This new method is now used in the default 'diff' output, making the sum of the 'baseline' column be 100%, eliminating blind spots. Now we need to use this for 'diff' with > 2 perf.data files and for multi event 'report' and 'annotate'. . libtraceevent fixes for compiler warnings trying to make perf it build on some distros, like fedora 14, 32-bit, some of the warnings really pointed to real bugs. . Remove temp dir on failure in 'perf test', fix from Jiri Olsa. . Fixes for handling data, stack mmaps, from Namhyung Kim. . Fix live annotation bug related to recent objdump lookup patches, from Namhyung Kim . Don't try to follow jump target on PLT symbols in the annotation browser, fix from Namhyung Kim. . Fix leak on hist_entry delete, from Namhyung Kim. . Fix a CPU_ALLOC related build error on builtin-test, from Zheng Liu. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQnXaoAAoJENZQFvNTUqpAl74QAJu3fmprfixK8V93gEKhcHXz 4VYD1AmKnitS0XDXrJLlt4DBIVgQX+Ghkz8Ojh4N2vX3CMbkXwB2+frh25nxttGv 815I5u/lSFdE37QtQLFjf96feVJf9DlF3bzKXV38fu9tjbUVSuIf9DcQyj8g5M/R 2RNT/CytFfO+SoQuMPS0gLiHQFTRSRhYZaZV3yB1oakF482gGllP5lQ98uJKsi+v HNm03o9u9GCItepHvs1biOwphmHc1Y6xIIQZXmmmKZ4X1aze971t8y3S/jH5dHoT ulUNItoOxMR4/OqwQY1XN4Q074XrpqfYfv+DX9uYZlQGdMgGwluiFbdRgOcht8Ks okdTqLDQVT6l3FHvJ9Hy1ThUe4Edr0EuPxaOdmaFB+KYV+1LUp+C4CIXN6ZXFSQ4 /H5c7SK7khnOaY4VNQ7+OXOOLKMsOtNfnUQLdYNt41jDHAPpGeDS2WctA+9efwbT N7OQw/CwD0DGD4uf/dhU9l9w+ECTPbFOBYXyUzvLJyu+Uvtrs67O7aIBY2yWKhcG 0FMP4WZeGQbZX1t6eVc4rqsOTIycQ0v/LC63bWQjZm072yYwXt53SaletrbJ83ff OAwpTKJRvFt4C8AdUnv0WOcfoCqVjevalPNydfPWhFnD7B1tO2QA+XilguuC+ON/ kbKwW4J6BoBt2IT+sLrv =8KWI -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' 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: * Don't show scripts menu for 'perf top', fix from Feng Tang * Add framework for automated perf_event_attr tests, where tools with different command line options will be run from a 'perf test', via python glue, and the perf syscall will be intercepted to verify that the perf_event_attr fields set by the tool are those expected, from Jiri Olsa * Use normalized arch name for searching objdump path. This fixes cases where the system's objdump (e.g. x86_64) supports the architecture in the perf.data file (e.g. i686), but is not the same, fix from Namhyung Kim. * Postpone objdump check until annotation requested, from Namhyung Kim. * Add a 'link' method for hists, so that we can have the leader with buckets for all the entries in all the hists. This new method is now used in the default 'diff' output, making the sum of the 'baseline' column be 100%, eliminating blind spots. Now we need to use this for 'diff' with > 2 perf.data files and for multi event 'report' and 'annotate'. * libtraceevent fixes for compiler warnings trying to make perf it build on some distros, like fedora 14, 32-bit, some of the warnings really pointed to real bugs. * Remove temp dir on failure in 'perf test', fix from Jiri Olsa. * Fixes for handling data, stack mmaps, from Namhyung Kim. * Fix live annotation bug related to recent objdump lookup patches, from Namhyung Kim * Don't try to follow jump target on PLT symbols in the annotation browser, fix from Namhyung Kim. * Fix leak on hist_entry delete, from Namhyung Kim. * Fix a CPU_ALLOC related build error on builtin-test, from Zheng Liu. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
ccf59d8da1
@ -174,7 +174,7 @@ static int cmdline_init(struct pevent *pevent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *find_cmdline(struct pevent *pevent, int pid)
|
||||
static const char *find_cmdline(struct pevent *pevent, int pid)
|
||||
{
|
||||
const struct cmdline *comm;
|
||||
struct cmdline key;
|
||||
@ -2637,7 +2637,7 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
|
||||
struct print_arg *farg;
|
||||
enum event_type type;
|
||||
char *token;
|
||||
char *test;
|
||||
const char *test;
|
||||
int i;
|
||||
|
||||
arg->type = PRINT_FUNC;
|
||||
@ -3889,7 +3889,7 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
|
||||
struct event_format *event, struct print_arg *arg)
|
||||
{
|
||||
unsigned char *buf;
|
||||
char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
|
||||
const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
|
||||
|
||||
if (arg->type == PRINT_FUNC) {
|
||||
process_defined_func(s, data, size, event, arg);
|
||||
@ -3931,7 +3931,8 @@ static int is_printable_array(char *p, unsigned int len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_event_fields(struct trace_seq *s, void *data, int size,
|
||||
static void print_event_fields(struct trace_seq *s, void *data,
|
||||
int size __maybe_unused,
|
||||
struct event_format *event)
|
||||
{
|
||||
struct format_field *field;
|
||||
@ -4408,7 +4409,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
struct pevent_record *record)
|
||||
{
|
||||
static char *spaces = " "; /* 20 spaces */
|
||||
static const char *spaces = " "; /* 20 spaces */
|
||||
struct event_format *event;
|
||||
unsigned long secs;
|
||||
unsigned long usecs;
|
||||
@ -5070,8 +5071,8 @@ static const char * const pevent_error_str[] = {
|
||||
};
|
||||
#undef _PE
|
||||
|
||||
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
|
||||
char *buf, size_t buflen)
|
||||
int pevent_strerror(struct pevent *pevent __maybe_unused,
|
||||
enum pevent_errno errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int idx;
|
||||
const char *msg;
|
||||
@ -5100,6 +5101,7 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
|
||||
case PEVENT_ERRNO__READ_FORMAT_FAILED:
|
||||
case PEVENT_ERRNO__READ_PRINT_FAILED:
|
||||
case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED:
|
||||
case PEVENT_ERRNO__INVALID_ARG_TYPE:
|
||||
snprintf(buf, buflen, "%s", msg);
|
||||
break;
|
||||
|
||||
@ -5362,7 +5364,7 @@ int pevent_register_print_function(struct pevent *pevent,
|
||||
if (type == PEVENT_FUNC_ARG_VOID)
|
||||
break;
|
||||
|
||||
if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) {
|
||||
if (type >= PEVENT_FUNC_ARG_MAX_TYPES) {
|
||||
do_warning("Invalid argument type %d", type);
|
||||
ret = PEVENT_ERRNO__INVALID_ARG_TYPE;
|
||||
goto out_free;
|
||||
@ -5560,7 +5562,7 @@ void pevent_free(struct pevent *pevent)
|
||||
}
|
||||
|
||||
if (pevent->func_map) {
|
||||
for (i = 0; i < pevent->func_count; i++) {
|
||||
for (i = 0; i < (int)pevent->func_count; i++) {
|
||||
free(pevent->func_map[i].func);
|
||||
free(pevent->func_map[i].mod);
|
||||
}
|
||||
@ -5582,7 +5584,7 @@ void pevent_free(struct pevent *pevent)
|
||||
}
|
||||
|
||||
if (pevent->printk_map) {
|
||||
for (i = 0; i < pevent->printk_count; i++)
|
||||
for (i = 0; i < (int)pevent->printk_count; i++)
|
||||
free(pevent->printk_map[i].printk);
|
||||
free(pevent->printk_map);
|
||||
}
|
||||
|
@ -48,7 +48,10 @@ For x86:
|
||||
II. Compile perf for Android
|
||||
------------------------------------------------
|
||||
You need to run make with the NDK toolchain and sysroot defined above:
|
||||
make CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
For arm:
|
||||
make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
For x86:
|
||||
make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
|
||||
III. Install perf
|
||||
-----------------------------------------------
|
||||
|
@ -169,7 +169,7 @@ endif
|
||||
|
||||
### --- END CONFIGURATION SECTION ---
|
||||
|
||||
BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -Iutil -I. -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y)
|
||||
@ -371,7 +371,6 @@ LIB_OBJS += $(OUTPUT)util/help.o
|
||||
LIB_OBJS += $(OUTPUT)util/levenshtein.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-options.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-test.o
|
||||
LIB_OBJS += $(OUTPUT)util/path.o
|
||||
LIB_OBJS += $(OUTPUT)util/rbtree.o
|
||||
LIB_OBJS += $(OUTPUT)util/bitmap.o
|
||||
@ -389,7 +388,6 @@ LIB_OBJS += $(OUTPUT)util/sigchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-elf.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso-test-data.o
|
||||
LIB_OBJS += $(OUTPUT)util/color.o
|
||||
LIB_OBJS += $(OUTPUT)util/pager.o
|
||||
LIB_OBJS += $(OUTPUT)util/header.o
|
||||
@ -430,6 +428,10 @@ LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)arch/common.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)tests/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)tests/dso-data.o
|
||||
LIB_OBJS += $(OUTPUT)tests/attr.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
# Benchmark modules
|
||||
@ -459,8 +461,8 @@ BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
|
||||
|
||||
@ -490,6 +492,8 @@ ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y)
|
||||
LIBC_SUPPORT := 1
|
||||
endif
|
||||
ifeq ($(LIBC_SUPPORT),1)
|
||||
msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev);
|
||||
|
||||
NO_LIBELF := 1
|
||||
NO_DWARF := 1
|
||||
NO_DEMANGLE := 1
|
||||
@ -497,7 +501,14 @@ ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y)
|
||||
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
|
||||
endif
|
||||
else
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
# for linking with debug library, run like:
|
||||
# make DEBUG=1 LIBDW_DIR=/opt/libdw/
|
||||
ifdef LIBDW_DIR
|
||||
LIBDW_CFLAGS := -I$(LIBDW_DIR)/include
|
||||
LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib
|
||||
endif
|
||||
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y)
|
||||
msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
|
||||
NO_DWARF := 1
|
||||
@ -552,7 +563,8 @@ ifndef NO_DWARF
|
||||
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
|
||||
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
|
||||
else
|
||||
BASIC_CFLAGS += -DDWARF_SUPPORT
|
||||
BASIC_CFLAGS := -DDWARF_SUPPORT $(LIBDW_CFLAGS) $(BASIC_CFLAGS)
|
||||
BASIC_LDFLAGS := $(LIBDW_LDFLAGS) $(BASIC_LDFLAGS)
|
||||
EXTLIBS += -lelf -ldw
|
||||
LIB_OBJS += $(OUTPUT)util/probe-finder.o
|
||||
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
|
||||
@ -891,10 +903,14 @@ $(OUTPUT)%.s: %.S
|
||||
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
|
||||
'-DBINDIR="$(bindir_relative_SQ)"' \
|
||||
'-DPREFIX="$(prefix_SQ)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DBINDIR="$(bindir_SQ)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
@ -1059,6 +1075,10 @@ install: all try-install-man
|
||||
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'
|
||||
$(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'
|
||||
$(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
|
||||
install-python_ext:
|
||||
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
|
||||
|
@ -93,16 +93,46 @@ static int lookup_triplets(const char *const *triplets, const char *name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return architecture name in a normalized form.
|
||||
* The conversion logic comes from the Makefile.
|
||||
*/
|
||||
static const char *normalize_arch(char *arch)
|
||||
{
|
||||
if (!strcmp(arch, "x86_64"))
|
||||
return "x86";
|
||||
if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6')
|
||||
return "x86";
|
||||
if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5))
|
||||
return "sparc";
|
||||
if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110"))
|
||||
return "arm";
|
||||
if (!strncmp(arch, "s390", 4))
|
||||
return "s390";
|
||||
if (!strncmp(arch, "parisc", 6))
|
||||
return "parisc";
|
||||
if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3))
|
||||
return "powerpc";
|
||||
if (!strncmp(arch, "mips", 4))
|
||||
return "mips";
|
||||
if (!strncmp(arch, "sh", 2) && isdigit(arch[2]))
|
||||
return "sh";
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
const char *name,
|
||||
const char **path)
|
||||
{
|
||||
int idx;
|
||||
char *arch, *cross_env;
|
||||
const char *arch, *cross_env;
|
||||
struct utsname uts;
|
||||
const char *const *path_list;
|
||||
char *buf = NULL;
|
||||
|
||||
arch = normalize_arch(env->arch);
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
goto out;
|
||||
|
||||
@ -110,7 +140,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
* We don't need to try to find objdump path for native system.
|
||||
* Just use default binutils path (e.g.: "objdump").
|
||||
*/
|
||||
if (!strcmp(uts.machine, env->arch))
|
||||
if (!strcmp(normalize_arch(uts.machine), arch))
|
||||
goto out;
|
||||
|
||||
cross_env = getenv("CROSS_COMPILE");
|
||||
@ -127,8 +157,6 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
free(buf);
|
||||
}
|
||||
|
||||
arch = env->arch;
|
||||
|
||||
if (!strcmp(arch, "arm"))
|
||||
path_list = arm_triplets;
|
||||
else if (!strcmp(arch, "powerpc"))
|
||||
@ -139,9 +167,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
path_list = s390_triplets;
|
||||
else if (!strcmp(arch, "sparc"))
|
||||
path_list = sparc_triplets;
|
||||
else if (!strcmp(arch, "x86") || !strcmp(arch, "i386") ||
|
||||
!strcmp(arch, "i486") || !strcmp(arch, "i586") ||
|
||||
!strcmp(arch, "i686"))
|
||||
else if (!strcmp(arch, "x86"))
|
||||
path_list = x86_triplets;
|
||||
else if (!strcmp(arch, "mips"))
|
||||
path_list = mips_triplets;
|
||||
@ -173,6 +199,13 @@ out_error:
|
||||
|
||||
int perf_session_env__lookup_objdump(struct perf_session_env *env)
|
||||
{
|
||||
/*
|
||||
* For live mode, env->arch will be NULL and we can use
|
||||
* the native objdump tool.
|
||||
*/
|
||||
if (env->arch == NULL)
|
||||
return 0;
|
||||
|
||||
return perf_session_env__lookup_binutils_path(env, "objdump",
|
||||
&objdump_path);
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ find_next:
|
||||
}
|
||||
|
||||
if (use_browser > 0) {
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0);
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL);
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
next = rb_next(nd);
|
||||
|
@ -154,7 +154,7 @@ static double get_period_percent(struct hist_entry *he, u64 period)
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
double new_percent = get_period_percent(he, he->stat.period);
|
||||
double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0;
|
||||
|
||||
@ -165,7 +165,7 @@ double perf_diff__compute_delta(struct hist_entry *he)
|
||||
|
||||
double perf_diff__compute_ratio(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
@ -176,7 +176,7 @@ double perf_diff__compute_ratio(struct hist_entry *he)
|
||||
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
@ -193,7 +193,7 @@ s64 perf_diff__compute_wdiff(struct hist_entry *he)
|
||||
|
||||
static int formula_delta(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
|
||||
if (!pair)
|
||||
return -1;
|
||||
@ -207,7 +207,7 @@ static int formula_delta(struct hist_entry *he, char *buf, size_t size)
|
||||
|
||||
static int formula_ratio(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
@ -219,7 +219,7 @@ static int formula_ratio(struct hist_entry *he, char *buf, size_t size)
|
||||
|
||||
static int formula_wdiff(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
@ -334,36 +334,6 @@ static void hists__name_resort(struct hists *self, bool sort)
|
||||
self->entries = tmp;
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__find_entry(struct hists *self,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node *n = self->entries.rb_node;
|
||||
|
||||
while (n) {
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
|
||||
int64_t cmp = hist_entry__cmp(he, iter);
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
else if (cmp > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hists__match(struct hists *older, struct hists *newer)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
pos->pair = hists__find_entry(older, pos);
|
||||
}
|
||||
}
|
||||
|
||||
static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
@ -402,7 +372,7 @@ static void hists__baseline_only(struct hists *hists)
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
if (!he->pair) {
|
||||
if (!hist_entry__next_pair(he)) {
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
hist_entry__free(he);
|
||||
}
|
||||
@ -517,10 +487,12 @@ static void hists__compute_resort(struct hists *hists)
|
||||
|
||||
static void hists__process(struct hists *old, struct hists *new)
|
||||
{
|
||||
hists__match(old, new);
|
||||
hists__match(new, old);
|
||||
|
||||
if (show_baseline_only)
|
||||
hists__baseline_only(new);
|
||||
else
|
||||
hists__link(new, old);
|
||||
|
||||
if (sort_compute) {
|
||||
hists__precompute(new);
|
||||
|
@ -428,10 +428,11 @@ static int __cmd_report(struct perf_report *rep)
|
||||
if (use_browser > 0) {
|
||||
if (use_browser == 1) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
NULL,
|
||||
&session->header.env);
|
||||
} else if (use_browser == 2) {
|
||||
perf_evlist__gtk_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
NULL);
|
||||
}
|
||||
} else
|
||||
perf_evlist__tty_browse_hists(session->evlist, rep, help);
|
||||
@ -672,12 +673,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
has_br_stack = perf_header__has_feat(&session->header,
|
||||
HEADER_BRANCH_STACK);
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&session->header.env);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sort__branch_mode == -1 && has_br_stack)
|
||||
sort__branch_mode = 1;
|
||||
|
||||
|
@ -582,6 +582,11 @@ static void *display_thread_tui(void *arg)
|
||||
struct perf_evsel *pos;
|
||||
struct perf_top *top = arg;
|
||||
const char *help = "For a higher level overview, try: perf top --sort comm,dso";
|
||||
struct hist_browser_timer hbt = {
|
||||
.timer = perf_top__sort_new_samples,
|
||||
.arg = top,
|
||||
.refresh = top->delay_secs,
|
||||
};
|
||||
|
||||
perf_top__sort_new_samples(top);
|
||||
|
||||
@ -593,9 +598,8 @@ static void *display_thread_tui(void *arg)
|
||||
list_for_each_entry(pos, &top->evlist->entries, node)
|
||||
pos->hists.uid_filter_str = top->target.uid_str;
|
||||
|
||||
perf_evlist__tui_browse_hists(top->evlist, help,
|
||||
perf_top__sort_new_samples,
|
||||
top, top->delay_secs);
|
||||
perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
||||
&top->session->header.env);
|
||||
|
||||
exit_browser(0);
|
||||
exit(0);
|
||||
|
@ -484,6 +484,8 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
test_attr__init();
|
||||
|
||||
/*
|
||||
* We use PATH to find perf commands, but we prepend some higher
|
||||
* precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
|
||||
|
@ -174,13 +174,26 @@ static inline unsigned long long rdclock(void)
|
||||
(void) (&_min1 == &_min2); \
|
||||
_min1 < _min2 ? _min1 : _min2; })
|
||||
|
||||
extern bool test_attr__enabled;
|
||||
void test_attr__init(void);
|
||||
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags);
|
||||
int test_attr__run(void);
|
||||
|
||||
static inline int
|
||||
sys_perf_event_open(struct perf_event_attr *attr,
|
||||
pid_t pid, int cpu, int group_fd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, attr, pid, cpu,
|
||||
group_fd, flags);
|
||||
int fd;
|
||||
|
||||
fd = syscall(__NR_perf_event_open, attr, pid, cpu,
|
||||
group_fd, flags);
|
||||
|
||||
if (unlikely(test_attr__enabled))
|
||||
test_attr__open(attr, pid, cpu, fd, group_fd, flags);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#define MAX_COUNTERS 256
|
||||
|
174
tools/perf/tests/attr.c
Normal file
174
tools/perf/tests/attr.c
Normal file
@ -0,0 +1,174 @@
|
||||
|
||||
/*
|
||||
* The struct perf_event_attr test support.
|
||||
*
|
||||
* This test is embedded inside into perf directly and is governed
|
||||
* by the PERF_TEST_ATTR environment variable and hook inside
|
||||
* sys_perf_event_open function.
|
||||
*
|
||||
* The general idea is to store 'struct perf_event_attr' details for
|
||||
* each event created within single perf command. Each event details
|
||||
* are stored into separate text file. Once perf command is finished
|
||||
* these files can be checked for values we expect for command.
|
||||
*
|
||||
* Besides 'struct perf_event_attr' values we also store 'fd' and
|
||||
* 'group_fd' values to allow checking for groups created.
|
||||
*
|
||||
* This all is triggered by setting PERF_TEST_ATTR environment variable.
|
||||
* It must contain name of existing directory with access and write
|
||||
* permissions. All the event text files are stored there.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include "exec_cmd.h"
|
||||
|
||||
#define ENV "PERF_TEST_ATTR"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
bool test_attr__enabled;
|
||||
|
||||
static char *dir;
|
||||
|
||||
void test_attr__init(void)
|
||||
{
|
||||
dir = getenv(ENV);
|
||||
test_attr__enabled = (dir != NULL);
|
||||
}
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
#define __WRITE_ASS(str, fmt, data) \
|
||||
do { \
|
||||
char buf[BUFSIZE]; \
|
||||
size_t size; \
|
||||
\
|
||||
size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \
|
||||
if (1 != fwrite(buf, size, 1, file)) { \
|
||||
perror("test attr - failed to write event file"); \
|
||||
fclose(file); \
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field)
|
||||
|
||||
static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags)
|
||||
{
|
||||
FILE *file;
|
||||
char path[PATH_MAX];
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir,
|
||||
attr->type, attr->config, fd);
|
||||
|
||||
file = fopen(path, "w+");
|
||||
if (!file) {
|
||||
perror("test attr - failed to open event file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fprintf(file, "[event-%d-%llu-%d]\n",
|
||||
attr->type, attr->config, fd) < 0) {
|
||||
perror("test attr - failed to write event file");
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* syscall arguments */
|
||||
__WRITE_ASS(fd, "d", fd);
|
||||
__WRITE_ASS(group_fd, "d", group_fd);
|
||||
__WRITE_ASS(cpu, "d", cpu);
|
||||
__WRITE_ASS(pid, "d", pid);
|
||||
__WRITE_ASS(flags, "lu", flags);
|
||||
|
||||
/* struct perf_event_attr */
|
||||
WRITE_ASS(type, PRIu32);
|
||||
WRITE_ASS(size, PRIu32);
|
||||
WRITE_ASS(config, "llu");
|
||||
WRITE_ASS(sample_period, "llu");
|
||||
WRITE_ASS(sample_type, "llu");
|
||||
WRITE_ASS(read_format, "llu");
|
||||
WRITE_ASS(disabled, "d");
|
||||
WRITE_ASS(inherit, "d");
|
||||
WRITE_ASS(pinned, "d");
|
||||
WRITE_ASS(exclusive, "d");
|
||||
WRITE_ASS(exclude_user, "d");
|
||||
WRITE_ASS(exclude_kernel, "d");
|
||||
WRITE_ASS(exclude_hv, "d");
|
||||
WRITE_ASS(exclude_idle, "d");
|
||||
WRITE_ASS(mmap, "d");
|
||||
WRITE_ASS(comm, "d");
|
||||
WRITE_ASS(freq, "d");
|
||||
WRITE_ASS(inherit_stat, "d");
|
||||
WRITE_ASS(enable_on_exec, "d");
|
||||
WRITE_ASS(task, "d");
|
||||
WRITE_ASS(watermark, "d");
|
||||
WRITE_ASS(precise_ip, "d");
|
||||
WRITE_ASS(mmap_data, "d");
|
||||
WRITE_ASS(sample_id_all, "d");
|
||||
WRITE_ASS(exclude_host, "d");
|
||||
WRITE_ASS(exclude_guest, "d");
|
||||
WRITE_ASS(exclude_callchain_kernel, "d");
|
||||
WRITE_ASS(exclude_callchain_user, "d");
|
||||
WRITE_ASS(wakeup_events, PRIu32);
|
||||
WRITE_ASS(bp_type, PRIu32);
|
||||
WRITE_ASS(config1, "llu");
|
||||
WRITE_ASS(config2, "llu");
|
||||
WRITE_ASS(branch_sample_type, "llu");
|
||||
WRITE_ASS(sample_regs_user, "llu");
|
||||
WRITE_ASS(sample_stack_user, PRIu32);
|
||||
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags)
|
||||
{
|
||||
int errno_saved = errno;
|
||||
|
||||
if (store_event(attr, pid, cpu, fd, group_fd, flags))
|
||||
die("test attr FAILED");
|
||||
|
||||
errno = errno_saved;
|
||||
}
|
||||
|
||||
static int run_dir(const char *d, const char *perf)
|
||||
{
|
||||
char cmd[3*PATH_MAX];
|
||||
|
||||
snprintf(cmd, 3*PATH_MAX, "python %s/attr.py -d %s/attr/ -p %s %s",
|
||||
d, d, perf, verbose ? "-v" : "");
|
||||
|
||||
return system(cmd);
|
||||
}
|
||||
|
||||
int test_attr__run(void)
|
||||
{
|
||||
struct stat st;
|
||||
char path_perf[PATH_MAX];
|
||||
char path_dir[PATH_MAX];
|
||||
|
||||
/* First try developement tree tests. */
|
||||
if (!lstat("./tests", &st))
|
||||
return run_dir("./tests", "./perf");
|
||||
|
||||
/* Then installed path. */
|
||||
snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path());
|
||||
snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR);
|
||||
|
||||
if (!lstat(path_dir, &st) &&
|
||||
!lstat(path_perf, &st))
|
||||
return run_dir(path_dir, path_perf);
|
||||
|
||||
fprintf(stderr, " (ommitted)");
|
||||
return 0;
|
||||
}
|
322
tools/perf/tests/attr.py
Normal file
322
tools/perf/tests/attr.py
Normal file
@ -0,0 +1,322 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import optparse
|
||||
import tempfile
|
||||
import logging
|
||||
import shutil
|
||||
import ConfigParser
|
||||
|
||||
class Fail(Exception):
|
||||
def __init__(self, test, msg):
|
||||
self.msg = msg
|
||||
self.test = test
|
||||
def getMsg(self):
|
||||
return '\'%s\' - %s' % (self.test.path, self.msg)
|
||||
|
||||
class Unsup(Exception):
|
||||
def __init__(self, test):
|
||||
self.test = test
|
||||
def getMsg(self):
|
||||
return '\'%s\'' % self.test.path
|
||||
|
||||
class Event(dict):
|
||||
terms = [
|
||||
'flags',
|
||||
'type',
|
||||
'size',
|
||||
'config',
|
||||
'sample_period',
|
||||
'sample_type',
|
||||
'read_format',
|
||||
'disabled',
|
||||
'inherit',
|
||||
'pinned',
|
||||
'exclusive',
|
||||
'exclude_user',
|
||||
'exclude_kernel',
|
||||
'exclude_hv',
|
||||
'exclude_idle',
|
||||
'mmap',
|
||||
'comm',
|
||||
'freq',
|
||||
'inherit_stat',
|
||||
'enable_on_exec',
|
||||
'task',
|
||||
'watermark',
|
||||
'precise_ip',
|
||||
'mmap_data',
|
||||
'sample_id_all',
|
||||
'exclude_host',
|
||||
'exclude_guest',
|
||||
'exclude_callchain_kernel',
|
||||
'exclude_callchain_user',
|
||||
'wakeup_events',
|
||||
'bp_type',
|
||||
'config1',
|
||||
'config2',
|
||||
'branch_sample_type',
|
||||
'sample_regs_user',
|
||||
'sample_stack_user',
|
||||
]
|
||||
|
||||
def add(self, data):
|
||||
for key, val in data:
|
||||
log.debug(" %s = %s" % (key, val))
|
||||
self[key] = val
|
||||
|
||||
def __init__(self, name, data, base):
|
||||
log.info(" Event %s" % name);
|
||||
self.name = name;
|
||||
self.group = ''
|
||||
self.add(base)
|
||||
self.add(data)
|
||||
|
||||
def compare_data(self, a, b):
|
||||
# Allow multiple values in assignment separated by '|'
|
||||
a_list = a.split('|')
|
||||
b_list = b.split('|')
|
||||
|
||||
for a_item in a_list:
|
||||
for b_item in b_list:
|
||||
if (a_item == b_item):
|
||||
return True
|
||||
elif (a_item == '*') or (b_item == '*'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def equal(self, other):
|
||||
for t in Event.terms:
|
||||
log.debug(" [%s] %s %s" % (t, self[t], other[t]));
|
||||
if not self.has_key(t) or not other.has_key(t):
|
||||
return False
|
||||
if not self.compare_data(self[t], other[t]):
|
||||
return False
|
||||
return True
|
||||
|
||||
# Test file description needs to have following sections:
|
||||
# [config]
|
||||
# - just single instance in file
|
||||
# - needs to specify:
|
||||
# 'command' - perf command name
|
||||
# 'args' - special command arguments
|
||||
# 'ret' - expected command return value (0 by default)
|
||||
#
|
||||
# [eventX:base]
|
||||
# - one or multiple instances in file
|
||||
# - expected values assignments
|
||||
class Test(object):
|
||||
def __init__(self, path, options):
|
||||
parser = ConfigParser.SafeConfigParser()
|
||||
parser.read(path)
|
||||
|
||||
log.warning("running '%s'" % path)
|
||||
|
||||
self.path = path
|
||||
self.test_dir = options.test_dir
|
||||
self.perf = options.perf
|
||||
self.command = parser.get('config', 'command')
|
||||
self.args = parser.get('config', 'args')
|
||||
|
||||
try:
|
||||
self.ret = parser.get('config', 'ret')
|
||||
except:
|
||||
self.ret = 0
|
||||
|
||||
self.expect = {}
|
||||
self.result = {}
|
||||
log.info(" loading expected events");
|
||||
self.load_events(path, self.expect)
|
||||
|
||||
def is_event(self, name):
|
||||
if name.find("event") == -1:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def load_events(self, path, events):
|
||||
parser_event = ConfigParser.SafeConfigParser()
|
||||
parser_event.read(path)
|
||||
|
||||
# The event record section header contains 'event' word,
|
||||
# optionaly followed by ':' allowing to load 'parent
|
||||
# event' first as a base
|
||||
for section in filter(self.is_event, parser_event.sections()):
|
||||
|
||||
parser_items = parser_event.items(section);
|
||||
base_items = {}
|
||||
|
||||
# Read parent event if there's any
|
||||
if (':' in section):
|
||||
base = section[section.index(':') + 1:]
|
||||
parser_base = ConfigParser.SafeConfigParser()
|
||||
parser_base.read(self.test_dir + '/' + base)
|
||||
base_items = parser_base.items('event')
|
||||
|
||||
e = Event(section, parser_items, base_items)
|
||||
events[section] = e
|
||||
|
||||
def run_cmd(self, tempdir):
|
||||
cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir,
|
||||
self.perf, self.command, tempdir, self.args)
|
||||
ret = os.WEXITSTATUS(os.system(cmd))
|
||||
|
||||
log.info(" running '%s' ret %d " % (cmd, ret))
|
||||
|
||||
if ret != int(self.ret):
|
||||
raise Unsup(self)
|
||||
|
||||
def compare(self, expect, result):
|
||||
match = {}
|
||||
|
||||
log.info(" compare");
|
||||
|
||||
# For each expected event find all matching
|
||||
# events in result. Fail if there's not any.
|
||||
for exp_name, exp_event in expect.items():
|
||||
exp_list = []
|
||||
log.debug(" matching [%s]" % exp_name)
|
||||
for res_name, res_event in result.items():
|
||||
log.debug(" to [%s]" % res_name)
|
||||
if (exp_event.equal(res_event)):
|
||||
exp_list.append(res_name)
|
||||
log.debug(" ->OK")
|
||||
else:
|
||||
log.debug(" ->FAIL");
|
||||
|
||||
log.info(" match: [%s] matches %s" % (exp_name, str(exp_list)))
|
||||
|
||||
# we did not any matching event - fail
|
||||
if (not exp_list):
|
||||
raise Fail(self, 'match failure');
|
||||
|
||||
match[exp_name] = exp_list
|
||||
|
||||
# For each defined group in the expected events
|
||||
# check we match the same group in the result.
|
||||
for exp_name, exp_event in expect.items():
|
||||
group = exp_event.group
|
||||
|
||||
if (group == ''):
|
||||
continue
|
||||
|
||||
for res_name in match[exp_name]:
|
||||
res_group = result[res_name].group
|
||||
if res_group not in match[group]:
|
||||
raise Fail(self, 'group failure')
|
||||
|
||||
log.info(" group: [%s] matches group leader %s" %
|
||||
(exp_name, str(match[group])))
|
||||
|
||||
log.info(" matched")
|
||||
|
||||
def resolve_groups(self, events):
|
||||
for name, event in events.items():
|
||||
group_fd = event['group_fd'];
|
||||
if group_fd == '-1':
|
||||
continue;
|
||||
|
||||
for iname, ievent in events.items():
|
||||
if (ievent['fd'] == group_fd):
|
||||
event.group = iname
|
||||
log.debug('[%s] has group leader [%s]' % (name, iname))
|
||||
break;
|
||||
|
||||
def run(self):
|
||||
tempdir = tempfile.mkdtemp();
|
||||
|
||||
try:
|
||||
# run the test script
|
||||
self.run_cmd(tempdir);
|
||||
|
||||
# load events expectation for the test
|
||||
log.info(" loading result events");
|
||||
for f in glob.glob(tempdir + '/event*'):
|
||||
self.load_events(f, self.result);
|
||||
|
||||
# resolve group_fd to event names
|
||||
self.resolve_groups(self.expect);
|
||||
self.resolve_groups(self.result);
|
||||
|
||||
# do the expectation - results matching - both ways
|
||||
self.compare(self.expect, self.result)
|
||||
self.compare(self.result, self.expect)
|
||||
|
||||
finally:
|
||||
# cleanup
|
||||
shutil.rmtree(tempdir)
|
||||
|
||||
|
||||
def run_tests(options):
|
||||
for f in glob.glob(options.test_dir + '/' + options.test):
|
||||
try:
|
||||
Test(f, options).run()
|
||||
except Unsup, obj:
|
||||
log.warning("unsupp %s" % obj.getMsg())
|
||||
|
||||
def setup_log(verbose):
|
||||
global log
|
||||
level = logging.CRITICAL
|
||||
|
||||
if verbose == 1:
|
||||
level = logging.WARNING
|
||||
if verbose == 2:
|
||||
level = logging.INFO
|
||||
if verbose >= 3:
|
||||
level = logging.DEBUG
|
||||
|
||||
log = logging.getLogger('test')
|
||||
log.setLevel(level)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(level)
|
||||
formatter = logging.Formatter('%(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
log.addHandler(ch)
|
||||
|
||||
USAGE = '''%s [OPTIONS]
|
||||
-d dir # tests dir
|
||||
-p path # perf binary
|
||||
-t test # single test
|
||||
-v # verbose level
|
||||
''' % sys.argv[0]
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage=USAGE)
|
||||
|
||||
parser.add_option("-t", "--test",
|
||||
action="store", type="string", dest="test")
|
||||
parser.add_option("-d", "--test-dir",
|
||||
action="store", type="string", dest="test_dir")
|
||||
parser.add_option("-p", "--perf",
|
||||
action="store", type="string", dest="perf")
|
||||
parser.add_option("-v", "--verbose",
|
||||
action="count", dest="verbose")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
if args:
|
||||
parser.error('FAILED wrong arguments %s' % ' '.join(args))
|
||||
return -1
|
||||
|
||||
setup_log(options.verbose)
|
||||
|
||||
if not options.test_dir:
|
||||
print 'FAILED no -d option specified'
|
||||
sys.exit(-1)
|
||||
|
||||
if not options.test:
|
||||
options.test = 'test*'
|
||||
|
||||
try:
|
||||
run_tests(options)
|
||||
|
||||
except Fail, obj:
|
||||
print "FAILED %s" % obj.getMsg();
|
||||
sys.exit(-1)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
64
tools/perf/tests/attr/README
Normal file
64
tools/perf/tests/attr/README
Normal file
@ -0,0 +1,64 @@
|
||||
The struct perf_event_attr test (attr tests) support
|
||||
====================================================
|
||||
This testing support is embedded into perf directly and is governed
|
||||
by the PERF_TEST_ATTR environment variable and hook inside the
|
||||
sys_perf_event_open function.
|
||||
|
||||
The general idea is to store 'struct perf_event_attr' details for
|
||||
each event created within single perf command. Each event details
|
||||
are stored into separate text file. Once perf command is finished
|
||||
these files are checked for values we expect for command.
|
||||
|
||||
The attr tests consist of following parts:
|
||||
|
||||
tests/attr.c
|
||||
------------
|
||||
This is the sys_perf_event_open hook implementation. The hook
|
||||
is triggered when the PERF_TEST_ATTR environment variable is
|
||||
defined. It must contain name of existing directory with access
|
||||
and write permissions.
|
||||
|
||||
For each sys_perf_event_open call event details are stored in
|
||||
separate file. Besides 'struct perf_event_attr' values we also
|
||||
store 'fd' and 'group_fd' values to allow checking for groups.
|
||||
|
||||
tests/attr.py
|
||||
-------------
|
||||
This is the python script that does all the hard work. It reads
|
||||
the test definition, executes it and checks results.
|
||||
|
||||
tests/attr/
|
||||
-----------
|
||||
Directory containing all attr test definitions.
|
||||
Following tests are defined (with perf commands):
|
||||
|
||||
perf record kill (test-record-basic)
|
||||
perf record -b kill (test-record-branch-any)
|
||||
perf record -j any kill (test-record-branch-filter-any)
|
||||
perf record -j any_call kill (test-record-branch-filter-any_call)
|
||||
perf record -j any_ret kill (test-record-branch-filter-any_ret)
|
||||
perf record -j hv kill (test-record-branch-filter-hv)
|
||||
perf record -j ind_call kill (test-record-branch-filter-ind_call)
|
||||
perf record -j k kill (test-record-branch-filter-k)
|
||||
perf record -j u kill (test-record-branch-filter-u)
|
||||
perf record -c 123 kill (test-record-count)
|
||||
perf record -d kill (test-record-data)
|
||||
perf record -F 100 kill (test-record-freq)
|
||||
perf record -g -- kill (test-record-graph-default)
|
||||
perf record -g dwarf -- kill (test-record-graph-dwarf)
|
||||
perf record -g fp kill (test-record-graph-fp)
|
||||
perf record --group -e cycles,instructions kill (test-record-group)
|
||||
perf record -e '{cycles,instructions}' kill (test-record-group1)
|
||||
perf record -D kill (test-record-no-delay)
|
||||
perf record -i kill (test-record-no-inherit)
|
||||
perf record -n kill (test-record-no-samples)
|
||||
perf record -c 100 -P kill (test-record-period)
|
||||
perf record -R kill (test-record-raw)
|
||||
perf stat -e cycles kill (test-stat-basic)
|
||||
perf stat kill (test-stat-default)
|
||||
perf stat -d kill (test-stat-detailed-1)
|
||||
perf stat -dd kill (test-stat-detailed-2)
|
||||
perf stat -ddd kill (test-stat-detailed-3)
|
||||
perf stat --group -e cycles,instructions kill (test-stat-group)
|
||||
perf stat -e '{cycles,instructions}' kill (test-stat-group1)
|
||||
perf stat -i -e cycles kill (test-stat-no-inherit)
|
39
tools/perf/tests/attr/base-record
Normal file
39
tools/perf/tests/attr/base-record
Normal file
@ -0,0 +1,39 @@
|
||||
[event]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
type=0|1
|
||||
size=96
|
||||
config=0
|
||||
sample_period=4000
|
||||
sample_type=263
|
||||
read_format=7
|
||||
disabled=1
|
||||
inherit=1
|
||||
pinned=0
|
||||
exclusive=0
|
||||
exclude_user=0
|
||||
exclude_kernel=0
|
||||
exclude_hv=0
|
||||
exclude_idle=0
|
||||
mmap=1
|
||||
comm=1
|
||||
freq=1
|
||||
inherit_stat=0
|
||||
enable_on_exec=1
|
||||
task=0
|
||||
watermark=0
|
||||
precise_ip=0
|
||||
mmap_data=0
|
||||
sample_id_all=1
|
||||
exclude_host=0
|
||||
exclude_guest=1
|
||||
exclude_callchain_kernel=0
|
||||
exclude_callchain_user=0
|
||||
wakeup_events=0
|
||||
bp_type=0
|
||||
config1=0
|
||||
config2=0
|
||||
branch_sample_type=0
|
||||
sample_regs_user=0
|
||||
sample_stack_user=0
|
39
tools/perf/tests/attr/base-stat
Normal file
39
tools/perf/tests/attr/base-stat
Normal file
@ -0,0 +1,39 @@
|
||||
[event]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
type=0
|
||||
size=96
|
||||
config=0
|
||||
sample_period=0
|
||||
sample_type=0
|
||||
read_format=3
|
||||
disabled=1
|
||||
inherit=1
|
||||
pinned=0
|
||||
exclusive=0
|
||||
exclude_user=0
|
||||
exclude_kernel=0
|
||||
exclude_hv=0
|
||||
exclude_idle=0
|
||||
mmap=0
|
||||
comm=0
|
||||
freq=0
|
||||
inherit_stat=0
|
||||
enable_on_exec=1
|
||||
task=0
|
||||
watermark=0
|
||||
precise_ip=0
|
||||
mmap_data=0
|
||||
sample_id_all=0
|
||||
exclude_host=0
|
||||
exclude_guest=1
|
||||
exclude_callchain_kernel=0
|
||||
exclude_callchain_user=0
|
||||
wakeup_events=0
|
||||
bp_type=0
|
||||
config1=0
|
||||
config2=0
|
||||
branch_sample_type=0
|
||||
sample_regs_user=0
|
||||
sample_stack_user=0
|
5
tools/perf/tests/attr/test-record-basic
Normal file
5
tools/perf/tests/attr/test-record-basic
Normal file
@ -0,0 +1,5 @@
|
||||
[config]
|
||||
command = record
|
||||
args = kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
8
tools/perf/tests/attr/test-record-branch-any
Normal file
8
tools/perf/tests/attr/test-record-branch-any
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -b kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-any
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-any_call
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any_call
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any_call kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=16
|
8
tools/perf/tests/attr/test-record-branch-filter-any_ret
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any_ret
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any_ret kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=32
|
8
tools/perf/tests/attr/test-record-branch-filter-hv
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-hv
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j hv kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-ind_call
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-ind_call
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j ind_call kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=64
|
8
tools/perf/tests/attr/test-record-branch-filter-k
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-k
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j k kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-u
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-u
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j u kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-count
Normal file
8
tools/perf/tests/attr/test-record-count
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -c 123 kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=123
|
||||
sample_type=7
|
||||
freq=0
|
8
tools/perf/tests/attr/test-record-data
Normal file
8
tools/perf/tests/attr/test-record-data
Normal file
@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -d kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=271
|
||||
mmap_data=1
|
6
tools/perf/tests/attr/test-record-freq
Normal file
6
tools/perf/tests/attr/test-record-freq
Normal file
@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -F 100 kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=100
|
6
tools/perf/tests/attr/test-record-graph-default
Normal file
6
tools/perf/tests/attr/test-record-graph-default
Normal file
@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g -- kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=295
|
10
tools/perf/tests/attr/test-record-graph-dwarf
Normal file
10
tools/perf/tests/attr/test-record-graph-dwarf
Normal file
@ -0,0 +1,10 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g dwarf -- kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=12583
|
||||
exclude_callchain_user=1
|
||||
sample_stack_user=8192
|
||||
# TODO different for each arch, no support for that now
|
||||
sample_regs_user=*
|
6
tools/perf/tests/attr/test-record-graph-fp
Normal file
6
tools/perf/tests/attr/test-record-graph-fp
Normal file
@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g fp kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=295
|
17
tools/perf/tests/attr/test-record-group
Normal file
17
tools/perf/tests/attr/test-record-group
Normal file
@ -0,0 +1,17 @@
|
||||
[config]
|
||||
command = record
|
||||
args = --group -e cycles,instructions kill >/dev/null 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
20
tools/perf/tests/attr/test-record-group1
Normal file
20
tools/perf/tests/attr/test-record-group1
Normal file
@ -0,0 +1,20 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
group_fd=1
|
||||
type=0
|
||||
config=1
|
||||
sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
# TODO this is disabled for --group option, enabled otherwise
|
||||
# check why..
|
||||
enable_on_exec=1
|
9
tools/perf/tests/attr/test-record-no-delay
Normal file
9
tools/perf/tests/attr/test-record-no-delay
Normal file
@ -0,0 +1,9 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -D kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=263
|
||||
watermark=0
|
||||
wakeup_events=1
|
7
tools/perf/tests/attr/test-record-no-inherit
Normal file
7
tools/perf/tests/attr/test-record-no-inherit
Normal file
@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -i kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=259
|
||||
inherit=0
|
6
tools/perf/tests/attr/test-record-no-samples
Normal file
6
tools/perf/tests/attr/test-record-no-samples
Normal file
@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -n kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=0
|
7
tools/perf/tests/attr/test-record-period
Normal file
7
tools/perf/tests/attr/test-record-period
Normal file
@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -c 100 -P kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=100
|
||||
freq=0
|
7
tools/perf/tests/attr/test-record-raw
Normal file
7
tools/perf/tests/attr/test-record-raw
Normal file
@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -R kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=1415
|
6
tools/perf/tests/attr/test-stat-basic
Normal file
6
tools/perf/tests/attr/test-stat-basic
Normal file
@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -e cycles kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-stat]
|
64
tools/perf/tests/attr/test-stat-default
Normal file
64
tools/perf/tests/attr/test-stat-default
Normal file
@ -0,0 +1,64 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
101
tools/perf/tests/attr/test-stat-detailed-1
Normal file
101
tools/perf/tests/attr/test-stat-detailed-1
Normal file
@ -0,0 +1,101 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -d kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
155
tools/perf/tests/attr/test-stat-detailed-2
Normal file
155
tools/perf/tests/attr/test-stat-detailed-2
Normal file
@ -0,0 +1,155 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -dd kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event15:base-stat]
|
||||
fd=15
|
||||
type=3
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event16:base-stat]
|
||||
fd=16
|
||||
type=3
|
||||
config=65537
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event17:base-stat]
|
||||
fd=17
|
||||
type=3
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event18:base-stat]
|
||||
fd=18
|
||||
type=3
|
||||
config=65539
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event19:base-stat]
|
||||
fd=19
|
||||
type=3
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event20:base-stat]
|
||||
fd=20
|
||||
type=3
|
||||
config=65540
|
173
tools/perf/tests/attr/test-stat-detailed-3
Normal file
173
tools/perf/tests/attr/test-stat-detailed-3
Normal file
@ -0,0 +1,173 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -ddd kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event15:base-stat]
|
||||
fd=15
|
||||
type=3
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event16:base-stat]
|
||||
fd=16
|
||||
type=3
|
||||
config=65537
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event17:base-stat]
|
||||
fd=17
|
||||
type=3
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event18:base-stat]
|
||||
fd=18
|
||||
type=3
|
||||
config=65539
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event19:base-stat]
|
||||
fd=19
|
||||
type=3
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event20:base-stat]
|
||||
fd=20
|
||||
type=3
|
||||
config=65540
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event21:base-stat]
|
||||
fd=21
|
||||
type=3
|
||||
config=512
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event22:base-stat]
|
||||
fd=22
|
||||
type=3
|
||||
config=66048
|
15
tools/perf/tests/attr/test-stat-group
Normal file
15
tools/perf/tests/attr/test-stat-group
Normal file
@ -0,0 +1,15 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = --group -e cycles,instructions kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event-1:base-stat]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
|
||||
[event-2:base-stat]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
disabled=0
|
||||
enable_on_exec=0
|
17
tools/perf/tests/attr/test-stat-group1
Normal file
17
tools/perf/tests/attr/test-stat-group1
Normal file
@ -0,0 +1,17 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -e '{cycles,instructions}' kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event-1:base-stat]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
|
||||
[event-2:base-stat]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
# TODO both disabled and enable_on_exec are disabled for --group option,
|
||||
# enabled otherwise, check why..
|
||||
disabled=1
|
||||
enable_on_exec=1
|
7
tools/perf/tests/attr/test-stat-no-inherit
Normal file
7
tools/perf/tests/attr/test-stat-no-inherit
Normal file
@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -i -e cycles kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-stat]
|
||||
inherit=0
|
@ -10,6 +10,7 @@
|
||||
#include "util/debug.h"
|
||||
#include "util/debugfs.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/symbol.h"
|
||||
@ -318,7 +319,7 @@ static int test__open_syscall_event(void)
|
||||
nr_open_calls, evsel->counts->cpu[0].val);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
|
||||
err = 0;
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
@ -604,19 +605,13 @@ out_free_threads:
|
||||
#undef nsyscalls
|
||||
}
|
||||
|
||||
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp,
|
||||
size_t *sizep)
|
||||
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
|
||||
{
|
||||
cpu_set_t *mask;
|
||||
size_t size;
|
||||
int i, cpu = -1, nrcpus = 1024;
|
||||
realloc:
|
||||
mask = CPU_ALLOC(nrcpus);
|
||||
size = CPU_ALLOC_SIZE(nrcpus);
|
||||
CPU_ZERO_S(size, mask);
|
||||
CPU_ZERO(maskp);
|
||||
|
||||
if (sched_getaffinity(pid, size, mask) == -1) {
|
||||
CPU_FREE(mask);
|
||||
if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
|
||||
if (errno == EINVAL && nrcpus < (1024 << 8)) {
|
||||
nrcpus = nrcpus << 2;
|
||||
goto realloc;
|
||||
@ -626,19 +621,14 @@ realloc:
|
||||
}
|
||||
|
||||
for (i = 0; i < nrcpus; i++) {
|
||||
if (CPU_ISSET_S(i, size, mask)) {
|
||||
if (cpu == -1) {
|
||||
if (CPU_ISSET(i, maskp)) {
|
||||
if (cpu == -1)
|
||||
cpu = i;
|
||||
*maskp = mask;
|
||||
*sizep = size;
|
||||
} else
|
||||
CPU_CLR_S(i, size, mask);
|
||||
else
|
||||
CPU_CLR(i, maskp);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu == -1)
|
||||
CPU_FREE(mask);
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
@ -653,8 +643,8 @@ static int test__PERF_RECORD(void)
|
||||
.freq = 10,
|
||||
.mmap_pages = 256,
|
||||
};
|
||||
cpu_set_t *cpu_mask = NULL;
|
||||
size_t cpu_mask_size = 0;
|
||||
cpu_set_t cpu_mask;
|
||||
size_t cpu_mask_size = sizeof(cpu_mask);
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_sample sample;
|
||||
@ -718,8 +708,7 @@ static int test__PERF_RECORD(void)
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
perf_evlist__config_attrs(evlist, &opts);
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask,
|
||||
&cpu_mask_size);
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
|
||||
if (err < 0) {
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
@ -730,9 +719,9 @@ static int test__PERF_RECORD(void)
|
||||
/*
|
||||
* So that we can check perf_sample.cpu on all the samples.
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) {
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
goto out_free_cpu_mask;
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -916,8 +905,6 @@ found_exit:
|
||||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_free_cpu_mask:
|
||||
CPU_FREE(cpu_mask);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
@ -1344,8 +1331,8 @@ static int test__syscall_open_tp_fields(void)
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Generate the event:
|
||||
*/
|
||||
* Generate the event:
|
||||
*/
|
||||
open(filename, flags);
|
||||
|
||||
while (1) {
|
||||
@ -1454,6 +1441,10 @@ static struct test {
|
||||
.desc = "Generate and check syscalls:sys_enter_open event fields",
|
||||
.func = test__syscall_open_tp_fields,
|
||||
},
|
||||
{
|
||||
.desc = "struct perf_event_attr setup",
|
||||
.func = test_attr__run,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
@ -1495,7 +1486,7 @@ static int __cmd_test(int argc, const char *argv[])
|
||||
width = len;
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
i = 0;
|
||||
while (tests[i].func) {
|
||||
int curr = i++, err;
|
@ -6,6 +6,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
@ -188,6 +188,12 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
||||
struct disasm_line *cursor = ab->selection, *target;
|
||||
struct browser_disasm_line *btarget, *bcursor;
|
||||
unsigned int from, to;
|
||||
struct map_symbol *ms = ab->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
|
||||
/* PLT symbols contain external offsets */
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
|
||||
!disasm_line__has_offset(cursor))
|
||||
@ -386,9 +392,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
|
||||
browser->b.nr_entries = browser->nr_asm_entries;
|
||||
}
|
||||
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
int evidx, void (*timer)(void *arg),
|
||||
void *arg, int delay_secs)
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct disasm_line *dl = browser->selection;
|
||||
@ -418,7 +423,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
|
||||
symbol__tui_annotate(target, ms->map, evidx, hbt);
|
||||
ui_browser__show_title(&browser->b, sym->name);
|
||||
return true;
|
||||
}
|
||||
@ -602,13 +607,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser
|
||||
}
|
||||
|
||||
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
void(*timer)(void *arg),
|
||||
void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct rb_node *nd = NULL;
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
const char *help = "Press 'h' for help on key bindings";
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&browser->b, sym->name, help) < 0)
|
||||
@ -639,8 +644,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
if (timer != NULL)
|
||||
timer(arg);
|
||||
if (hbt)
|
||||
hbt->timer(hbt->arg);
|
||||
|
||||
if (delay_secs != 0)
|
||||
symbol__annotate_decay_histogram(sym, evidx);
|
||||
@ -740,7 +745,7 @@ show_help:
|
||||
goto show_sup_ins;
|
||||
goto out;
|
||||
} else if (!(annotate_browser__jump(browser) ||
|
||||
annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {
|
||||
annotate_browser__callq(browser, evidx, hbt))) {
|
||||
show_sup_ins:
|
||||
ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
|
||||
}
|
||||
@ -763,16 +768,21 @@ out:
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
|
||||
timer, arg, delay_secs);
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);
|
||||
}
|
||||
|
||||
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
|
||||
size_t size)
|
||||
{
|
||||
u64 offset;
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
|
||||
/* PLT symbols contain external offsets */
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
for (offset = 0; offset < size; ++offset) {
|
||||
struct disasm_line *dl = browser->offsets[offset], *dlt;
|
||||
@ -816,8 +826,7 @@ static inline int width_jumps(int n)
|
||||
}
|
||||
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
struct annotation *notes;
|
||||
@ -899,7 +908,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
|
||||
annotate_browser__update_addr_width(&browser);
|
||||
|
||||
ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
|
||||
ret = annotate_browser__run(&browser, evidx, hbt);
|
||||
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
disasm_line__free(pos);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "../../util/pstack.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../arch/common.h"
|
||||
|
||||
#include "../browser.h"
|
||||
#include "../helpline.h"
|
||||
@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
|
||||
}
|
||||
|
||||
static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
int key;
|
||||
char title[160];
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
|
||||
browser->b.entries = &browser->hists->entries;
|
||||
browser->b.nr_entries = browser->hists->nr_entries;
|
||||
@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
timer(arg);
|
||||
hbt->timer(hbt->arg);
|
||||
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
|
||||
|
||||
if (browser->hists->stats.nr_lost_warned !=
|
||||
@ -1127,11 +1129,17 @@ static inline void free_popup_options(char **options, int n)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether the browser is for 'top' or 'report' */
|
||||
static inline bool is_report_browser(void *timer)
|
||||
{
|
||||
return timer == NULL;
|
||||
}
|
||||
|
||||
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
const char *helpline, const char *ev_name,
|
||||
bool left_exits,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_browser *browser = hist_browser__new(hists);
|
||||
@ -1142,6 +1150,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
int key = -1;
|
||||
char buf[64];
|
||||
char script_opt[64];
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
|
||||
if (browser == NULL)
|
||||
return -1;
|
||||
@ -1164,7 +1173,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
|
||||
nr_options = 0;
|
||||
|
||||
key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
|
||||
key = hist_browser__run(browser, ev_name, hbt);
|
||||
|
||||
if (browser->he_selection != NULL) {
|
||||
thread = hist_browser__selected_thread(browser);
|
||||
@ -1214,7 +1223,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
}
|
||||
continue;
|
||||
case 'r':
|
||||
goto do_scripts;
|
||||
if (is_report_browser(hbt))
|
||||
goto do_scripts;
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
@ -1233,7 +1244,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"r Run available scripts\n"
|
||||
"r Run available scripts('perf report' only)\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"/ Filter symbol by name");
|
||||
@ -1358,6 +1369,9 @@ retry_popup_menu:
|
||||
struct hist_entry *he;
|
||||
int err;
|
||||
do_annotate:
|
||||
if (!objdump_path && perf_session_env__lookup_objdump(env))
|
||||
continue;
|
||||
|
||||
he = hist_browser__selected_entry(browser);
|
||||
if (he == NULL)
|
||||
continue;
|
||||
@ -1380,8 +1394,7 @@ do_annotate:
|
||||
* Don't let this be freed, say, by hists__decay_entry.
|
||||
*/
|
||||
he->used = true;
|
||||
err = hist_entry__tui_annotate(he, evsel->idx,
|
||||
timer, arg, delay_secs);
|
||||
err = hist_entry__tui_annotate(he, evsel->idx, hbt);
|
||||
he->used = false;
|
||||
/*
|
||||
* offer option to annotate the other branch source or target
|
||||
@ -1462,6 +1475,7 @@ struct perf_evsel_menu {
|
||||
struct ui_browser b;
|
||||
struct perf_evsel *selection;
|
||||
bool lost_events, lost_events_warned;
|
||||
struct perf_session_env *env;
|
||||
};
|
||||
|
||||
static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
@ -1504,11 +1518,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
|
||||
static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
||||
int nr_events, const char *help,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct perf_evlist *evlist = menu->b.priv;
|
||||
struct perf_evsel *pos;
|
||||
const char *ev_name, *title = "Available samples";
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&menu->b, title,
|
||||
@ -1520,7 +1535,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
timer(arg);
|
||||
hbt->timer(hbt->arg);
|
||||
|
||||
if (!menu->lost_events_warned && menu->lost_events) {
|
||||
ui_browser__warn_lost_events(&menu->b);
|
||||
@ -1538,12 +1553,12 @@ browse_hists:
|
||||
* Give the calling tool a chance to populate the non
|
||||
* default evsel resorted hists tree.
|
||||
*/
|
||||
if (timer)
|
||||
timer(arg);
|
||||
if (hbt)
|
||||
hbt->timer(hbt->arg);
|
||||
ev_name = perf_evsel__name(pos);
|
||||
key = perf_evsel__hists_browse(pos, nr_events, help,
|
||||
ev_name, true, timer,
|
||||
arg, delay_secs);
|
||||
ev_name, true, hbt,
|
||||
menu->env);
|
||||
ui_browser__show_title(&menu->b, title);
|
||||
switch (key) {
|
||||
case K_TAB:
|
||||
@ -1591,8 +1606,8 @@ out:
|
||||
|
||||
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evsel_menu menu = {
|
||||
@ -1604,6 +1619,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
.nr_entries = evlist->nr_entries,
|
||||
.priv = evlist,
|
||||
},
|
||||
.env = env,
|
||||
};
|
||||
|
||||
ui_helpline__push("Press ESC to exit");
|
||||
@ -1616,23 +1632,20 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
menu.b.width = line_len;
|
||||
}
|
||||
|
||||
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
|
||||
arg, delay_secs);
|
||||
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt);
|
||||
}
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
if (evlist->nr_entries == 1) {
|
||||
struct perf_evsel *first = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
const char *ev_name = perf_evsel__name(first);
|
||||
return perf_evsel__hists_browse(first, evlist->nr_entries, help,
|
||||
ev_name, false, timer, arg,
|
||||
delay_secs);
|
||||
ev_name, false, hbt, env);
|
||||
}
|
||||
|
||||
return __perf_evlist__tui_browse_hists(evlist, help,
|
||||
timer, arg, delay_secs);
|
||||
return __perf_evlist__tui_browse_hists(evlist, help, hbt, env);
|
||||
}
|
||||
|
@ -237,9 +237,7 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
void (*timer) (void *arg)__maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int delay_secs __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
|
@ -161,7 +161,7 @@ static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static double baseline_percent(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
struct hists *pair_hists = pair ? pair->hists : NULL;
|
||||
double percent = 0.0;
|
||||
|
||||
@ -179,7 +179,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
|
||||
if (he->pair)
|
||||
if (hist_entry__has_pairs(he))
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
@ -190,7 +190,7 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
double percent = baseline_percent(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
|
||||
|
||||
if (he->pair || symbol_conf.field_sep)
|
||||
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
@ -248,7 +248,7 @@ static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
u64 period = pair ? pair->stat.period : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
|
||||
|
||||
@ -354,7 +354,7 @@ static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused)
|
||||
static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
long displacement = pair ? pair->position - he->position : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s";
|
||||
char buf[32] = " ";
|
||||
|
@ -9,18 +9,14 @@ GVF=${OUTPUT}PERF-VERSION-FILE
|
||||
LF='
|
||||
'
|
||||
|
||||
#
|
||||
# First check if there is a .git to get the version from git describe
|
||||
# otherwise try to get the version from the kernel makefile
|
||||
# otherwise try to get the version from the kernel Makefile
|
||||
#
|
||||
if test -d ../../.git -o -f ../../.git &&
|
||||
VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git update-index -q --refresh
|
||||
test -z "$(git diff-index --name-only HEAD --)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*")
|
||||
then
|
||||
VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD))
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
else
|
||||
VN=$(MAKEFLAGS= make -sC ../.. kernelversion)
|
||||
|
@ -171,15 +171,15 @@ static int lock__parse(struct ins_operands *ops)
|
||||
if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
|
||||
goto out_free_ops;
|
||||
|
||||
ops->locked.ins = ins__find(name);
|
||||
if (ops->locked.ins == NULL)
|
||||
goto out_free_ops;
|
||||
ops->locked.ins = ins__find(name);
|
||||
if (ops->locked.ins == NULL)
|
||||
goto out_free_ops;
|
||||
|
||||
if (!ops->locked.ins->ops)
|
||||
return 0;
|
||||
if (!ops->locked.ins->ops)
|
||||
return 0;
|
||||
|
||||
if (ops->locked.ins->ops->parse)
|
||||
ops->locked.ins->ops->parse(ops->locked.ops);
|
||||
if (ops->locked.ins->ops->parse)
|
||||
ops->locked.ins->ops->parse(ops->locked.ops);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -401,6 +401,8 @@ static struct ins instructions[] = {
|
||||
{ .name = "testb", .ops = &mov_ops, },
|
||||
{ .name = "testl", .ops = &mov_ops, },
|
||||
{ .name = "xadd", .ops = &mov_ops, },
|
||||
{ .name = "xbeginl", .ops = &jump_ops, },
|
||||
{ .name = "xbeginq", .ops = &jump_ops, },
|
||||
};
|
||||
|
||||
static int ins__cmp(const void *name, const void *insp)
|
||||
@ -856,12 +858,41 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
||||
struct source_line *iter;
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int ret;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct source_line, node);
|
||||
|
||||
if (src_line->percent > iter->percent)
|
||||
ret = strcmp(iter->path, src_line->path);
|
||||
if (ret == 0) {
|
||||
iter->percent_sum += src_line->percent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
src_line->percent_sum = src_line->percent;
|
||||
|
||||
rb_link_node(&src_line->node, parent, p);
|
||||
rb_insert_color(&src_line->node, root);
|
||||
}
|
||||
|
||||
static void __resort_source_line(struct rb_root *root, struct source_line *src_line)
|
||||
{
|
||||
struct source_line *iter;
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct source_line, node);
|
||||
|
||||
if (src_line->percent_sum > iter->percent_sum)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
@ -871,6 +902,24 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
||||
rb_insert_color(&src_line->node, root);
|
||||
}
|
||||
|
||||
static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root)
|
||||
{
|
||||
struct source_line *src_line;
|
||||
struct rb_node *node;
|
||||
|
||||
node = rb_first(src_root);
|
||||
while (node) {
|
||||
struct rb_node *next;
|
||||
|
||||
src_line = rb_entry(node, struct source_line, node);
|
||||
next = rb_next(node);
|
||||
rb_erase(node, src_root);
|
||||
|
||||
__resort_source_line(dest_root, src_line);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol__free_source_line(struct symbol *sym, int len)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
@ -895,6 +944,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
struct source_line *src_line;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
struct rb_root tmp_root = RB_ROOT;
|
||||
|
||||
if (!h->sum)
|
||||
return 0;
|
||||
@ -929,12 +979,13 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
goto next;
|
||||
|
||||
strcpy(src_line[i].path, path);
|
||||
insert_source_line(root, &src_line[i]);
|
||||
insert_source_line(&tmp_root, &src_line[i]);
|
||||
|
||||
next:
|
||||
pclose(fp);
|
||||
}
|
||||
|
||||
resort_source_line(root, &tmp_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -958,7 +1009,7 @@ static void print_summary(struct rb_root *root, const char *filename)
|
||||
char *path;
|
||||
|
||||
src_line = rb_entry(node, struct source_line, node);
|
||||
percent = src_line->percent;
|
||||
percent = src_line->percent_sum;
|
||||
color = get_percent_color(percent);
|
||||
path = src_line->path;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
#include "symbol.h"
|
||||
#include "hist.h"
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <pthread.h>
|
||||
@ -75,6 +76,7 @@ struct sym_hist {
|
||||
struct source_line {
|
||||
struct rb_node node;
|
||||
double percent;
|
||||
double percent_sum;
|
||||
char *path;
|
||||
};
|
||||
|
||||
@ -140,14 +142,13 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
struct hist_browser_timer *hbt);
|
||||
#else
|
||||
static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
struct map *map __maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
void(*timer)(void *arg) __maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int delay_secs __maybe_unused)
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "symbol.h"
|
||||
#include "dso.h"
|
||||
#include "machine.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -244,6 +244,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
he->ms.map->referenced = true;
|
||||
if (symbol_conf.use_callchain)
|
||||
callchain_init(he->callchain);
|
||||
|
||||
INIT_LIST_HEAD(&he->pairs.node);
|
||||
}
|
||||
|
||||
return he;
|
||||
@ -410,6 +412,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
|
||||
|
||||
void hist_entry__free(struct hist_entry *he)
|
||||
{
|
||||
free(he->branch_info);
|
||||
free(he);
|
||||
}
|
||||
|
||||
@ -713,3 +716,100 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
|
||||
++hists->stats.nr_events[0];
|
||||
++hists->stats.nr_events[type];
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
struct hist_entry *pair)
|
||||
{
|
||||
struct rb_node **p = &hists->entries.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *he;
|
||||
int cmp;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
he = rb_entry(parent, struct hist_entry, rb_node);
|
||||
|
||||
cmp = hist_entry__cmp(pair, he);
|
||||
|
||||
if (!cmp)
|
||||
goto out;
|
||||
|
||||
if (cmp < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
he = hist_entry__new(pair);
|
||||
if (he) {
|
||||
he->stat.nr_events = 0;
|
||||
he->stat.period = 0;
|
||||
he->hists = hists;
|
||||
rb_link_node(&he->rb_node, parent, p);
|
||||
rb_insert_color(&he->rb_node, &hists->entries);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
}
|
||||
out:
|
||||
return he;
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__find_entry(struct hists *hists,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node *n = hists->entries.rb_node;
|
||||
|
||||
while (n) {
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
|
||||
int64_t cmp = hist_entry__cmp(he, iter);
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
else if (cmp > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for pairs to link to the leader buckets (hist_entries):
|
||||
*/
|
||||
void hists__match(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
pair = hists__find_entry(other, pos);
|
||||
|
||||
if (pair)
|
||||
hist__entry_add_pair(pos, pair);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for entries in the other hists that are not present in the leader, if
|
||||
* we find them, just add a dummy entry on the leader hists, with period=0,
|
||||
* nr_events=0, to serve as the list header.
|
||||
*/
|
||||
int hists__link(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
|
||||
if (!hist_entry__has_pairs(pos)) {
|
||||
pair = hists__add_dummy_entry(leader, pos);
|
||||
if (pair == NULL)
|
||||
return -1;
|
||||
hist__entry_add_pair(pair, pos);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <pthread.h>
|
||||
#include "callchain.h"
|
||||
#include "header.h"
|
||||
|
||||
extern struct callchain_param callchain_param;
|
||||
|
||||
@ -114,6 +115,9 @@ bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
|
||||
void hists__reset_col_len(struct hists *hists);
|
||||
void hists__calc_col_len(struct hists *hists, struct hist_entry *he);
|
||||
|
||||
void hists__match(struct hists *leader, struct hists *other);
|
||||
int hists__link(struct hists *leader, struct hists *other);
|
||||
|
||||
struct perf_hpp {
|
||||
char *buf;
|
||||
size_t size;
|
||||
@ -157,22 +161,27 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
|
||||
struct perf_evlist;
|
||||
|
||||
struct hist_browser_timer {
|
||||
void (*timer)(void *arg);
|
||||
void *arg;
|
||||
int refresh;
|
||||
};
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
#include "../ui/keysyms.h"
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
struct hist_browser_timer *hbt);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env);
|
||||
int script_browse(const char *script_opt);
|
||||
#else
|
||||
static inline
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
const char *help __maybe_unused,
|
||||
void(*timer)(void *arg) __maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int refresh __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused,
|
||||
struct perf_session_env *env __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -180,10 +189,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
static inline int hist_entry__tui_annotate(struct hist_entry *self
|
||||
__maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
void(*timer)(void *arg)
|
||||
__maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int delay_secs __maybe_unused)
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -199,15 +206,12 @@ static inline int script_browse(const char *script_opt)
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
struct hist_browser_timer *hbt __maybe_unused);
|
||||
#else
|
||||
static inline
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
const char *help __maybe_unused,
|
||||
void(*timer)(void *arg) __maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int refresh __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,9 +2,192 @@
|
||||
#include "event.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "strlist.h"
|
||||
#include "thread.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
|
||||
{
|
||||
map_groups__init(&machine->kmaps);
|
||||
RB_CLEAR_NODE(&machine->rb_node);
|
||||
INIT_LIST_HEAD(&machine->user_dsos);
|
||||
INIT_LIST_HEAD(&machine->kernel_dsos);
|
||||
|
||||
machine->threads = RB_ROOT;
|
||||
INIT_LIST_HEAD(&machine->dead_threads);
|
||||
machine->last_match = NULL;
|
||||
|
||||
machine->kmaps.machine = machine;
|
||||
machine->pid = pid;
|
||||
|
||||
machine->root_dir = strdup(root_dir);
|
||||
if (machine->root_dir == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pid != HOST_KERNEL_ID) {
|
||||
struct thread *thread = machine__findnew_thread(machine, pid);
|
||||
char comm[64];
|
||||
|
||||
if (thread == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(comm, sizeof(comm), "[guest/%d]", pid);
|
||||
thread__set_comm(thread, comm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsos__delete(struct list_head *dsos)
|
||||
{
|
||||
struct dso *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, dsos, node) {
|
||||
list_del(&pos->node);
|
||||
dso__delete(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void machine__exit(struct machine *machine)
|
||||
{
|
||||
map_groups__exit(&machine->kmaps);
|
||||
dsos__delete(&machine->user_dsos);
|
||||
dsos__delete(&machine->kernel_dsos);
|
||||
free(machine->root_dir);
|
||||
machine->root_dir = NULL;
|
||||
}
|
||||
|
||||
void machine__delete(struct machine *machine)
|
||||
{
|
||||
machine__exit(machine);
|
||||
free(machine);
|
||||
}
|
||||
|
||||
struct machine *machines__add(struct rb_root *machines, pid_t pid,
|
||||
const char *root_dir)
|
||||
{
|
||||
struct rb_node **p = &machines->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct machine *pos, *machine = malloc(sizeof(*machine));
|
||||
|
||||
if (machine == NULL)
|
||||
return NULL;
|
||||
|
||||
if (machine__init(machine, root_dir, pid) != 0) {
|
||||
free(machine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
pos = rb_entry(parent, struct machine, rb_node);
|
||||
if (pid < pos->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&machine->rb_node, parent, p);
|
||||
rb_insert_color(&machine->rb_node, machines);
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
struct machine *machines__find(struct rb_root *machines, pid_t pid)
|
||||
{
|
||||
struct rb_node **p = &machines->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct machine *machine;
|
||||
struct machine *default_machine = NULL;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
machine = rb_entry(parent, struct machine, rb_node);
|
||||
if (pid < machine->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else if (pid > machine->pid)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return machine;
|
||||
if (!machine->pid)
|
||||
default_machine = machine;
|
||||
}
|
||||
|
||||
return default_machine;
|
||||
}
|
||||
|
||||
struct machine *machines__findnew(struct rb_root *machines, pid_t pid)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
const char *root_dir = "";
|
||||
struct machine *machine = machines__find(machines, pid);
|
||||
|
||||
if (machine && (machine->pid == pid))
|
||||
goto out;
|
||||
|
||||
if ((pid != HOST_KERNEL_ID) &&
|
||||
(pid != DEFAULT_GUEST_KERNEL_ID) &&
|
||||
(symbol_conf.guestmount)) {
|
||||
sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
|
||||
if (access(path, R_OK)) {
|
||||
static struct strlist *seen;
|
||||
|
||||
if (!seen)
|
||||
seen = strlist__new(true, NULL);
|
||||
|
||||
if (!strlist__has_entry(seen, path)) {
|
||||
pr_err("Can't access file %s\n", path);
|
||||
strlist__add(seen, path);
|
||||
}
|
||||
machine = NULL;
|
||||
goto out;
|
||||
}
|
||||
root_dir = path;
|
||||
}
|
||||
|
||||
machine = machines__add(machines, pid, root_dir);
|
||||
out:
|
||||
return machine;
|
||||
}
|
||||
|
||||
void machines__process(struct rb_root *machines,
|
||||
machine__process_t process, void *data)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(machines); nd; nd = rb_next(nd)) {
|
||||
struct machine *pos = rb_entry(nd, struct machine, rb_node);
|
||||
process(pos, data);
|
||||
}
|
||||
}
|
||||
|
||||
char *machine__mmap_name(struct machine *machine, char *bf, size_t size)
|
||||
{
|
||||
if (machine__is_host(machine))
|
||||
snprintf(bf, size, "[%s]", "kernel.kallsyms");
|
||||
else if (machine__is_default_guest(machine))
|
||||
snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
|
||||
else {
|
||||
snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms",
|
||||
machine->pid);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct machine *machine;
|
||||
|
||||
for (node = rb_first(machines); node; node = rb_next(node)) {
|
||||
machine = rb_entry(node, struct machine, rb_node);
|
||||
machine->id_hdr_size = id_hdr_size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid,
|
||||
bool create)
|
||||
{
|
||||
@ -84,15 +267,19 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
|
||||
static void machine__set_kernel_mmap_len(struct machine *machine,
|
||||
union perf_event *event)
|
||||
{
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->start = event->mmap.start;
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->end = (event->mmap.start +
|
||||
event->mmap.len);
|
||||
/*
|
||||
* Be a bit paranoid here, some perf.data file came with
|
||||
* a zero sized synthesized MMAP event for the kernel.
|
||||
*/
|
||||
if (machine->vmlinux_maps[MAP__FUNCTION]->end == 0)
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->end = ~0ULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAP__NR_TYPES; i++) {
|
||||
machine->vmlinux_maps[i]->start = event->mmap.start;
|
||||
machine->vmlinux_maps[i]->end = (event->mmap.start +
|
||||
event->mmap.len);
|
||||
/*
|
||||
* Be a bit paranoid here, some perf.data file came with
|
||||
* a zero sized synthesized MMAP event for the kernel.
|
||||
*/
|
||||
if (machine->vmlinux_maps[i]->end == 0)
|
||||
machine->vmlinux_maps[i]->end = ~0ULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int machine__process_kernel_mmap_event(struct machine *machine,
|
||||
|
@ -2,11 +2,40 @@
|
||||
#define __PERF_MACHINE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "map.h"
|
||||
|
||||
struct branch_stack;
|
||||
struct perf_evsel;
|
||||
struct perf_sample;
|
||||
struct symbol;
|
||||
struct thread;
|
||||
struct machine;
|
||||
union perf_event;
|
||||
|
||||
/* Native host kernel uses -1 as pid index in machine */
|
||||
#define HOST_KERNEL_ID (-1)
|
||||
#define DEFAULT_GUEST_KERNEL_ID (0)
|
||||
|
||||
struct machine {
|
||||
struct rb_node rb_node;
|
||||
pid_t pid;
|
||||
u16 id_hdr_size;
|
||||
char *root_dir;
|
||||
struct rb_root threads;
|
||||
struct list_head dead_threads;
|
||||
struct thread *last_match;
|
||||
struct list_head user_dsos;
|
||||
struct list_head kernel_dsos;
|
||||
struct map_groups kmaps;
|
||||
struct map *vmlinux_maps[MAP__NR_TYPES];
|
||||
};
|
||||
|
||||
static inline
|
||||
struct map *machine__kernel_map(struct machine *machine, enum map_type type)
|
||||
{
|
||||
return machine->vmlinux_maps[type];
|
||||
}
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t pid);
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event);
|
||||
@ -16,4 +45,104 @@ int machine__process_lost_event(struct machine *machine, union perf_event *event
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_event(struct machine *machine, union perf_event *event);
|
||||
|
||||
typedef void (*machine__process_t)(struct machine *machine, void *data);
|
||||
|
||||
void machines__process(struct rb_root *machines,
|
||||
machine__process_t process, void *data);
|
||||
|
||||
struct machine *machines__add(struct rb_root *machines, pid_t pid,
|
||||
const char *root_dir);
|
||||
struct machine *machines__find_host(struct rb_root *machines);
|
||||
struct machine *machines__find(struct rb_root *machines, pid_t pid);
|
||||
struct machine *machines__findnew(struct rb_root *machines, pid_t pid);
|
||||
|
||||
void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size);
|
||||
char *machine__mmap_name(struct machine *machine, char *bf, size_t size);
|
||||
|
||||
int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
|
||||
void machine__exit(struct machine *machine);
|
||||
void machine__delete(struct machine *machine);
|
||||
|
||||
|
||||
struct branch_info *machine__resolve_bstack(struct machine *machine,
|
||||
struct thread *thread,
|
||||
struct branch_stack *bs);
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent);
|
||||
|
||||
/*
|
||||
* Default guest kernel is defined by parameter --guestkallsyms
|
||||
* and --guestmodules
|
||||
*/
|
||||
static inline bool machine__is_default_guest(struct machine *machine)
|
||||
{
|
||||
return machine ? machine->pid == DEFAULT_GUEST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
static inline bool machine__is_host(struct machine *machine)
|
||||
{
|
||||
return machine ? machine->pid == HOST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid);
|
||||
void machine__remove_thread(struct machine *machine, struct thread *th);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_symbol(struct machine *machine,
|
||||
enum map_type type, u64 addr,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return map_groups__find_symbol(&machine->kmaps, type, addr,
|
||||
mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return machine__find_kernel_symbol(machine, MAP__FUNCTION, addr,
|
||||
mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_function_by_name(struct machine *machine,
|
||||
const char *name,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return map_groups__find_function_by_name(&machine->kmaps, name, mapp,
|
||||
filter);
|
||||
}
|
||||
|
||||
struct map *machine__new_module(struct machine *machine, u64 start,
|
||||
const char *filename);
|
||||
|
||||
int machine__load_kallsyms(struct machine *machine, const char *filename,
|
||||
enum map_type type, symbol_filter_t filter);
|
||||
int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
|
||||
symbol_filter_t filter);
|
||||
|
||||
size_t machine__fprintf_dsos_buildid(struct machine *machine,
|
||||
FILE *fp, bool with_hits);
|
||||
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp);
|
||||
size_t machines__fprintf_dsos_buildid(struct rb_root *machines,
|
||||
FILE *fp, bool with_hits);
|
||||
|
||||
void machine__destroy_kernel_maps(struct machine *machine);
|
||||
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel);
|
||||
int machine__create_kernel_maps(struct machine *machine);
|
||||
|
||||
int machines__create_kernel_maps(struct rb_root *machines, pid_t pid);
|
||||
int machines__create_guest_kernel_maps(struct rb_root *machines);
|
||||
void machines__destroy_guest_kernel_maps(struct rb_root *machines);
|
||||
|
||||
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
|
||||
|
||||
#endif /* __PERF_MACHINE_H */
|
||||
|
@ -24,7 +24,7 @@ static inline int is_anon_memory(const char *filename)
|
||||
|
||||
static inline int is_no_dso_memory(const char *filename)
|
||||
{
|
||||
return !strcmp(filename, "[stack]") ||
|
||||
return !strncmp(filename, "[stack", 6) ||
|
||||
!strcmp(filename, "[heap]");
|
||||
}
|
||||
|
||||
@ -590,182 +590,3 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int machine__init(struct machine *self, const char *root_dir, pid_t pid)
|
||||
{
|
||||
map_groups__init(&self->kmaps);
|
||||
RB_CLEAR_NODE(&self->rb_node);
|
||||
INIT_LIST_HEAD(&self->user_dsos);
|
||||
INIT_LIST_HEAD(&self->kernel_dsos);
|
||||
|
||||
self->threads = RB_ROOT;
|
||||
INIT_LIST_HEAD(&self->dead_threads);
|
||||
self->last_match = NULL;
|
||||
|
||||
self->kmaps.machine = self;
|
||||
self->pid = pid;
|
||||
self->root_dir = strdup(root_dir);
|
||||
if (self->root_dir == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pid != HOST_KERNEL_ID) {
|
||||
struct thread *thread = machine__findnew_thread(self, pid);
|
||||
char comm[64];
|
||||
|
||||
if (thread == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(comm, sizeof(comm), "[guest/%d]", pid);
|
||||
thread__set_comm(thread, comm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsos__delete(struct list_head *self)
|
||||
{
|
||||
struct dso *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, self, node) {
|
||||
list_del(&pos->node);
|
||||
dso__delete(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void machine__exit(struct machine *self)
|
||||
{
|
||||
map_groups__exit(&self->kmaps);
|
||||
dsos__delete(&self->user_dsos);
|
||||
dsos__delete(&self->kernel_dsos);
|
||||
free(self->root_dir);
|
||||
self->root_dir = NULL;
|
||||
}
|
||||
|
||||
void machine__delete(struct machine *self)
|
||||
{
|
||||
machine__exit(self);
|
||||
free(self);
|
||||
}
|
||||
|
||||
struct machine *machines__add(struct rb_root *self, pid_t pid,
|
||||
const char *root_dir)
|
||||
{
|
||||
struct rb_node **p = &self->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct machine *pos, *machine = malloc(sizeof(*machine));
|
||||
|
||||
if (!machine)
|
||||
return NULL;
|
||||
|
||||
if (machine__init(machine, root_dir, pid) != 0) {
|
||||
free(machine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
pos = rb_entry(parent, struct machine, rb_node);
|
||||
if (pid < pos->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&machine->rb_node, parent, p);
|
||||
rb_insert_color(&machine->rb_node, self);
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
struct machine *machines__find(struct rb_root *self, pid_t pid)
|
||||
{
|
||||
struct rb_node **p = &self->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct machine *machine;
|
||||
struct machine *default_machine = NULL;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
machine = rb_entry(parent, struct machine, rb_node);
|
||||
if (pid < machine->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else if (pid > machine->pid)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return machine;
|
||||
if (!machine->pid)
|
||||
default_machine = machine;
|
||||
}
|
||||
|
||||
return default_machine;
|
||||
}
|
||||
|
||||
struct machine *machines__findnew(struct rb_root *self, pid_t pid)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
const char *root_dir = "";
|
||||
struct machine *machine = machines__find(self, pid);
|
||||
|
||||
if (machine && (machine->pid == pid))
|
||||
goto out;
|
||||
|
||||
if ((pid != HOST_KERNEL_ID) &&
|
||||
(pid != DEFAULT_GUEST_KERNEL_ID) &&
|
||||
(symbol_conf.guestmount)) {
|
||||
sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
|
||||
if (access(path, R_OK)) {
|
||||
static struct strlist *seen;
|
||||
|
||||
if (!seen)
|
||||
seen = strlist__new(true, NULL);
|
||||
|
||||
if (!strlist__has_entry(seen, path)) {
|
||||
pr_err("Can't access file %s\n", path);
|
||||
strlist__add(seen, path);
|
||||
}
|
||||
machine = NULL;
|
||||
goto out;
|
||||
}
|
||||
root_dir = path;
|
||||
}
|
||||
|
||||
machine = machines__add(self, pid, root_dir);
|
||||
|
||||
out:
|
||||
return machine;
|
||||
}
|
||||
|
||||
void machines__process(struct rb_root *self, machine__process_t process, void *data)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(self); nd; nd = rb_next(nd)) {
|
||||
struct machine *pos = rb_entry(nd, struct machine, rb_node);
|
||||
process(pos, data);
|
||||
}
|
||||
}
|
||||
|
||||
char *machine__mmap_name(struct machine *self, char *bf, size_t size)
|
||||
{
|
||||
if (machine__is_host(self))
|
||||
snprintf(bf, size, "[%s]", "kernel.kallsyms");
|
||||
else if (machine__is_default_guest(self))
|
||||
snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
|
||||
else
|
||||
snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct machine *machine;
|
||||
|
||||
for (node = rb_first(machines); node; node = rb_next(node)) {
|
||||
machine = rb_entry(node, struct machine, rb_node);
|
||||
machine->id_hdr_size = id_hdr_size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -57,30 +57,6 @@ struct map_groups {
|
||||
struct machine *machine;
|
||||
};
|
||||
|
||||
/* Native host kernel uses -1 as pid index in machine */
|
||||
#define HOST_KERNEL_ID (-1)
|
||||
#define DEFAULT_GUEST_KERNEL_ID (0)
|
||||
|
||||
struct machine {
|
||||
struct rb_node rb_node;
|
||||
pid_t pid;
|
||||
u16 id_hdr_size;
|
||||
char *root_dir;
|
||||
struct rb_root threads;
|
||||
struct list_head dead_threads;
|
||||
struct thread *last_match;
|
||||
struct list_head user_dsos;
|
||||
struct list_head kernel_dsos;
|
||||
struct map_groups kmaps;
|
||||
struct map *vmlinux_maps[MAP__NR_TYPES];
|
||||
};
|
||||
|
||||
static inline
|
||||
struct map *machine__kernel_map(struct machine *self, enum map_type type)
|
||||
{
|
||||
return self->vmlinux_maps[type];
|
||||
}
|
||||
|
||||
static inline struct kmap *map__kmap(struct map *self)
|
||||
{
|
||||
return (struct kmap *)(self + 1);
|
||||
@ -143,44 +119,9 @@ int map_groups__clone(struct map_groups *mg,
|
||||
size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp);
|
||||
size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp);
|
||||
|
||||
typedef void (*machine__process_t)(struct machine *self, void *data);
|
||||
|
||||
void machines__process(struct rb_root *self, machine__process_t process, void *data);
|
||||
struct machine *machines__add(struct rb_root *self, pid_t pid,
|
||||
const char *root_dir);
|
||||
struct machine *machines__find_host(struct rb_root *self);
|
||||
struct machine *machines__find(struct rb_root *self, pid_t pid);
|
||||
struct machine *machines__findnew(struct rb_root *self, pid_t pid);
|
||||
void machines__set_id_hdr_size(struct rb_root *self, u16 id_hdr_size);
|
||||
char *machine__mmap_name(struct machine *self, char *bf, size_t size);
|
||||
int machine__init(struct machine *self, const char *root_dir, pid_t pid);
|
||||
void machine__exit(struct machine *self);
|
||||
void machine__delete(struct machine *self);
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_sample;
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent);
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
|
||||
u64 addr);
|
||||
|
||||
/*
|
||||
* Default guest kernel is defined by parameter --guestkallsyms
|
||||
* and --guestmodules
|
||||
*/
|
||||
static inline bool machine__is_default_guest(struct machine *self)
|
||||
{
|
||||
return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
static inline bool machine__is_host(struct machine *self)
|
||||
{
|
||||
return self ? self->pid == HOST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
static inline void map_groups__insert(struct map_groups *mg, struct map *map)
|
||||
{
|
||||
maps__insert(&mg->maps[map->type], map);
|
||||
@ -209,29 +150,6 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter);
|
||||
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid);
|
||||
void machine__remove_thread(struct machine *machine, struct thread *th);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_symbol(struct machine *self,
|
||||
enum map_type type, u64 addr,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *map_groups__find_function_by_name(struct map_groups *mg,
|
||||
const char *name, struct map **mapp,
|
||||
@ -240,22 +158,11 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *mg,
|
||||
return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_function_by_name(struct machine *self,
|
||||
const char *name,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return map_groups__find_function_by_name(&self->kmaps, name, mapp,
|
||||
filter);
|
||||
}
|
||||
|
||||
int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
|
||||
int verbose, FILE *fp);
|
||||
|
||||
struct map *map_groups__find_by_name(struct map_groups *mg,
|
||||
enum map_type type, const char *name);
|
||||
struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
|
||||
|
||||
void map_groups__flush(struct map_groups *mg);
|
||||
|
||||
|
@ -81,6 +81,7 @@ num_dec [0-9]+
|
||||
num_hex 0x[a-fA-F0-9]+
|
||||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
||||
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
|
||||
modifier_event [ukhpGH]{1,8}
|
||||
modifier_bp [rwx]{1,3}
|
||||
|
||||
@ -168,6 +169,7 @@ period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
, { return ','; }
|
||||
"/" { BEGIN(INITIAL); return '/'; }
|
||||
{name_minus} { return str(yyscanner, PE_NAME); }
|
||||
}
|
||||
|
||||
mem: { BEGIN(mem); return PE_PREFIX_MEM; }
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "hist.h"
|
||||
#include "event.h"
|
||||
#include "header.h"
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include <linux/rbtree.h>
|
||||
@ -68,10 +69,6 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
|
||||
struct branch_info *machine__resolve_bstack(struct machine *self,
|
||||
struct thread *thread,
|
||||
struct branch_stack *bs);
|
||||
|
||||
bool perf_session__has_traces(struct perf_session *self, const char *msg);
|
||||
|
||||
void mem_bswap_64(void *src, int byte_size);
|
||||
|
@ -77,6 +77,10 @@ struct hist_entry_diff {
|
||||
struct hist_entry {
|
||||
struct rb_node rb_node_in;
|
||||
struct rb_node rb_node;
|
||||
union {
|
||||
struct list_head node;
|
||||
struct list_head head;
|
||||
} pairs;
|
||||
struct he_stat stat;
|
||||
struct map_symbol ms;
|
||||
struct thread *thread;
|
||||
@ -96,15 +100,30 @@ struct hist_entry {
|
||||
char *srcline;
|
||||
struct symbol *parent;
|
||||
unsigned long position;
|
||||
union {
|
||||
struct hist_entry *pair;
|
||||
struct rb_root sorted_chain;
|
||||
};
|
||||
struct rb_root sorted_chain;
|
||||
struct branch_info *branch_info;
|
||||
struct hists *hists;
|
||||
struct callchain_root callchain[0];
|
||||
};
|
||||
|
||||
static inline bool hist_entry__has_pairs(struct hist_entry *he)
|
||||
{
|
||||
return !list_empty(&he->pairs.node);
|
||||
}
|
||||
|
||||
static inline struct hist_entry *hist_entry__next_pair(struct hist_entry *he)
|
||||
{
|
||||
if (hist_entry__has_pairs(he))
|
||||
return list_entry(he->pairs.node.next, struct hist_entry, pairs.node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void hist__entry_add_pair(struct hist_entry *he,
|
||||
struct hist_entry *pair)
|
||||
{
|
||||
list_add_tail(&he->pairs.head, &pair->pairs.node);
|
||||
}
|
||||
|
||||
enum sort_type {
|
||||
SORT_PID,
|
||||
SORT_COMM,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "build-id.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
#include "strlist.h"
|
||||
|
||||
|
@ -200,16 +200,6 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
|
||||
symbol_filter_t filter);
|
||||
int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map,
|
||||
symbol_filter_t filter);
|
||||
int machine__load_kallsyms(struct machine *machine, const char *filename,
|
||||
enum map_type type, symbol_filter_t filter);
|
||||
int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
|
||||
symbol_filter_t filter);
|
||||
|
||||
size_t machine__fprintf_dsos_buildid(struct machine *machine,
|
||||
FILE *fp, bool with_hits);
|
||||
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp);
|
||||
size_t machines__fprintf_dsos_buildid(struct rb_root *machines,
|
||||
FILE *fp, bool with_hits);
|
||||
|
||||
struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
|
||||
u64 addr);
|
||||
@ -224,14 +214,6 @@ int kallsyms__parse(const char *filename, void *arg,
|
||||
int filename__read_debuglink(const char *filename, char *debuglink,
|
||||
size_t size);
|
||||
|
||||
void machine__destroy_kernel_maps(struct machine *machine);
|
||||
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel);
|
||||
int machine__create_kernel_maps(struct machine *machine);
|
||||
|
||||
int machines__create_kernel_maps(struct rb_root *machines, pid_t pid);
|
||||
int machines__create_guest_kernel_maps(struct rb_root *machines);
|
||||
void machines__destroy_guest_kernel_maps(struct rb_root *machines);
|
||||
|
||||
int symbol__init(void);
|
||||
void symbol__exit(void);
|
||||
void symbol__elf_init(void);
|
||||
@ -242,8 +224,6 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp);
|
||||
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
|
||||
|
||||
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
|
||||
|
||||
int dso__test_data(void);
|
||||
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
||||
struct symsrc *runtime_ss, symbol_filter_t filter,
|
||||
|
Loading…
Reference in New Issue
Block a user