On Tue, May 24, 2016 at 09:20:27AM +0000, He Kuang wrote: > Use thread specific unwind ops to unwind cross-platform callchains. > > Currently, unwind methods is suitable for local unwind, this patch > changes the fixed methods to thread/map related. Each time a map is > inserted, we find the target arch and see if this platform can be > remote unwind. We test for x86 platform and only show proper > messages. The real unwind methods are not implemented, will be > introduced in next patch. > > CONFIG_LIBUNWIND/NO_LIBUNWIND are changed to > CONFIG_LOCAL_LIBUNWIND/NO_LOCAL_LIBUNWIND for retaining local unwind > features. CONFIG_LIBUNWIND stands for either local or remote or both > unwind are supported and NO_LIBUNWIND means neither local nor remote > libunwind are supported. > > Signed-off-by: He Kuang <heku...@huawei.com> > --- > tools/perf/arch/x86/util/Build | 2 +- > tools/perf/config/Makefile | 23 +++++++++- > tools/perf/util/Build | 2 +- > tools/perf/util/thread.c | 5 +-- > tools/perf/util/thread.h | 17 ++++++-- > tools/perf/util/unwind-libunwind.c | 49 +++++++++++++++++---- > tools/perf/util/unwind-libunwind_common.c | 71 > +++++++++++++++++++++++++++++-- > tools/perf/util/unwind.h | 37 +++++++++++----- > 8 files changed, 173 insertions(+), 33 deletions(-) > > diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build > index 4659703..bc24b75 100644 > --- a/tools/perf/arch/x86/util/Build > +++ b/tools/perf/arch/x86/util/Build > @@ -7,7 +7,7 @@ libperf-y += perf_regs.o > libperf-$(CONFIG_DWARF) += dwarf-regs.o > libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o > > -libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o > +libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o > libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o > > libperf-$(CONFIG_AUXTRACE) += auxtrace.o > diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile > index c9e1625..8ac0440 100644 > --- a/tools/perf/config/Makefile > +++ b/tools/perf/config/Makefile > @@ -354,15 +354,31 @@ ifeq ($(ARCH),powerpc) > endif > > ifndef NO_LIBUNWIND > + have_libunwind = > ifeq ($(feature-libunwind-x86), 1) > $(call detected,CONFIG_LIBUNWIND_X86) > CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT > + LDFLAGS += -lunwind-x86 > + have_libunwind = 1 > endif > > ifneq ($(feature-libunwind), 1) > msg := $(warning No libunwind found. Please install libunwind-dev[el] >= > 1.1 and/or set LIBUNWIND_DIR); > + NO_LOCAL_LIBUNWIND := 1 > + else > + have_libunwind = 1 > + CFLAGS += -DHAVE_LIBUNWIND_LOCAL_SUPPORT > + $(call detected,CONFIG_LOCAL_LIBUNWIND) > + endif > + > + ifneq ($(have_libunwind), 1) > NO_LIBUNWIND := 1 > + else > + CFLAGS += -I$(LIBUNWIND_DIR)/include > + LDFLAGS += -L$(LIBUNWIND_DIR)/lib > endif > +else > + NO_LOCAL_LIBUNWIND := 1 > endif > > ifndef NO_LIBBPF > @@ -400,7 +416,7 @@ else > NO_DWARF_UNWIND := 1 > endif > > -ifndef NO_LIBUNWIND > +ifndef NO_LOCAL_LIBUNWIND > ifeq ($(ARCH),$(filter $(ARCH),arm arm64)) > $(call feature_check,libunwind-debug-frame) > ifneq ($(feature-libunwind-debug-frame), 1) > @@ -411,12 +427,15 @@ ifndef NO_LIBUNWIND > # non-ARM has no dwarf_find_debug_frame() function: > CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME > endif > - CFLAGS += -DHAVE_LIBUNWIND_SUPPORT > EXTLIBS += $(LIBUNWIND_LIBS) > CFLAGS += $(LIBUNWIND_CFLAGS) > LDFLAGS += $(LIBUNWIND_LDFLAGS) > endif > > +ifndef NO_LIBUNWIND > + CFLAGS += -DHAVE_LIBUNWIND_SUPPORT > +endif > + > ifndef NO_LIBAUDIT > ifneq ($(feature-libaudit), 1) > msg := $(warning No libaudit.h found, disables 'trace' tool, please > install audit-libs-devel or libaudit-dev); > diff --git a/tools/perf/util/Build b/tools/perf/util/Build > index 25c31fb..ce69721 100644 > --- a/tools/perf/util/Build > +++ b/tools/perf/util/Build > @@ -99,7 +99,7 @@ libperf-$(CONFIG_DWARF) += probe-finder.o > libperf-$(CONFIG_DWARF) += dwarf-aux.o > > libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o > -libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o > +libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o > libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o > > libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o > diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c > index 3043113..4e1aaf5 100644 > --- a/tools/perf/util/thread.c > +++ b/tools/perf/util/thread.c > @@ -43,9 +43,6 @@ struct thread *thread__new(pid_t pid, pid_t tid) > thread->cpu = -1; > INIT_LIST_HEAD(&thread->comm_list); > > - if (unwind__prepare_access(thread) < 0) > - goto err_thread; > - > comm_str = malloc(32); > if (!comm_str) > goto err_thread; > @@ -59,6 +56,8 @@ struct thread *thread__new(pid_t pid, pid_t tid) > list_add(&comm->list, &thread->comm_list); > atomic_set(&thread->refcnt, 1); > RB_CLEAR_NODE(&thread->rb_node); > + > + register_null_unwind_libunwind_ops(thread); > } > > return thread; > diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h > index 45fba13..647b011 100644 > --- a/tools/perf/util/thread.h > +++ b/tools/perf/util/thread.h > @@ -9,12 +9,20 @@ > #include "symbol.h" > #include <strlist.h> > #include <intlist.h> > -#ifdef HAVE_LIBUNWIND_SUPPORT > -#include <libunwind.h> > -#endif > > struct thread_stack; > > +struct unwind_entry; > +typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); > +struct unwind_libunwind_ops { > + int (*prepare_access)(struct thread *thread); > + void (*flush_access)(struct thread *thread); > + void (*finish_access)(struct thread *thread); > + int (*get_entries)(unwind_entry_cb_t cb, void *arg, > + struct thread *thread, > + struct perf_sample *data, int max_stack); > +}; > + > struct thread { > union { > struct rb_node rb_node; > @@ -36,7 +44,8 @@ struct thread { > void *priv; > struct thread_stack *ts; > #ifdef HAVE_LIBUNWIND_SUPPORT > - unw_addr_space_t addr_space; > + void *addr_space; > + struct unwind_libunwind_ops *unwind_libunwind_ops; > #endif > }; > > diff --git a/tools/perf/util/unwind-libunwind.c > b/tools/perf/util/unwind-libunwind.c > index 63687d3..a6ec587 100644 > --- a/tools/perf/util/unwind-libunwind.c > +++ b/tools/perf/util/unwind-libunwind.c > @@ -22,8 +22,11 @@ > #include <unistd.h> > #include <sys/mman.h> > #include <linux/list.h> > +#ifdef REMOTE_UNWIND_LIBUNWIND > +#include "libunwind-arch.h" > +#else > #include <libunwind.h> > -#include <libunwind-ptrace.h> > +#endif > #include "callchain.h" > #include "thread.h" > #include "session.h" > @@ -34,6 +37,20 @@ > #include "debug.h" > #include "asm/bug.h" > > +#ifndef REMOTE_UNWIND_LIBUNWIND > + #define LOCAL_UNWIND_LIBUNWIND > + #undef UNWT_OBJ > + #define UNWT_OBJ(x) _##x > +#else > + #undef NO_LIBUNWIND_DEBUG_FRAME > + #if defined(LIBUNWIND_ARM) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM) > + #elif defined(LIBUNWIND_AARCH64) && \ > + defined(NO_LIBUNWIND_DEBUG_FRAME_ARM_AARCH64) > + #else > + #define NO_LIBUNWIND_DEBUG_FRAME > + #endif > +#endif > + > extern int > UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, > unw_word_t ip, > @@ -508,7 +525,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as, > return 0; > } > > - id = libunwind__arch_reg_id(regnum); > + id = LIBUNWIND__ARCH_REG_ID(regnum); > if (id < 0) > return -EINVAL; > > @@ -579,7 +596,7 @@ static unw_accessors_t accessors = { > .get_proc_name = get_proc_name, > }; > > -int unwind__prepare_access(struct thread *thread) > +static int UNWT_OBJ(_unwind__prepare_access)(struct thread *thread) > { > if (callchain_param.record_mode != CALLCHAIN_DWARF) > return 0; > @@ -594,7 +611,7 @@ int unwind__prepare_access(struct thread *thread) > return 0; > } > > -void unwind__flush_access(struct thread *thread) > +static void UNWT_OBJ(_unwind__flush_access)(struct thread *thread) > { > if (callchain_param.record_mode != CALLCHAIN_DWARF) > return; > @@ -602,7 +619,7 @@ void unwind__flush_access(struct thread *thread) > unw_flush_cache(thread->addr_space, 0, 0); > } > > -void unwind__finish_access(struct thread *thread) > +static void UNWT_OBJ(_unwind__finish_access)(struct thread *thread) > { > if (callchain_param.record_mode != CALLCHAIN_DWARF) > return; > @@ -662,9 +679,10 @@ static int get_entries(struct unwind_info *ui, > unwind_entry_cb_t cb, > return ret; > } > > -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, > - struct thread *thread, > - struct perf_sample *data, int max_stack) > +static int UNWT_OBJ(_unwind__get_entries)(unwind_entry_cb_t cb, void *arg, > + struct thread *thread, > + struct perf_sample *data, > + int max_stack) > { > struct unwind_info ui = { > .sample = data, > @@ -680,3 +698,18 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, > > return get_entries(&ui, cb, arg, max_stack); > } > + > +struct unwind_libunwind_ops > +UNWT_OBJ(unwind_libunwind_ops) = { > + .prepare_access = UNWT_OBJ(_unwind__prepare_access), > + .flush_access = UNWT_OBJ(_unwind__flush_access), > + .finish_access = UNWT_OBJ(_unwind__finish_access), > + .get_entries = UNWT_OBJ(_unwind__get_entries), > +}; > + > +#ifdef LOCAL_UNWIND_LIBUNWIND > +void register_local_unwind_libunwind_ops(struct thread *thread) > +{ > + thread->unwind_libunwind_ops = &UNWT_OBJ(unwind_libunwind_ops); > +} > +#endif > diff --git a/tools/perf/util/unwind-libunwind_common.c > b/tools/perf/util/unwind-libunwind_common.c > index 3946c99..f44833b 100644 > --- a/tools/perf/util/unwind-libunwind_common.c > +++ b/tools/perf/util/unwind-libunwind_common.c > @@ -5,10 +5,64 @@ > #include "debug.h" > #include "arch/common.h" > > +static int __null__prepare_access(struct thread *thread __maybe_unused) > +{ > + return 0; > +} > + > +static void __null__flush_access(struct thread *thread __maybe_unused) > +{ > +} > + > +static void __null__finish_access(struct thread *thread __maybe_unused) > +{ > +} > + > +static int __null__get_entries(unwind_entry_cb_t cb __maybe_unused, > + void *arg __maybe_unused, > + struct thread *thread __maybe_unused, > + struct perf_sample *data __maybe_unused, > + int max_stack __maybe_unused) > +{ > + return 0; > +} > + > +static struct unwind_libunwind_ops null_unwind_libunwind_ops = { > + .prepare_access = __null__prepare_access, > + .flush_access = __null__flush_access, > + .finish_access = __null__finish_access, > + .get_entries = __null__get_entries, > +}; > + > +void register_null_unwind_libunwind_ops(struct thread *thread) > +{ > + thread->unwind_libunwind_ops = &null_unwind_libunwind_ops; > + if (thread->mg) > + pr_err("unwind: target platform=%s unwind unsupported\n", > + thread->mg->machine->env->arch); > +} > + > +void register_unwind_libunwind_ops(struct unwind_libunwind_ops *ops, > + struct thread *thread) > +{ > + thread->unwind_libunwind_ops = ops; > +} > + > +void unwind__flush_access(struct thread *thread) > +{ > + thread->unwind_libunwind_ops->flush_access(thread); > +} > + > +void unwind__finish_access(struct thread *thread) > +{ > + thread->unwind_libunwind_ops->finish_access(thread); > +} > + > void unwind__get_arch(struct thread *thread, struct map *map) > { > const char *arch; > enum dso_type dso_type; > + int use_local_unwind = 1; > > if (!thread->mg->machine->env) > return; > @@ -17,18 +71,27 @@ void unwind__get_arch(struct thread *thread, struct map > *map) > if (dso_type == DSO__TYPE_UNKNOWN) > return; > > - if (thread->addr_space) > + if (thread->addr_space) { > pr_debug("unwind: thread map already set, 64bit is %d, > dso=%s\n", > dso_type == DSO__TYPE_64BIT, map->dso->name); > + return; > + } > > arch = normalize_arch(thread->mg->machine->env->arch); > > if (!strcmp(arch, "x86")) { > - if (dso_type != DSO__TYPE_64BIT) > + if (dso_type != DSO__TYPE_64BIT) { > #ifdef HAVE_LIBUNWIND_X86_SUPPORT > pr_err("unwind: target platform=%s is not > implemented\n", arch); > -#else > - pr_err("unwind: target platform=%s is not supported\n", > arch); > #endif > + register_null_unwind_libunwind_ops(thread); > + use_local_unwind = 0; > + } > } > + > + if (use_local_unwind) > + register_local_unwind_libunwind_ops(thread); > + > + if (thread->unwind_libunwind_ops->prepare_access(thread) < 0) > + return; > } > diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h > index 889d630..e170be7 100644 > --- a/tools/perf/util/unwind.h > +++ b/tools/perf/util/unwind.h > @@ -15,26 +15,46 @@ struct unwind_entry { > typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); > > #ifdef HAVE_DWARF_UNWIND_SUPPORT > + > +#ifndef LIBUNWIND__ARCH_REG_ID > +#define LIBUNWIND__ARCH_REG_ID libunwind__arch_reg_id > +#endif > + > int unwind__get_entries(unwind_entry_cb_t cb, void *arg, > struct thread *thread, > struct perf_sample *data, int max_stack); > /* libunwind specific */ > #ifdef HAVE_LIBUNWIND_SUPPORT > int libunwind__arch_reg_id(int regnum); > -int unwind__prepare_access(struct thread *thread); > void unwind__flush_access(struct thread *thread); > void unwind__finish_access(struct thread *thread); > void unwind__get_arch(struct thread *thread, struct map *map); > -#else > -static inline int unwind__prepare_access(struct thread *thread > __maybe_unused) > -{ > - return 0; > +void register_unwind_libunwind_ops(struct unwind_libunwind_ops *ops, > + struct thread *thread); > +void register_null_unwind_libunwind_ops(struct thread *thread); > + > +#ifndef HAVE_LIBUNWIND_LOCAL_SUPPORT > +static inline void > +register_local_unwind_libunwind_ops(struct thread *thread) { > + register_null_unwind_libunwind_ops(thread); > } > +#else > +void register_local_unwind_libunwind_ops(struct thread *thread); > +#endif > + > +#define unwind__get_entries(cb, arg, \ > + thread, \ > + data, max_stack) \ > + thread->unwind_libunwind_ops->get_entries(cb, arg, thread, \ > + data, max_stack)
I think this should go along with other ops wrappers like unwind__flush_access/unwind__finish_access.. in unwind-libunwind_common.c jirka