Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH 8/8] perf callchain: Support aarch64 cross-platform

274 views
Skip to first unread message

He Kuang

unread,
May 6, 2016, 5:10:08 AM5/6/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 ++++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 4 ++++
tools/perf/util/thread.c | 12 ++++++++++++
tools/perf/util/unwind.h | 3 +++
6 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..7bc8a00
--- /dev/null
+++ b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-aarch64.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_AARCH64
+int libunwind__aarch64_reg_id(int regnum);
+
+#include <../../../arm64/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__aarch64_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, aarch64), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..5b557a5 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,14 @@

#include <errno.h>
-#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"

+#ifndef LIBUNWIND_AARCH64
int libunwind__arch_reg_id(int regnum)
+#else
+int libunwind__aarch64_reg_id(int regnum)
+#endif
{
switch (regnum) {
case UNW_AARCH64_X0:
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 16f14b1..c1bfc5e 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -353,6 +353,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2dd3939..10e42ad 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -102,10 +102,14 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
$(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

+$(OUTPUT)util/unwind-libunwind_arm64.o: util/unwind-libunwind.c arch/arm64/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/arm64/include/libunwind -c -o $@ util/unwind-libunwind.c
+
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

libperf-y += scripting-engines/
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 2b93856..ba3e597 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -221,6 +221,18 @@ void thread__insert_map(struct thread *thread, struct map *map)
goto err;
#endif
}
+ } else if (!strcmp(arch, "aarch64") || !strncmp(arch, "arm", 3)) {
+ pr_debug("Thread map is ARM, 64bit is %d, dso=%s\n",
+ is_64_bit, map->dso->name);
+ if (is_64_bit) {
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ register_unwind_libunwind_ops(
+ &_Uaarch64_unwind_libunwind_ops, thread);
+#else
+ register_null_unwind_libunwind_ops(thread);
+ goto err;
+#endif
+ }
} else {
register_local_unwind_libunwind_ops(thread);
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 98d40bd..d215717 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -59,6 +59,9 @@ static inline void unwind__finish_access(struct thread *thread __maybe_unused) {
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
extern struct unwind_libunwind_ops _Ux86_unwind_libunwind_ops;
#endif
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+extern struct unwind_libunwind_ops _Uaarch64_unwind_libunwind_ops;
+#endif

#else
static inline int
--
1.8.5.2

He Kuang

unread,
May 6, 2016, 5:10:09 AM5/6/16
to
There's no need for dwarf support when perf recording with callchain.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/util.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index b7766c5..e5ebfd4 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -471,7 +471,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
"needed for --call-graph fp\n");
break;

-#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
@@ -487,7 +486,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
ret = get_stack_size(tok, &size);
param->dump_size = size;
}
-#endif /* HAVE_DWARF_UNWIND_SUPPORT */
} else if (!strncmp(name, "lbr", sizeof("lbr"))) {
if (!strtok_r(NULL, ",", &saveptr)) {
param->record_mode = CALLCHAIN_LBR;
--
1.8.5.2

He Kuang

unread,
May 6, 2016, 5:10:10 AM5/6/16
to
Currently, perf script uses host unwind methods to parse perf.data
callchain info regardless of the target architecture. So we get wrong
result and no promotion when do remote unwind on other
platforms/machines.

This patch set adds build tests for the supported platforms for remote
unwinding, and checks the map elf info for each thread, use remote
unwind methods instead.

Only x86 and aarch64 is added in this patch set to show the work flow,
other platforms can be added easily.

We can see the right result for unwind infos on different machines,
for example: we record perf.data on i686 qemu with '-g' option and
parse it on x86_64 machine.

before this patchset:

hello 1071 [000] 417.567832: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b77c8ba9 [unknown] ([vdso32])

after:

hello 1071 [000] 417.567832: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b77c8ba9 [unknown] ([vdso32])
b76e51cc close (/lib/libc-2.22.so)
804842e fib (/tmp/hello)
804849d main (/tmp/hello)
b762546e __libc_start_main (/lib/libc-2.22.so)
8048341 _start (/tmp/hello)

Thanks, discussion welcomed.

He Kuang (8):
perf tools: Omit DWARF judgement when recording dwarf callchain
perf script: Add options for custom vdso name
perf build: Add build-test for libunwind cross-platforms support
perf build: Add build-test for debug-frame on arm/arm64
perf tools: Promote proper messages for cross-platform unwind
perf callchain: Add support for cross-platform unwind
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

tools/build/Makefile.feature | 11 +++-
tools/build/feature/Makefile | 26 +++++++-
tools/build/feature/test-libunwind-aarch64.c | 26 ++++++++
tools/build/feature/test-libunwind-arm.c | 27 +++++++++
.../feature/test-libunwind-debug-frame-aarch64.c | 16 +++++
.../build/feature/test-libunwind-debug-frame-arm.c | 16 +++++
tools/build/feature/test-libunwind-debug-frame.c | 16 -----
tools/build/feature/test-libunwind-x86.c | 27 +++++++++
tools/build/feature/test-libunwind-x86_64.c | 27 +++++++++
.../arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 +-
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++++
tools/perf/arch/x86/util/unwind-libunwind.c | 42 +++++++++++++
tools/perf/builtin-script.c | 2 +
tools/perf/config/Makefile | 35 ++++++++++-
tools/perf/util/Build | 13 +++-
tools/perf/util/dso.c | 7 +++
tools/perf/util/dso.h | 1 +
tools/perf/util/symbol-elf.c | 16 +++++
tools/perf/util/symbol.c | 50 ++++++++++++++++
tools/perf/util/symbol.h | 3 +
tools/perf/util/thread.c | 70 ++++++++++++++++++++--
tools/perf/util/thread.h | 14 ++++-
tools/perf/util/unwind-libunwind.c | 50 +++++++++++++---
tools/perf/util/unwind-libunwind_common.c | 60 +++++++++++++++++++
tools/perf/util/unwind.h | 30 ++++++++++
tools/perf/util/util.c | 2 -
27 files changed, 589 insertions(+), 39 deletions(-)
create mode 100644 tools/build/feature/test-libunwind-aarch64.c
create mode 100644 tools/build/feature/test-libunwind-arm.c
create mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
create mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame.c
create mode 100644 tools/build/feature/test-libunwind-x86.c
create mode 100644 tools/build/feature/test-libunwind-x86_64.c
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
create mode 100644 tools/perf/arch/x86/include/libunwind/libunwind-arch.h
create mode 100644 tools/perf/util/unwind-libunwind_common.c

--
1.8.5.2

He Kuang

unread,
May 6, 2016, 5:10:10 AM5/6/16
to
Support x86(32bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++++++++
tools/perf/arch/x86/util/unwind-libunwind.c | 42 ++++++++++++++++++++++
tools/perf/util/Build | 6 ++++
tools/perf/util/thread.c | 10 +++---
tools/perf/util/unwind-libunwind.c | 2 +-
tools/perf/util/unwind.h | 5 +++
6 files changed, 77 insertions(+), 6 deletions(-)
create mode 100644 tools/perf/arch/x86/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/x86/include/libunwind/libunwind-arch.h b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..265f14d
--- /dev/null
+++ b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-x86.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_X86_32
+int libunwind__x86_reg_id(int regnum);
+
+#include <../../../x86/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, x86), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..d422fbf 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -5,6 +5,7 @@
#include "../../util/unwind.h"
#include "../../util/debug.h"

+#ifndef LIBUNWIND_X86_32
#ifdef HAVE_ARCH_X86_64_SUPPORT
int libunwind__arch_reg_id(int regnum)
{
@@ -110,3 +111,44 @@ int libunwind__arch_reg_id(int regnum)
return id;
}
#endif /* HAVE_ARCH_X86_64_SUPPORT */
+#else
+int libunwind__x86_reg_id(int regnum)
+{
+ int id;
+
+ switch (regnum) {
+ case UNW_X86_EAX:
+ id = PERF_REG_X86_AX;
+ break;
+ case UNW_X86_EDX:
+ id = PERF_REG_X86_DX;
+ break;
+ case UNW_X86_ECX:
+ id = PERF_REG_X86_CX;
+ break;
+ case UNW_X86_EBX:
+ id = PERF_REG_X86_BX;
+ break;
+ case UNW_X86_ESI:
+ id = PERF_REG_X86_SI;
+ break;
+ case UNW_X86_EDI:
+ id = PERF_REG_X86_DI;
+ break;
+ case UNW_X86_EBP:
+ id = PERF_REG_X86_BP;
+ break;
+ case UNW_X86_ESP:
+ id = PERF_REG_X86_SP;
+ break;
+ case UNW_X86_EIP:
+ id = PERF_REG_X86_IP;
+ break;
+ default:
+ pr_err("unwind: invalid reg id %d\n", regnum);
+ return -EINVAL;
+ }
+
+ return id;
+}
+#endif /* LIBUNWIND_X86_32 */
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2e21529..2dd3939 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,3 +1,5 @@
+include ../scripts/Makefile.include
+
libperf-y += alias.o
libperf-y += annotate.o
libperf-y += build-id.o
@@ -99,6 +101,10 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
+libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+
+$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index cf60db1..2b93856 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -214,12 +214,12 @@ void thread__insert_map(struct thread *thread, struct map *map)
pr_debug("Thread map is X86, 64bit is %d\n", is_64_bit);
if (!is_64_bit) {
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("target platform=%s is not implemented!\n",
- arch);
+ register_unwind_libunwind_ops(
+ &_Ux86_unwind_libunwind_ops, thread);
#else
- pr_err("target platform=%s is not supported!\n", arch);
-#endif
+ register_null_unwind_libunwind_ops(thread);
goto err;
+#endif
}
} else {
register_local_unwind_libunwind_ops(thread);
@@ -231,7 +231,7 @@ void thread__insert_map(struct thread *thread, struct map *map)
return;

err: __maybe_unused
- register_null_unwind_libunwind_ops(thread);
+ pr_err("target platform=%s not support!\n", arch);
return;
#endif
}
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 2558bf3..a195b56 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -527,7 +527,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;

diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 61b44a6..98d40bd 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -55,6 +55,11 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
#endif
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+extern struct unwind_libunwind_ops _Ux86_unwind_libunwind_ops;
+#endif
+
#else
static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
--
1.8.5.2

Arnaldo Carvalho de Melo

unread,
May 6, 2016, 7:50:06 AM5/6/16
to
Em Fri, May 06, 2016 at 08:59:07AM +0000, He Kuang escreveu:
> There's no need for dwarf support when perf recording with callchain.

Can you rewrite this? We do need to have support for dwarf when
processing "dwarf style" callchains.

- Arnaldo

Arnaldo Carvalho de Melo

unread,
May 6, 2016, 8:00:06 AM5/6/16
to
Em Fri, May 06, 2016 at 08:59:06AM +0000, He Kuang escreveu:
> Currently, perf script uses host unwind methods to parse perf.data
> callchain info regardless of the target architecture. So we get wrong
> result and no promotion when do remote unwind on other
> platforms/machines.

Thanks for working on this, being able to record on one machine and do
analysis in another, of a different hw architectures is indeed a goal
for the perf tools!

Jiri, if you could please take a look, that would be really great!

- Arnaldo

Jiri Olsa

unread,
May 7, 2016, 2:10:07 PM5/7/16
to
On Fri, May 06, 2016 at 08:59:07AM +0000, He Kuang wrote:
> There's no need for dwarf support when perf recording with callchain.
>
> Signed-off-by: He Kuang <hek...@huawei.com>

Acked-by: Jiri Olsa <jo...@kernel.org>

thanks,
jirka

Arnaldo Carvalho de Melo

unread,
May 9, 2016, 12:20:07 PM5/9/16
to
Em Sat, May 07, 2016 at 08:03:46PM +0200, Jiri Olsa escreveu:
> On Fri, May 06, 2016 at 08:59:07AM +0000, He Kuang wrote:
> > There's no need for dwarf support when perf recording with callchain.
> >
> > Signed-off-by: He Kuang <hek...@huawei.com>
>
> Acked-by: Jiri Olsa <jo...@kernel.org>

Ok, due to your Ack I went on to read the code and indeed, with this
changeset description:

"There is no need to check for DWARF unwinding support when using the
'dwarf' callchain record method, as this will only ask the kernel to
collect stack dumps for later DWARF CFI processing, which can be done
in another machine, where the support for DWARF unwinding need to be
present."

Thanks,

- Arnaldo

Hekuang

unread,
May 9, 2016, 9:50:07 PM5/9/16
to
ok

He Kuang

unread,
May 10, 2016, 3:50:08 AM5/10/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 ++++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 4 ++++
tools/perf/util/unwind-libunwind_common.c | 12 ++++++++++++
tools/perf/util/unwind.h | 3 +++
6 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..7bc8a00
--- /dev/null
+++ b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-aarch64.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_AARCH64
+int libunwind__aarch64_reg_id(int regnum);
+
+#include <../../../arm64/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__aarch64_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, aarch64), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..5b557a5 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,14 @@

#include <errno.h>
-#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2dd3939..10e42ad 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -102,10 +102,14 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
$(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

+$(OUTPUT)util/unwind-libunwind_arm64.o: util/unwind-libunwind.c arch/arm64/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/arm64/include/libunwind -c -o $@ util/unwind-libunwind.c
+
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

libperf-y += scripting-engines/
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index d0128f0..fde42b2 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -89,6 +89,18 @@ void unwind__get_arch(struct thread *thread, struct map *map)
#endif
} else
use_local_unwind = 1;
+ } else if (!strcmp(arch, "aarch64") || !strncmp(arch, "arm", 3)) {
+ pr_debug("Thread map is ARM, 64bit is %d, dso=%s\n",
+ is_64_bit, map->dso->name);
+ if (is_64_bit) {
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ register_unwind_libunwind_ops(
+ &_Uaarch64_unwind_libunwind_ops, thread);
+#else
+ register_null_unwind_libunwind_ops(thread);
+#endif
+ } else
+ use_local_unwind = 1;
} else {
use_local_unwind = 1;
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 8f7c4357..741a53f 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -58,6 +58,9 @@ register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 10, 2016, 3:50:08 AM5/10/16
to
There is no need to check for DWARF unwinding support when using the
'dwarf' callchain record method, as this will only ask the kernel to
collect stack dumps for later DWARF CFI processing, which can be done
in another machine, where the support for DWARF unwinding need to be
present.

Signed-off-by: He Kuang <hek...@huawei.com>
Acked-by: Jiri Olsa <jo...@kernel.org>

He Kuang

unread,
May 10, 2016, 3:50:09 AM5/10/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++++++++
tools/perf/arch/x86/util/unwind-libunwind.c | 42 ++++++++++++++++++++++
tools/perf/util/Build | 6 ++++
tools/perf/util/unwind-libunwind.c | 2 +-
tools/perf/util/unwind-libunwind_common.c | 7 ++--
tools/perf/util/unwind.h | 5 +++
6 files changed, 76 insertions(+), 4 deletions(-)
create mode 100644 tools/perf/arch/x86/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/x86/include/libunwind/libunwind-arch.h b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..265f14d
--- /dev/null
+++ b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-x86.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_X86_32
+int libunwind__x86_reg_id(int regnum);
+
+#include <../../../x86/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, x86), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..d422fbf 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -5,6 +5,7 @@
#include "../../util/unwind.h"
#include "../../util/debug.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2e21529..2dd3939 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,3 +1,5 @@
+include ../scripts/Makefile.include
+
libperf-y += alias.o
libperf-y += annotate.o
libperf-y += build-id.o
@@ -99,6 +101,10 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
+libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+
+$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 2a8d24e..1844431 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -527,7 +527,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;

diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index 4bdd3b9..d0128f0 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -82,10 +82,11 @@ void unwind__get_arch(struct thread *thread, struct map *map)
pr_debug("unwind: thread map is X86, 64bit is %d\n", is_64_bit);
if (!is_64_bit) {
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("unwind: target platform=%s is not implemented\n",
- arch);
-#endif
+ register_unwind_libunwind_ops(
+ &_Ux86_unwind_libunwind_ops, thread);
+#else
register_null_unwind_libunwind_ops(thread);
+#endif
} else
use_local_unwind = 1;
} else {
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index e045600..8f7c4357 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -54,6 +54,11 @@ static inline void unwind__get_arch(struct thread *thread __maybe_unused,
static inline void
register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 10, 2016, 3:50:09 AM5/10/16
to
v1 url:
http://thread.gmane.org/gmane.linux.kernel/2216256

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
get wrong result and no promotion when do remote unwind on other
platforms/machines.

This patchset adds build tests for the supported platforms for remote
unwinding, and checks whether a dso is 32-bit or 64-bit according to
elf class info for each thread to let perf use the correct remote
unwind methods instead.

Only x86 and aarch64 is added in this patchset to show the work flow,
other platforms can be added easily.

We can see the right result for unwind info on different machines, for
example: perf.data recorded on i686 qemu with '-g' option and parsed
on x86_64 machine.

before this patchset:

hello 1071 [000] 417.567832: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b77c8ba9 [unknown] ([vdso32])

after:

hello 1071 [000] 417.567832: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b77c8ba9 [unknown] ([vdso32])
b76e51cc close (/lib/libc-2.22.so)
804842e fib (/tmp/hello)
804849d main (/tmp/hello)
b762546e __libc_start_main (/lib/libc-2.22.so)
8048341 _start (/tmp/hello)

v2:

- Explain the reason why we can omit dwarf judgement when recording
in commit message.
- Elaborate on why we need to add a custom vdso path option, and
change the type name to DSO_BINARY_TYPE__VDSO.
- Hide the build tests status for cross platform unwind.
- Keep generic version of libunwind-debug-frame test.
- Put 32/64-bit test functions into separate patch.
- Extract unwind related functions to unwind-libunwind.c and add new
file for common parts used by both local and remote unwind.
- Eliminate most of the ifdefs in .c file.

Thanks.

He Kuang (9):
perf tools: Omit DWARF judgement when recording dwarf callchain
perf script: Add options for custom vdso path
perf build: Add build-test for libunwind cross-platforms support
perf build: Add build-test for debug-frame on arm/arm64
perf tools: Add methods to test dso is 64-bit or 32-bit
perf tools: Promote proper messages for cross-platform unwind
perf callchain: Add support for cross-platform unwind
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

tools/build/Makefile.feature | 8 +-
tools/build/feature/Makefile | 23 +++++
tools/build/feature/test-libunwind-aarch64.c | 26 +++++
tools/build/feature/test-libunwind-arm.c | 27 +++++
.../feature/test-libunwind-debug-frame-aarch64.c | 16 +++
.../build/feature/test-libunwind-debug-frame-arm.c | 16 +++
tools/build/feature/test-libunwind-x86.c | 27 +++++
tools/build/feature/test-libunwind-x86_64.c | 27 +++++
.../arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 +-
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/x86/util/unwind-libunwind.c | 42 ++++++++
tools/perf/builtin-script.c | 2 +
tools/perf/config/Makefile | 35 ++++++-
tools/perf/util/Build | 13 ++-
tools/perf/util/dso.c | 7 ++
tools/perf/util/dso.h | 1 +
tools/perf/util/symbol-elf.c | 16 +++
tools/perf/util/symbol.c | 50 +++++++++
tools/perf/util/symbol.h | 3 +
tools/perf/util/thread.c | 7 +-
tools/perf/util/thread.h | 14 ++-
tools/perf/util/unwind-libunwind.c | 49 +++++++--
tools/perf/util/unwind-libunwind_common.c | 113 +++++++++++++++++++++
tools/perf/util/unwind.h | 48 +++++++--
tools/perf/util/util.c | 2 -
26 files changed, 585 insertions(+), 28 deletions(-)
create mode 100644 tools/build/feature/test-libunwind-aarch64.c
create mode 100644 tools/build/feature/test-libunwind-arm.c
create mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
create mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c

Arnaldo Carvalho de Melo

unread,
May 10, 2016, 9:50:08 AM5/10/16
to
Em Tue, May 10, 2016 at 07:40:29AM +0000, He Kuang escreveu:
> There is no need to check for DWARF unwinding support when using the
> 'dwarf' callchain record method, as this will only ask the kernel to
> collect stack dumps for later DWARF CFI processing, which can be done
> in another machine, where the support for DWARF unwinding need to be
> present.

I had this one already applied, thanks.

- Arnaldo

tip-bot for He Kuang

unread,
May 10, 2016, 4:40:08 PM5/10/16
to
Commit-ID: 841e3558b2de6d8c53dae4f01c7a81cd53e42e5c
Gitweb: http://git.kernel.org/tip/841e3558b2de6d8c53dae4f01c7a81cd53e42e5c
Author: He Kuang <hek...@huawei.com>
AuthorDate: Fri, 6 May 2016 08:59:07 +0000
Committer: Arnaldo Carvalho de Melo <ac...@redhat.com>
CommitDate: Mon, 9 May 2016 13:29:36 -0300

perf callchain: Recording 'dwarf' callchains do not need DWARF unwinding support

There is no need to check for DWARF unwinding support when using the
'dwarf' callchain record method, as this will only ask the kernel to
collect stack dumps for later DWARF CFI processing, which can be done in
another machine, where the support for DWARF unwinding need to be
present.

Signed-off-by: He Kuang <hek...@huawei.com>
Acked-by: Jiri Olsa <jo...@kernel.org>
Cc: Adrian Hunter <adrian...@intel.com>
Cc: Alexander Shishkin <alexander...@linux.intel.com>
Cc: Andi Kleen <a...@linux.intel.com>
Cc: David Ahern <dsa...@gmail.com>
Cc: Ekaterina Tumanova <tuma...@linux.vnet.ibm.com>
Cc: Josh Poimboeuf <jpoi...@redhat.com>
Cc: Kan Liang <kan....@intel.com>
Cc: Masami Hiramatsu <mhir...@kernel.org>
Cc: Namhyung Kim <namh...@kernel.org>
Cc: Pekka Enberg <pen...@kernel.org>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Stephane Eranian <era...@google.com>
Cc: Sukadev Bhattiprolu <suk...@linux.vnet.ibm.com>
Cc: Wang Nan <wang...@huawei.com>
Link: http://lkml.kernel.org/r/1462525154-125656-2-g...@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/util/util.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 619ba20..01c9433 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -507,7 +507,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
"needed for --call-graph fp\n");
break;

-#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
@@ -523,7 +522,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)

He Kuang

unread,
May 12, 2016, 4:50:07 AM5/12/16
to
When unwinding callchains on a different machine, vdso info should be
provided so the unwind process won't be interrupted if address falls
into vdso region. But in most cases, the addresses of sample events
are not in vdso range, the buildid of a zero hit vdso won't be stored
into perf.data.

This patch stores vdso buildid regardless of whether the vdso is hit
or not.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/build-id.c | 2 +-
tools/perf/util/dso.c | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 0573c2e..bdc7580 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -256,7 +256,7 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
size_t name_len;
bool in_kernel = false;

- if (!pos->hit)
+ if (!pos->hit && !dso__is_vdso(pos))
continue;

if (dso__is_vdso(pos)) {
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 8e639543..b39b80c 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -7,6 +7,7 @@
#include "auxtrace.h"
#include "util.h"
#include "debug.h"
+#include "vdso.h"

char dso__symtab_origin(const struct dso *dso)
{
@@ -1169,7 +1170,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
struct dso *pos;

list_for_each_entry(pos, head, node) {
- if (with_hits && !pos->hit)
+ if (with_hits && !pos->hit && !dso__is_vdso(pos))
continue;
if (pos->has_build_id) {
have_build_id = true;
--
1.8.5.2

He Kuang

unread,
May 12, 2016, 4:50:07 AM5/12/16
to
v2 url:
http://thread.gmane.org/gmane.linux.kernel/2218373

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
get wrong result and no promotion when do remote unwind on other
platforms/machines.

This patchset adds build tests for the supported platforms for remote
unwinding, and checks whether a dso is 32-bit or 64-bit according to
elf class info for each thread to let perf use the correct remote
unwind methods instead.

Only x86 and aarch64 is added in this patchset to show the work flow,
other platforms can be added easily.

We can see the right result for unwind info on different machines, for
example: perf.data recorded on i686 qemu with '-g' option and parsed
on x86_64 machine.

before this patchset:

hello 1219 [001] 72190.667975: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b777aba9 [unknown] ([vdso32])

after:
(Add vdso into buildid-cache first by 'perf buildid-cache -a' and
libraries are provided in symfs dir)

hello 1219 [001] 72190.667975: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b777aba9 __kernel_vsyscall ([vdso32])
b76971cc close (/lib/libc-2.22.so)
804842e fib (/tmp/hello)
804849d main (/tmp/hello)
b75d746e __libc_start_main (/lib/libc-2.22.so)
8048341 _start (/tmp/hello)

v3:

- Remove --vdso option, store vdso buildid in perf.data and let perf
fetch it automatically.
- Use existing dso__type() function to test if dso is 32-bit or
64-bit.

v2:

- Explain the reason why we can omit dwarf judgement when recording
in commit message.
- Elaborate on why we need to add a custom vdso path option, and
change the type name to DSO_BINARY_TYPE__VDSO.
- Hide the build tests status for cross platform unwind.
- Keep generic version of libunwind-debug-frame test.
- Put 32/64-bit test functions into separate patch.
- Extract unwind related functions to unwind-libunwind.c and add new
file for common parts used by both local and remote unwind.
- Eliminate most of the ifdefs in .c file.

Thanks.

He Kuang (7):
perf tools: Set vdso name to vdso[64,32] depending on platform
perf tools: Store vdso buildid unconditionally
perf tools: Remove the logical that skip buildid cache if symfs is
given
perf tools: Promote proper messages for cross-platform unwind
perf callchain: Add support for cross-platform unwind
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

.../arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 +-
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/x86/util/unwind-libunwind.c | 42 ++++++++
tools/perf/config/Makefile | 35 ++++++-
tools/perf/util/Build | 13 ++-
tools/perf/util/build-id.c | 2 +-
tools/perf/util/dso.c | 6 +-
tools/perf/util/thread.c | 7 +-
tools/perf/util/thread.h | 14 ++-
tools/perf/util/unwind-libunwind.c | 49 +++++++--
tools/perf/util/unwind-libunwind_common.c | 114 +++++++++++++++++++++
tools/perf/util/unwind.h | 48 +++++++--
tools/perf/util/vdso.h | 7 +-
14 files changed, 348 insertions(+), 30 deletions(-)

He Kuang

unread,
May 12, 2016, 4:50:07 AM5/12/16
to
Currently, perf script uses host unwind methods to parse perf.data
callchain info regardless of the target architecture. So we get wrong
result and no promotion when unwinding callchains of x86(32-bit) on
x86(64-bit) machine.

This patch shows proper error messages when we do remote unwind
x86(32-bit) on other machines.

Same thing for other platforms will be added in next patches.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 6 ++++++
tools/perf/util/thread.c | 2 ++
tools/perf/util/unwind-libunwind.c | 33 +++++++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 5 +++++
4 files changed, 46 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..a86b864 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -345,6 +345,12 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ ifeq ($(feature-libunwind-x86), 1)
+ LIBUNWIND_LIBS += -lunwind-x86
+ $(call detected,CONFIG_LIBUNWIND_X86)
+ CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
+ endif
+
ifneq ($(feature-libunwind), 1)
msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR);
NO_LIBUNWIND := 1
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index dfd00c6..244c4f6 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -186,6 +186,8 @@ void thread__insert_map(struct thread *thread, struct map *map)
{
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);
+
+ unwind__get_arch(thread, map);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 63687d3..d464c35 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -680,3 +680,36 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,

return get_entries(&ui, cb, arg, max_stack);
}
+
+void unwind__get_arch(struct thread *thread, struct map *map)
+{
+ char *arch;
+ enum dso_type dso_type;
+
+ if (!thread->mg->machine->env)
+ return;
+
+ dso_type = dso__type(map->dso, thread->mg->machine);
+ if (dso_type == DSO__TYPE_UNKNOWN)
+ return;
+
+ if (thread->addr_space)
+ pr_debug("Thread map already set, 64bit is %d, dso=%s\n",
+ dso_type == DSO__TYPE_64BIT, map->dso->name);
+
+ arch = thread->mg->machine->env->arch;
+
+ if (!strcmp(arch, "x86_64")
+ || !strcmp(arch, "x86")
+ || !strcmp(arch, "i686")) {
+ pr_debug("Thread map is X86, 64bit is %d\n",
+ dso_type == DSO__TYPE_64BIT);
+ if (dso_type != DSO__TYPE_64BIT)
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ pr_err("target platform=%s is not implemented!\n",
+ arch);
+#else
+ pr_err("target platform=%s is not supported!\n", arch);
+#endif
+ }
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 12790cf..889d630 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -24,6 +24,7 @@ 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)
{
@@ -32,6 +33,8 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__get_arch(struct thread *thread __maybe_unused,
+ struct map *map __maybe_unused) {}
#endif
#else
static inline int
@@ -51,5 +54,7 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__get_arch(struct thread *thread __maybe_unused,
+ struct map *map __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */
--
1.8.5.2

He Kuang

unread,
May 12, 2016, 4:50:07 AM5/12/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 2a8d24e..1844431 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -527,7 +527,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;

diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index f67707d..dbc44b1 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -83,10 +83,11 @@ void unwind__get_arch(struct thread *thread, struct map *map)
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);
-#endif
+ register_unwind_libunwind_ops(
+ &_Ux86_unwind_libunwind_ops, thread);
+#else
register_null_unwind_libunwind_ops(thread);
+#endif
} else
use_local_unwind = 1;
} else {
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index e045600..8f7c4357 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h

He Kuang

unread,
May 12, 2016, 4:50:08 AM5/12/16
to
Use thread specific unwind ops to unwind cross-platform callchains.

Before this patch, 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. In this patch, we test for x86 platform and only show
proper messages. The real unwind methods are not implemented, will be
introduced in next patch.

Common function used by both local unwind and remote unwind are
separated into new file 'unwind-libunwind_common.c'.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 19 +++++-
tools/perf/util/Build | 3 +-
tools/perf/util/thread.c | 5 +-
tools/perf/util/thread.h | 14 ++++-
tools/perf/util/unwind-libunwind.c | 74 +++++++++++-----------
tools/perf/util/unwind-libunwind_common.c | 101 ++++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 35 ++++++++---
7 files changed, 197 insertions(+), 54 deletions(-)
create mode 100644 tools/perf/util/unwind-libunwind_common.c

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index a86b864..16f14b1 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -345,14 +345,24 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ have_libunwind =
ifeq ($(feature-libunwind-x86), 1)
- LIBUNWIND_LIBS += -lunwind-x86
$(call detected,CONFIG_LIBUNWIND_X86)
CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
+ LDFLAGS += -lunwind -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
endif
endif
@@ -392,7 +402,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)
@@ -403,12 +413,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 ea4ac03..2e21529 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -97,7 +97,8 @@ 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 244c4f6..2274263 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -41,9 +41,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;
@@ -57,6 +54,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 e214207..6f2d4cd 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -15,6 +15,17 @@

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 +47,8 @@ struct thread {
void *priv;
struct thread_stack *ts;
#ifdef HAVE_LIBUNWIND_SUPPORT
- unw_addr_space_t addr_space;
+ unw_addr_space_t 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 d464c35..2a8d24e 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -22,6 +22,9 @@
#include <unistd.h>
#include <sys/mman.h>
#include <linux/list.h>
+#ifdef ARCH_UNWIND_LIBUNWIND
+#include "libunwind-arch.h"
+#endif
#include <libunwind.h>
#include <libunwind-ptrace.h>
#include "callchain.h"
@@ -34,6 +37,22 @@
#include "debug.h"
#include "asm/bug.h"

+#ifndef ARCH_UNWIND_LIBUNWIND
+ #define LIBUNWIND__ARCH_REG_ID libunwind__arch_reg_id
+ #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,
@@ -579,7 +598,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 +613,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 +621,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 +681,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,
@@ -681,35 +701,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
return get_entries(&ui, cb, arg, max_stack);
}

-void unwind__get_arch(struct thread *thread, struct map *map)
-{
- char *arch;
- enum dso_type dso_type;
-
- if (!thread->mg->machine->env)
- return;
-
- dso_type = dso__type(map->dso, thread->mg->machine);
- if (dso_type == DSO__TYPE_UNKNOWN)
- return;
+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),
+};

- if (thread->addr_space)
- pr_debug("Thread map already set, 64bit is %d, dso=%s\n",
- dso_type == DSO__TYPE_64BIT, map->dso->name);
-
- arch = thread->mg->machine->env->arch;
-
- if (!strcmp(arch, "x86_64")
- || !strcmp(arch, "x86")
- || !strcmp(arch, "i686")) {
- pr_debug("Thread map is X86, 64bit is %d\n",
- dso_type == DSO__TYPE_64BIT);
- if (dso_type != DSO__TYPE_64BIT)
-#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("target platform=%s is not implemented!\n",
- arch);
-#else
- pr_err("target platform=%s is not supported!\n", arch);
-#endif
- }
+#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
new file mode 100644
index 0000000..f67707d
--- /dev/null
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -0,0 +1,101 @@
+#include "thread.h"
+#include "session.h"
+#include "unwind.h"
+#include "symbol.h"
+#include "debug.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)
+{
+ char *arch;
+ enum dso_type dso_type;
+ int use_local_unwind = 0;
+
+ if (!thread->mg->machine->env)
+ return;
+
+ dso_type = dso__type(map->dso, thread->mg->machine);
+ if (dso_type == DSO__TYPE_UNKNOWN)
+ return;
+
+ 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);
+
+ arch = thread->mg->machine->env->arch;
+
+ if (!strcmp(arch, "x86_64")
+ || !strcmp(arch, "x86")
+ || !strcmp(arch, "i686")) {
+ pr_debug("unwind: thread map is X86, 64bit is %d\n",
+ 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);
+#endif
+ register_null_unwind_libunwind_ops(thread);
+ } else
+ use_local_unwind = 1;
+ } else {
+ use_local_unwind = 1;
+ }
+
+ 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..e045600 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -21,20 +21,38 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
/* 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)
+
+#else
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}
#endif
#else
static inline int
@@ -47,14 +65,11 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
return 0;
}

-static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
-{
- return 0;
-}
-
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 12, 2016, 4:50:08 AM5/12/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 ++++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 4 ++++
tools/perf/util/unwind-libunwind_common.c | 12 ++++++++++++
tools/perf/util/unwind.h | 3 +++
6 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..7bc8a00
--- /dev/null
+++ b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-aarch64.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_AARCH64
+int libunwind__aarch64_reg_id(int regnum);
+
+#include <../../../arm64/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__aarch64_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, aarch64), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..5b557a5 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,14 @@

#include <errno.h>
-#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"

+#ifndef LIBUNWIND_AARCH64
int libunwind__arch_reg_id(int regnum)
+#else
+int libunwind__aarch64_reg_id(int regnum)
+#endif
{
switch (regnum) {
case UNW_AARCH64_X0:
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 16f14b1..c1bfc5e 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -353,6 +353,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2dd3939..10e42ad 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -102,10 +102,14 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
$(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

+$(OUTPUT)util/unwind-libunwind_arm64.o: util/unwind-libunwind.c arch/arm64/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DARCH_UNWIND_LIBUNWIND -Iarch/arm64/include/libunwind -c -o $@ util/unwind-libunwind.c
+
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

libperf-y += scripting-engines/
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index dbc44b1..14a209d 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -90,6 +90,18 @@ void unwind__get_arch(struct thread *thread, struct map *map)
#endif
} else
use_local_unwind = 1;
+ } else if (!strcmp(arch, "aarch64") || !strncmp(arch, "arm", 3)) {
+ pr_debug("Thread map is ARM, 64bit is %d, dso=%s\n",
+ dso_type == DSO__TYPE_64BIT, map->dso->name);
+ if (dso_type == DSO__TYPE_64BIT) {
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ register_unwind_libunwind_ops(
+ &_Uaarch64_unwind_libunwind_ops, thread);
+#else
+ register_null_unwind_libunwind_ops(thread);
+#endif
+ } else
+ use_local_unwind = 1;
} else {
use_local_unwind = 1;
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 8f7c4357..741a53f 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h

Arnaldo Carvalho de Melo

unread,
May 12, 2016, 9:20:06 AM5/12/16
to
Em Thu, May 12, 2016 at 08:43:11AM +0000, He Kuang escreveu:
> When unwinding callchains on a different machine, vdso info should be
> provided so the unwind process won't be interrupted if address falls
> into vdso region. But in most cases, the addresses of sample events
> are not in vdso range, the buildid of a zero hit vdso won't be stored
> into perf.data.
>
> This patch stores vdso buildid regardless of whether the vdso is hit
> or not.

Looks ok, applied.

He Kuang

unread,
May 19, 2016, 7:50:06 AM5/19/16
to
Currently, perf script uses host unwind methods to parse perf.data
callchain info regardless of the target architecture. So we get wrong
result and no promotion when unwinding callchains of x86(32-bit) on
x86(64-bit) machine.

This patch shows proper error messages when we do remote unwind
x86(32-bit) on other machines. Same thing for other platforms will be
added in next patches.

Common functions which will be used by both local unwind and remote
unwind are separated into new file 'unwind-libunwind_common.c'.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
tools/perf/config/Makefile | 6 ++++++
tools/perf/util/Build | 1 +
tools/perf/util/thread.c | 2 ++
tools/perf/util/unwind-libunwind_common.c | 34 +++++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 5 +++++
7 files changed, 50 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/util/unwind-libunwind_common.c

diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index e83c8ce..fa090a9 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -102,7 +102,7 @@ static int lookup_triplets(const char *const *triplets, const char *name)
* Return architecture name in a normalized form.
* The conversion logic comes from the Makefile.
*/
-static const char *normalize_arch(char *arch)
+const char *normalize_arch(char *arch)
{
if (!strcmp(arch, "x86_64"))
return "x86";
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h
index 7529cfb..6b01c73 100644
--- a/tools/perf/arch/common.h
+++ b/tools/perf/arch/common.h
@@ -6,5 +6,6 @@
extern const char *objdump_path;

int perf_env__lookup_objdump(struct perf_env *env);
+const char *normalize_arch(char *arch);

#endif /* ARCH_PERF_COMMON_H */
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..a86b864 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -345,6 +345,12 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ ifeq ($(feature-libunwind-x86), 1)
+ LIBUNWIND_LIBS += -lunwind-x86
+ $(call detected,CONFIG_LIBUNWIND_X86)
+ CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
+ endif
+
ifneq ($(feature-libunwind), 1)
msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR);
NO_LIBUNWIND := 1
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8c6c8a0..25c31fb 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -100,6 +100,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o

libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_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 45fcb71..3043113 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -205,6 +205,8 @@ void thread__insert_map(struct thread *thread, struct map *map)
{
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);
+
+ unwind__get_arch(thread, map);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
new file mode 100644
index 0000000..3946c99
--- /dev/null
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -0,0 +1,34 @@
+#include "thread.h"
+#include "session.h"
+#include "unwind.h"
+#include "symbol.h"
+#include "debug.h"
+#include "arch/common.h"
+
+void unwind__get_arch(struct thread *thread, struct map *map)
+{
+ const char *arch;
+ enum dso_type dso_type;
+
+ if (!thread->mg->machine->env)
+ return;
+
+ dso_type = dso__type(map->dso, thread->mg->machine);
+ if (dso_type == DSO__TYPE_UNKNOWN)
+ return;
+
+ 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);
+
+ arch = normalize_arch(thread->mg->machine->env->arch);
+
+ if (!strcmp(arch, "x86")) {
+ 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
+ }
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 12790cf..889d630 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -24,6 +24,7 @@ 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)
{
@@ -32,6 +33,8 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__get_arch(struct thread *thread __maybe_unused,
+ struct map *map __maybe_unused) {}
#endif
#else
static inline int
@@ -51,5 +54,7 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__get_arch(struct thread *thread __maybe_unused,
+ struct map *map __maybe_unused) {}

He Kuang

unread,
May 19, 2016, 7:50:06 AM5/19/16
to
v3 url:
http://thread.gmane.org/gmane.linux.kernel/2220387

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
get wrong result and no promotion when do remote unwind on other
platforms/machines.

This patchset checks whether a dso is 32-bit or 64-bit according to
elf class info for each thread to let perf use the correct remote
unwind methods instead.

Only x86 and aarch64 is added in this patchset to show the work flow,
other platforms can be added easily.

We can see the right result for unwind info on different machines, for
example: perf.data recorded on i686 qemu with '-g' option and parsed
on x86_64 machine.

before this patchset:

hello 1219 [001] 72190.667975: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b777aba9 [unknown] ([vdso32])

after:
(Add vdso into buildid-cache first by 'perf buildid-cache -a' and
libraries are provided in symfs dir)

hello 1219 [001] 72190.667975: probe:sys_close: (c1169d60)
c1169d61 sys_close ([kernel.kallsyms])
c189c0d7 sysenter_past_esp ([kernel.kallsyms])
b777aba9 __kernel_vsyscall ([vdso32])
b76971cc close (/lib/libc-2.22.so)
804842e fib (/tmp/hello)
804849d main (/tmp/hello)
b75d746e __libc_start_main (/lib/libc-2.22.so)
8048341 _start (/tmp/hello)

v4:

- Move reference of buildid dir to 'symfs/.debug' if --symfs is
given.
- Split makefile changes out of patch 'Add support for
cross-platform unwind'.
- Use existing code normalize_arch() for testing the arch of
perf.data.

v3:

- Remove --vdso option, store vdso buildid in perf.data and let perf
fetch it automatically.
- Use existing dso__type() function to test if dso is 32-bit or
64-bit.

v2:

- Explain the reason why we can omit dwarf judgement when recording
in commit message.
- Elaborate on why we need to add a custom vdso path option, and
change the type name to DSO_BINARY_TYPE__VDSO.
- Hide the build tests status for cross platform unwind.
- Keep generic version of libunwind-debug-frame test.
- Put 32/64-bit test functions into separate patch.
- Extract unwind related functions to unwind-libunwind.c and add new
file for common parts used by both local and remote unwind.
- Eliminate most of the ifdefs in .c file.

Thanks.

He Kuang (6):
perf tools: Set buildid dir under symfs when --symfs is provided
perf tools: Promote proper messages for cross-platform unwind
perf tools: Separate local and remote unwind support detection
perf callchain: Add support for cross-platform unwind
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

.../arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 +-
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/x86/util/unwind-libunwind.c | 12 ++-
tools/perf/builtin-annotate.c | 5 +-
tools/perf/builtin-diff.c | 5 +-
tools/perf/builtin-report.c | 5 +-
tools/perf/builtin-script.c | 5 +-
tools/perf/builtin-timechart.c | 5 +-
tools/perf/config/Makefile | 37 ++++++-
tools/perf/util/Build | 13 ++-
tools/perf/util/dso.c | 4 +-
tools/perf/util/symbol.c | 23 +++++
tools/perf/util/symbol.h | 2 +
tools/perf/util/thread.c | 7 +-
tools/perf/util/thread.h | 14 ++-
tools/perf/util/unwind-libunwind.c | 48 +++++++--
tools/perf/util/unwind-libunwind_common.c | 109 +++++++++++++++++++++
tools/perf/util/unwind.h | 45 +++++++--
21 files changed, 341 insertions(+), 42 deletions(-)

He Kuang

unread,
May 19, 2016, 7:50:07 AM5/19/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 ++++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 4 ++++
tools/perf/util/unwind-libunwind_common.c | 10 ++++++++++
tools/perf/util/unwind.h | 3 +++
6 files changed, 51 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
new file mode 100644
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 3035dbf..d3b77d8 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -353,6 +353,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2373130..f1b51a2 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -104,10 +104,14 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
$(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

+$(OUTPUT)util/unwind-libunwind_arm64.o: util/unwind-libunwind.c arch/arm64/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/arm64/include/libunwind -c -o $@ util/unwind-libunwind.c
+
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

libperf-y += scripting-engines/
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index 619c6c0..d19b062 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -89,6 +89,16 @@ void unwind__get_arch(struct thread *thread, struct map *map)
#endif
use_local_unwind = 0;
}
+ } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
+ if (dso_type == DSO__TYPE_64BIT) {
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ register_unwind_libunwind_ops(
+ &_Uaarch64_unwind_libunwind_ops, thread);
+#else
+ register_null_unwind_libunwind_ops(thread);
+#endif
+ use_local_unwind = 0;
+ }
}

if (use_local_unwind)
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 73f4bd9..9748c36 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -55,6 +55,9 @@ register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 19, 2016, 7:50:08 AM5/19/16
to
Use thread specific unwind ops to unwind cross-platform callchains.

Before this patch, 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. In this patch, we test for x86 platform and only show
proper messages. The real unwind methods are not implemented, will be
introduced in next patch.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.c | 5 +--
tools/perf/util/thread.h | 14 +++++-
tools/perf/util/unwind-libunwind.c | 48 ++++++++++++++++++---
tools/perf/util/unwind-libunwind_common.c | 71 +++++++++++++++++++++++++++++--
tools/perf/util/unwind.h | 32 +++++++++-----
5 files changed, 145 insertions(+), 25 deletions(-)

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..de8e42e 100644
index 63687d3..d3a2ec44 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -22,6 +22,9 @@
#include <unistd.h>
#include <sys/mman.h>
#include <linux/list.h>
+#ifdef REMOTE_UNWIND_LIBUNWIND
+#include "libunwind-arch.h"
+#endif
#include <libunwind.h>
#include <libunwind-ptrace.h>
#include "callchain.h"
@@ -34,6 +37,21 @@
#include "debug.h"
#include "asm/bug.h"

+#ifndef REMOTE_UNWIND_LIBUNWIND
+ #define LIBUNWIND__ARCH_REG_ID libunwind__arch_reg_id
+ #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 +526,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 +597,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 +612,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 +620,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 +680,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 +699,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"
+ 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..1187950 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -21,20 +21,35 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
/* 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)
+
+#else
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}
#endif
#else
static inline int
@@ -47,14 +62,11 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
return 0;
}

-static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
-{
- return 0;
-}
-
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 19, 2016, 7:50:08 AM5/19/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/x86/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/x86/util/unwind-libunwind.c | 12 +++++++++---
tools/perf/util/Build | 6 ++++++
tools/perf/util/unwind-libunwind_common.c | 6 ++++--
tools/perf/util/unwind.h | 5 +++++
5 files changed, 42 insertions(+), 5 deletions(-)
create mode 100644 tools/perf/arch/x86/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/x86/include/libunwind/libunwind-arch.h b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..265f14d
--- /dev/null
+++ b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-x86.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_X86_32
+int libunwind__x86_reg_id(int regnum);
+
+#include <../../../x86/util/unwind-libunwind.c>
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, x86), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..a4f5449 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -5,7 +5,7 @@
#include "../../util/unwind.h"
#include "../../util/debug.h"

-#ifdef HAVE_ARCH_X86_64_SUPPORT
+#if !defined(LIBUNWIND_X86_32) && defined(HAVE_ARCH_X86_64_SUPPORT)
int libunwind__arch_reg_id(int regnum)
{
int id;
@@ -69,8 +69,14 @@ int libunwind__arch_reg_id(int regnum)

return id;
}
-#else
+#endif
+
+#if defined(LIBUNWIND_X86_32) || !defined(HAVE_ARCH_X86_64_SUPPORT)
+#ifndef LIBUNWIND_X86_32
int libunwind__arch_reg_id(int regnum)
+#else
+int libunwind__x86_reg_id(int regnum)
+#endif
{
int id;

@@ -109,4 +115,4 @@ int libunwind__arch_reg_id(int regnum)

return id;
}
-#endif /* HAVE_ARCH_X86_64_SUPPORT */
+#endif
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index ce69721..2373130 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,3 +1,5 @@
+include ../scripts/Makefile.include
+
libperf-y += alias.o
libperf-y += annotate.o
libperf-y += build-id.o
@@ -101,6 +103,10 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
+libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+
+$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index f44833b..619c6c0 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -82,9 +82,11 @@ void unwind__get_arch(struct thread *thread, struct map *map)
if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT) {
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("unwind: target platform=%s is not implemented\n", arch);
-#endif
+ register_unwind_libunwind_ops(
+ &_Ux86_unwind_libunwind_ops, thread);
+#else
register_null_unwind_libunwind_ops(thread);
+#endif
use_local_unwind = 0;
}
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 1187950..73f4bd9 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -51,6 +51,11 @@ static inline void unwind__get_arch(struct thread *thread __maybe_unused,

He Kuang

unread,
May 19, 2016, 7:50:08 AM5/19/16
to
This patch changes original CONFIG_LIBUNWIND/NO_LIBUNWIND 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 <hek...@huawei.com>
---
tools/perf/config/Makefile | 21 ++++++++++++++++++---
tools/perf/util/Build | 2 +-
2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index a86b864..3035dbf 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -345,16 +345,28 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ have_libunwind =
ifeq ($(feature-libunwind-x86), 1)
- LIBUNWIND_LIBS += -lunwind-x86
$(call detected,CONFIG_LIBUNWIND_X86)
CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
+ LDFLAGS += -lunwind -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
endif
+else
+ NO_LOCAL_LIBUNWIND := 1
endif

ifndef NO_LIBBPF
@@ -392,7 +404,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)
@@ -403,12 +415,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
--
1.8.5.2

Arnaldo Carvalho de Melo

unread,
May 19, 2016, 11:00:08 AM5/19/16
to
Em Thu, May 19, 2016 at 11:47:38AM +0000, He Kuang escreveu:
> Currently, perf script uses host unwind methods to parse perf.data
> callchain info regardless of the target architecture. So we get wrong
> result and no promotion when unwinding callchains of x86(32-bit) on

What you mean by "promotion" here? Can you use some other synonym so
that I can make sense of this description?

Jiri Olsa

unread,
May 19, 2016, 12:20:07 PM5/19/16
to
On Thu, May 19, 2016 at 11:50:10AM -0300, Arnaldo Carvalho de Melo wrote:
> Em Thu, May 19, 2016 at 11:47:38AM +0000, He Kuang escreveu:
> > Currently, perf script uses host unwind methods to parse perf.data
> > callchain info regardless of the target architecture. So we get wrong
> > result and no promotion when unwinding callchains of x86(32-bit) on
>
> What you mean by "promotion" here? Can you use some other synonym so
> that I can make sense of this description?

I'd say something like: s/and no promotion/without any warning/

jirka

Jiri Olsa

unread,
May 19, 2016, 12:50:08 PM5/19/16
to
On Thu, May 19, 2016 at 11:47:38AM +0000, He Kuang wrote:

SNIP

> #endif /* ARCH_PERF_COMMON_H */
> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> index 1e46277..a86b864 100644
> --- a/tools/perf/config/Makefile
> +++ b/tools/perf/config/Makefile
> @@ -345,6 +345,12 @@ ifeq ($(ARCH),powerpc)
> endif
>
> ifndef NO_LIBUNWIND
> + ifeq ($(feature-libunwind-x86), 1)
> + LIBUNWIND_LIBS += -lunwind-x86
> + $(call detected,CONFIG_LIBUNWIND_X86)
> + CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
> + endif
> +

how does one install that lirary?

thanks,
jirka

Jiri Olsa

unread,
May 19, 2016, 1:20:13 PM5/19/16
to
had to uninstall x86_64..

[jolsa@krava perf]$ rpm -qa | grep libunw
libunwind-devel-1.1-10.fc22.i686
libunwind-1.1-10.fc22.i686
libunwind-1.1-10.fc22.x86_64


however it's still off:

[jolsa@krava perf]$ make VF=1
BUILD: Doing 'make -j4' parallel build

Auto-detecting system features:

SNIP

... libunwind: [ OFF ]

SNIP

... libunwind-x86: [ OFF ]
... libunwind-x86_64: [ OFF ]
... libunwind-arm: [ OFF ]
... libunwind-aarch64: [ OFF ]
... pthread-attr-setaffinity-np: [ on ]
... stackprotector-all: [ on ]
... timerfd: [ on ]


thanks,
jirka

Hekuang

unread,
May 19, 2016, 11:10:06 PM5/19/16
to
hi
My work environment is on an old suse distribution, so it's
difficult to find libunwind-$arch rpm packages, so I build them
from source.

The git repository url is here:
http://git.savannah.gnu.org/r/libunwind.git(master)

Then flow the build step in README, first for i686:

$ ./autogen.sh
$ ./configure prefix=/xx/dst_i686 --target=i686-oe-linux
CC=x86_64-oe-linux-gcc
$ make && make install

Similar for aarch64:

$ make clean
$ ./configure prefix=/xx/dst_aarch64 --target=i686-oe-linux
CC=x86_64-oe-linux-gcc
$ make && make install

NOTICE: the contents in '--target' should be like
'i686-oe-linux', only give 'i686' cause strange build errors.

It looks like that libunwind don't support building for multiple
platforms at the same time, so I build them separately into
different directories.

Finally, copy the outputs into /usr/include and /usr/lib64, now
perf can detect them:

$ make VF=1 ARCH=x86_64 CROSS_COMPILE=x86_64-oe-linux-
EXTRA_CFLAGS="-m64"
...

... libunwind-x86: [ on ]
... libunwind-x86_64: [ OFF ]
... libunwind-arm: [ OFF ]
... libunwind-aarch64: [ on ]

Thanks.

Hekuang

unread,
May 19, 2016, 11:10:06 PM5/19/16
to
Yes, that's what I want to express.

tip-bot for He Kuang

unread,
May 20, 2016, 2:50:06 AM5/20/16
to
Commit-ID: 6ae98ba611ed1c11ddc5645475bc03b46a3c04e7
Gitweb: http://git.kernel.org/tip/6ae98ba611ed1c11ddc5645475bc03b46a3c04e7
Author: He Kuang <hek...@huawei.com>
AuthorDate: Thu, 12 May 2016 08:43:11 +0000
Committer: Arnaldo Carvalho de Melo <ac...@redhat.com>
CommitDate: Mon, 16 May 2016 23:11:45 -0300

perf symbols: Store vdso buildid unconditionally

When unwinding callchains on a different machine, vdso info should be
available so the unwind process won't be interrupted if address falls
into vdso region. But in most cases, the addresses of sample events are
not in vdso range, the buildid of a zero hit vdso won't be stored into
perf.data.

This patch stores vdso buildid regardless of whether the vdso is hit or
not.

Signed-off-by: He Kuang <hek...@huawei.com>
Cc: Adrian Hunter <adrian...@intel.com>
Cc: Alexander Shishkin <alexander...@linux.intel.com>
Cc: Andi Kleen <a...@linux.intel.com>
Cc: David Ahern <dsa...@gmail.com>
Cc: Ekaterina Tumanova <tuma...@linux.vnet.ibm.com>
Cc: Jiri Olsa <jo...@redhat.com>
Cc: Josh Poimboeuf <jpoi...@redhat.com>
Cc: Kan Liang <kan....@intel.com>
Cc: Masami Hiramatsu <masami.hi...@hitachi.com>
Cc: Namhyung Kim <namh...@kernel.org>
Cc: Pekka Enberg <pen...@kernel.org>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Stephane Eranian <era...@google.com>
Cc: Sukadev Bhattiprolu <suk...@linux.vnet.ibm.com>
Cc: Wang Nan <wang...@huawei.com>
Link: http://lkml.kernel.org/r/1463042596-61703-3-g...@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <ac...@redhat.com>
---
tools/perf/util/build-id.c | 2 +-
tools/perf/util/dso.c | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index bff425e..67e5966 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -256,7 +256,7 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
size_t name_len;
bool in_kernel = false;

- if (!pos->hit)
+ if (!pos->hit && !dso__is_vdso(pos))
continue;

if (dso__is_vdso(pos)) {
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 3357479..75b7561 100644

Jiri Olsa

unread,
May 20, 2016, 6:00:07 AM5/20/16
to
we will need to extend LIBUNWIND_DIR or add variable,
to be able to detect this from arbitrary directory

jirka

He Kuang

unread,
May 24, 2016, 5:30:05 AM5/24/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 ++++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 4 ++++
tools/perf/util/unwind-libunwind_common.c | 10 ++++++++++
tools/perf/util/unwind.h | 3 +++
6 files changed, 51 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/arch/arm64/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..47d13a6
--- /dev/null
+++ b/tools/perf/arch/arm64/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-aarch64.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_AARCH64
+int libunwind__aarch64_reg_id(int regnum);
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__aarch64_reg_id
+
+#include <../../../arm64/util/unwind-libunwind.c>
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, aarch64), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..5b557a5 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,14 @@

#include <errno.h>
-#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"

+#ifndef LIBUNWIND_AARCH64
int libunwind__arch_reg_id(int regnum)
+#else
+int libunwind__aarch64_reg_id(int regnum)
+#endif
{
switch (regnum) {
case UNW_AARCH64_X0:
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 8ac0440..eb7cbce 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -362,6 +362,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2373130..f1b51a2 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -104,10 +104,14 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
$(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

+$(OUTPUT)util/unwind-libunwind_arm64.o: util/unwind-libunwind.c arch/arm64/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/arm64/include/libunwind -c -o $@ util/unwind-libunwind.c
+
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

libperf-y += scripting-engines/
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index 619c6c0..d19b062 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -89,6 +89,16 @@ void unwind__get_arch(struct thread *thread, struct map *map)
#endif
use_local_unwind = 0;
}
+ } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
+ if (dso_type == DSO__TYPE_64BIT) {
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ register_unwind_libunwind_ops(
+ &_Uaarch64_unwind_libunwind_ops, thread);
+#else
+ register_null_unwind_libunwind_ops(thread);
+#endif
+ use_local_unwind = 0;
+ }
}

if (use_local_unwind)
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 7dafb6e..359f756 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -60,6 +60,9 @@ register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 24, 2016, 5:30:06 AM5/24/16
to
Pass LIBUNWIND_DIR to feature check flags for remote libunwind
tests. So perf can be able to detect remote libunwind libraries from
arbitrary directory.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..6f9f566 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -67,9 +67,18 @@ endif
#
# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
#
+
+libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code))
+define libunwind_arch_set_flags_code
+ FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
+ FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
+endef
+
ifdef LIBUNWIND_DIR
LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
+ LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64
+ $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch)))
endif
LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS)

--
1.8.5.2

He Kuang

unread,
May 24, 2016, 5:30:06 AM5/24/16
to
v4 url:
http://thread.gmane.org/gmane.linux.kernel/2224430

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
For using remote libunwind libraries, reference this:
http://thread.gmane.org/gmane.linux.kernel/2224430

and now we can use LIBUNWIND_DIR to specific custom dirctories
containing libunwind libs.

v5:

- Support LIBUNWIND_DIR args for detect remote libunwind libraries.
- Change patch 2/5 commit messages for better understanding.
- Self test for local (un)supported, remote un(supported) cases and
fix some bugs in v4.

v4:

- Move reference of buildid dir to 'symfs/.debug' if --symfs is
given.
- Split makefile changes out from patch 'Add support for
cross-platform unwind'.
- Use existing code normalize_arch() for testing the arch of
perf.data.

v3:

- Remove --vdso option, store vdso buildid in perf.data and let perf
fetch it automatically.
- Use existing dso__type() function to test if dso is 32-bit or
64-bit.

v2:

- Explain the reason why we can omit dwarf judgement when recording
in commit message.
- Elaborate on why we need to add a custom vdso path option, and
change the type name to DSO_BINARY_TYPE__VDSO.
- Hide the build tests status for cross platform unwind.
- Keep generic version of libunwind-debug-frame test.
- Put 32/64-bit test functions into separate patch.
- Extract unwind related functions to unwind-libunwind.c and add new
file for common parts used by both local and remote unwind.
- Eliminate most of the ifdefs in .c file.

Thanks.

He Kuang (5):
perf tools: Use LIBUNWIND_DIR for remote libunwind feature check
perf tools: Show warnings for unsupported cross-platform unwind
perf callchain: Add support for cross-platform unwind
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

.../arch/arm64/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/arm64/util/unwind-libunwind.c | 5 +-
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
.../arch/x86/include/libunwind/libunwind-arch.h | 18 ++++
tools/perf/arch/x86/util/Build | 2 +-
tools/perf/arch/x86/util/unwind-libunwind.c | 19 +++-
tools/perf/config/Makefile | 49 ++++++++-
tools/perf/util/Build | 13 ++-
tools/perf/util/thread.c | 7 +-
tools/perf/util/thread.h | 17 +++-
tools/perf/util/unwind-libunwind.c | 49 +++++++--
tools/perf/util/unwind-libunwind_common.c | 109 +++++++++++++++++++++
tools/perf/util/unwind.h | 50 ++++++++--
14 files changed, 323 insertions(+), 36 deletions(-)

He Kuang

unread,
May 24, 2016, 5:30:06 AM5/24/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
.../perf/arch/x86/include/libunwind/libunwind-arch.h | 18 ++++++++++++++++++
tools/perf/arch/x86/util/unwind-libunwind.c | 19 ++++++++++++++-----
tools/perf/util/Build | 6 ++++++
tools/perf/util/unwind-libunwind_common.c | 6 ++++--
tools/perf/util/unwind.h | 5 +++++
5 files changed, 47 insertions(+), 7 deletions(-)
create mode 100644 tools/perf/arch/x86/include/libunwind/libunwind-arch.h

diff --git a/tools/perf/arch/x86/include/libunwind/libunwind-arch.h b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
new file mode 100644
index 0000000..be8c675
--- /dev/null
+++ b/tools/perf/arch/x86/include/libunwind/libunwind-arch.h
@@ -0,0 +1,18 @@
+#ifndef _LIBUNWIND_ARCH_H
+#define _LIBUNWIND_ARCH_H
+
+#include <libunwind-x86.h>
+#include <../perf_regs.h>
+#include <../../../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#define LIBUNWIND_X86_32
+int libunwind__x86_reg_id(int regnum);
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#include <../../../x86/util/unwind-libunwind.c>
+
+#define UNWT_PREFIX UNW_PASTE(UNW_PASTE(_U, x86), _)
+#define UNWT_OBJ(fn) UNW_PASTE(UNWT_PREFIX, fn)
+
+#endif /* _LIBUNWIND_ARCH_H */
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..28831d8 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -1,12 +1,18 @@

#include <errno.h>
+#if defined(LIBUNWIND_X86_32)
+#include <libunwind-x86.h>
+#elif defined(LIBUNWIND_X86_64)
+#include <libunwind-x86_64.h>
+#elif defined(HAVE_LIBUNWIND_LOCAL_SUPPORT)
#include <libunwind.h>
+#endif
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"

-#ifdef HAVE_ARCH_X86_64_SUPPORT
-int libunwind__arch_reg_id(int regnum)
+#if !defined(REMOTE_UNWIND_LIBUNWIND) && defined(HAVE_ARCH_X86_64_SUPPORT)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

@@ -69,8 +75,11 @@ int libunwind__arch_reg_id(int regnum)

return id;
}
-#else
-int libunwind__arch_reg_id(int regnum)
+#endif
+
+#if !defined(REMOTE_UNWIND_LIBUNWIND) && !defined(HAVE_ARCH_X86_64_SUPPORT) || \
+ defined(LIBUNWIND_X86_32)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

@@ -109,4 +118,4 @@ int libunwind__arch_reg_id(int regnum)

return id;
}
-#endif /* HAVE_ARCH_X86_64_SUPPORT */
+#endif
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index ce69721..2373130 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,3 +1,5 @@
+include ../scripts/Makefile.include
+
libperf-y += alias.o
libperf-y += annotate.o
libperf-y += build-id.o
@@ -101,6 +103,10 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind_common.o
+libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
+
+$(OUTPUT)util/unwind-libunwind_x86_32.o: util/unwind-libunwind.c arch/x86/util/unwind-libunwind.c
+ $(QUIET_CC)$(CC) $(CFLAGS) -DREMOTE_UNWIND_LIBUNWIND -Iarch/x86/include/libunwind -c -o $@ util/unwind-libunwind.c

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
index f44833b..619c6c0 100644
--- a/tools/perf/util/unwind-libunwind_common.c
+++ b/tools/perf/util/unwind-libunwind_common.c
@@ -82,9 +82,11 @@ void unwind__get_arch(struct thread *thread, struct map *map)
if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT) {
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("unwind: target platform=%s is not implemented\n", arch);
-#endif
+ register_unwind_libunwind_ops(
+ &_Ux86_unwind_libunwind_ops, thread);
+#else
register_null_unwind_libunwind_ops(thread);
+#endif
use_local_unwind = 0;
}
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index e170be7..7dafb6e 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -56,6 +56,11 @@ static inline void unwind__get_arch(struct thread *thread __maybe_unused,

He Kuang

unread,
May 24, 2016, 5:30:07 AM5/24/16
to
Currently, perf script uses host unwind methods to parse perf.data
callchain info regardless of the target architecture. So we get wrong
result without any warnings when unwinding callchains of x86(32-bit)
on x86(64-bit) machine.

This patch shows warning messages when we do remote unwind x86(32-bit)
on other machines. Same thing for other platforms will be added in
next patches.

Common functions which will be used by both local unwind and remote
unwind are separated into new file 'unwind-libunwind_common.c'.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
tools/perf/config/Makefile | 5 +++++
tools/perf/util/Build | 1 +
tools/perf/util/thread.c | 2 ++
tools/perf/util/unwind-libunwind_common.c | 34 +++++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 5 +++++
7 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/util/unwind-libunwind_common.c

diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index e83c8ce..fa090a9 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -102,7 +102,7 @@ static int lookup_triplets(const char *const *triplets, const char *name)
* Return architecture name in a normalized form.
* The conversion logic comes from the Makefile.
*/
-static const char *normalize_arch(char *arch)
+const char *normalize_arch(char *arch)
{
if (!strcmp(arch, "x86_64"))
return "x86";
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h
index 7529cfb..6b01c73 100644
--- a/tools/perf/arch/common.h
+++ b/tools/perf/arch/common.h
@@ -6,5 +6,6 @@
extern const char *objdump_path;

int perf_env__lookup_objdump(struct perf_env *env);
+const char *normalize_arch(char *arch);

#endif /* ARCH_PERF_COMMON_H */
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 6f9f566..c9e1625 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -354,6 +354,11 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ ifeq ($(feature-libunwind-x86), 1)
+ $(call detected,CONFIG_LIBUNWIND_X86)
+ CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
+ endif
+
ifneq ($(feature-libunwind), 1)
msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR);
NO_LIBUNWIND := 1
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8c6c8a0..25c31fb 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -100,6 +100,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o

libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_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 45fcb71..3043113 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -205,6 +205,8 @@ void thread__insert_map(struct thread *thread, struct map *map)
{
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);
+
+ unwind__get_arch(thread, map);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/unwind-libunwind_common.c b/tools/perf/util/unwind-libunwind_common.c
new file mode 100644
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 12790cf..889d630 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h

He Kuang

unread,
May 24, 2016, 5:30:09 AM5/24/16
to
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 <hek...@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
+ 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)

+#else
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}
#endif
#else
static inline int
@@ -47,14 +67,11 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
return 0;
}

-static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
-{
- return 0;
-}
-
static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
static inline void unwind__get_arch(struct thread *thread __maybe_unused,
struct map *map __maybe_unused) {}
+static inline void
+register_null_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

Jiri Olsa

unread,
May 26, 2016, 1:50:06 PM5/26/16
to
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.

hi,
I think this is too complex and error prone, I'd rather see it split
to several pieces. Basically every logicaly single piece should be
in separate patch for better readablebility and review.

I might be missing some but I'd mainly sepatate following:

- introducing struct unwind_libunwind_ops for local unwind
- moving unwind__prepare_access from thread_new into thread__insert_map
- keep unwind__prepare_access name instead of unwind__get_arch
and keep the return value, we need to bail out in case of error
- I wouldn't use null ops.. just check for for ops == NULL in wrapper function

- I understand we need to compile 3 objects from unwind-libunwind.c,
how about we create 3 files like:

util/unwind-libunwind-local.c
util/unwind-libunwind-x86_32.c
util/unwind-libunwind-arm64.c

which would setup all necessary defines and include unwind-libunwind.c like:

---
/* comments explaining every define ;-) */
...
#define LOCAL... REMOTE..
...
#include <util/unwind-libunwind-local.c>
...
----

this way we will keep all the special setup for given unwind object
in one place and you can also use simple rule in the Build file like
without defining special rule:

libperf-$(CONFIG_LIBUNWIND_X86) += unwind-libunwind_x86_32.o
libperf-$(CONFIG_LIBUNWIND_AARCH64) += unwind-libunwind_arm64.o

the same way for the arch object:

arch/x86/util/unwind-libunwind-local.c
arch/x86/util/unwind-libunwind-x86_32.c


Not sure I thought everything through, but I think this way
we'll keep it more maintainable and readable..

let me know what you think

thanks,
jirka

Jiri Olsa

unread,
May 26, 2016, 2:00:07 PM5/26/16
to
On Tue, May 24, 2016 at 09:20:27AM +0000, He Kuang wrote:
I think this should go along with other ops wrappers like
unwind__flush_access/unwind__finish_access.. in unwind-libunwind_common.c

jirka

Jiri Olsa

unread,
May 26, 2016, 2:00:07 PM5/26/16
to
is there a reason for using libunwind define for the symbol prefix?
what's the '_U' and all those '__' for? why dont we use simple macro
for arch prefix?

this explanation would be great to have in those wrapper obects
I meantioned in earlier email:
util/unwind-libunwind-local.c
util/unwind-libunwind-x86_32.c
util/unwind-libunwind-arm64.c


thanks,
jirka

Hekuang

unread,
May 27, 2016, 3:20:06 AM5/27/16
to
hi

在 2016/5/27 1:42, Jiri Olsa 写道:
> 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.
> hi,
> I think this is too complex and error prone, I'd rather see it split
> to several pieces. Basically every logicaly single piece should be
> in separate patch for better readablebility and review.
>
> I might be missing some but I'd mainly sepatate following:
>
> - introducing struct unwind_libunwind_ops for local unwind
> - moving unwind__prepare_access from thread_new into thread__insert_map
> - keep unwind__prepare_access name instead of unwind__get_arch
> and keep the return value, we need to bail out in case of error
> - I wouldn't use null ops.. just check for for ops == NULL in wrapper function

OK
The only concern is that, if later we support more platforms,
there will be too much files named as 'tools/perf/util/unwind-libunwind*.c'
Is it acceptable or not?

And I thought all files belongs to specific archs should
go to folder under 'tools/perf/arch/xxx', is that right?

Thanks.
> thanks,
> jirka
>

Jiri Olsa

unread,
May 27, 2016, 3:40:06 AM5/27/16
to
On Fri, May 27, 2016 at 03:13:04PM +0800, Hekuang wrote:

SNIP
hum, I wouldn't worry about that.. but you're right,
let's put them under arch

thanks,
jirka

Hekuang

unread,
May 27, 2016, 4:10:05 AM5/27/16
to
But only 'tools/perf/arch/$(host platform)' will be built, in our case,
we should built the unwind-libunwind-$(arch) as long as we have
the remote libunwind libraries. So, I think there's a conflict in the
existing build script and not easy to 'put them under arch'. That's why
I choose a complex way in my previous patch.

Do you have some suggestions?
>
> thanks,
> jirka
>

Jiri Olsa

unread,
May 27, 2016, 4:50:08 AM5/27/16
to
On Fri, May 27, 2016 at 04:02:59PM +0800, Hekuang wrote:

SNIP

> > > The only concern is that, if later we support more platforms,
> > > there will be too much files named as 'tools/perf/util/unwind-libunwind*.c'
> > > Is it acceptable or not?
> > >
> > > And I thought all files belongs to specific archs should
> > > go to folder under 'tools/perf/arch/xxx', is that right?
> > hum, I wouldn't worry about that.. but you're right,
> > let's put them under arch
>
> But only 'tools/perf/arch/$(host platform)' will be built, in our case,
> we should built the unwind-libunwind-$(arch) as long as we have
> the remote libunwind libraries. So, I think there's a conflict in the
> existing build script and not easy to 'put them under arch'. That's why
> I choose a complex way in my previous patch.

we dive into arch dirs via:

arch/Build:
libperf-y += $(ARCH)/

that's not changed AFAICS..

jirka

He Kuang

unread,
May 28, 2016, 8:10:06 AM5/28/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/Build | 1 +
tools/perf/arch/x86/util/unwind-libunwind.c | 7 ++++---
tools/perf/arch/x86/util/unwind-libunwind_x86_32.c | 21 +++++++++++++++++++++
tools/perf/util/unwind-libunwind-local.c | 4 ++++
tools/perf/util/unwind-libunwind.c | 19 +++++++++++++------
tools/perf/util/unwind.h | 10 ++++++++++
6 files changed, 53 insertions(+), 9 deletions(-)
create mode 100644 tools/perf/arch/x86/util/unwind-libunwind_x86_32.c

diff --git a/tools/perf/arch/Build b/tools/perf/arch/Build
index 109eb75..3fc4af1 100644
--- a/tools/perf/arch/Build
+++ b/tools/perf/arch/Build
@@ -1,2 +1,3 @@
libperf-y += common.o
libperf-y += $(ARCH)/
+libperf-$(CONFIG_LIBUNWIND_X86) += x86/util/unwind-libunwind_x86_32.o
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..3b0be69 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -1,12 +1,13 @@
-
+#ifndef REMOTE_UNWIND_LIBUNWIND
#include <errno.h>
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
+#endif

#ifdef HAVE_ARCH_X86_64_SUPPORT
-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

@@ -70,7 +71,7 @@ int libunwind__arch_reg_id(int regnum)
return id;
}
#else
-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

diff --git a/tools/perf/arch/x86/util/unwind-libunwind_x86_32.c b/tools/perf/arch/x86/util/unwind-libunwind_x86_32.c
new file mode 100644
index 0000000..9d5359e
--- /dev/null
+++ b/tools/perf/arch/x86/util/unwind-libunwind_x86_32.c
@@ -0,0 +1,21 @@
+#define REMOTE_UNWIND_LIBUNWIND
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#include "unwind.h"
+#include "debug.h"
+#include "libunwind-x86.h"
+#include <../../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#undef HAVE_ARCH_X86_64_SUPPORT
+#include "unwind-libunwind.c"
+
+#undef NO_LIBUNWIND_DEBUG_FRAME
+#define NO_LIBUNWIND_DEBUG_FRAME
+#include "util/unwind-libunwind-local.c"
+
+int register_x86_32_unwind_libunwind_ops(struct thread *thread)
+{
+ thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
+ return 0;
+}
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index b391e3e..849fec1 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -5,7 +5,9 @@
#include <unistd.h>
#include <sys/mman.h>
#include <linux/list.h>
+#ifndef REMOTE_UNWIND_LIBUNWIND
#include <libunwind.h>
+#endif
#include "callchain.h"
#include "thread.h"
#include "session.h"
@@ -671,7 +673,9 @@ _unwind_libunwind_ops = {
.get_entries = _unwind__get_entries,
};

+#ifndef REMOTE_UNWIND_LIBUNWIND
void register_local_unwind_libunwind_ops(struct thread *thread)
{
thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
}
+#endif
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 037ee72..c1d9d36 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -8,6 +8,9 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
{
const char *arch;
enum dso_type dso_type;
+ int use_local_unwind = 1;
+ int ret;
+ int (*register_func)(struct thread *thread) = NULL;

if (!thread->mg->machine->env)
return -1;
@@ -22,16 +25,20 @@ int unwind__prepare_access(struct thread *thread, struct map *map)

arch = normalize_arch(thread->mg->machine->env->arch);

- if (!strcmp(arch, "x86")) {
+ if (!strcmp(arch, "x86"))
if (dso_type != DSO__TYPE_64BIT)
-#ifdef HAVE_LIBUNWIND_X86_SUPPORT
- pr_err("unwind: target platform=%s is not implemented\n", arch);
-#else
+ register_func = register_x86_32_unwind_libunwind_ops;
+
+ if (register_func) {
+ ret = register_func(thread);
+ if (!ret)
+ use_local_unwind = 0;
+ else
pr_err("unwind: target platform=%s is not supported\n", arch);
-#endif
}

- register_local_unwind_libunwind_ops(thread);
+ if (use_local_unwind)
+ register_local_unwind_libunwind_ops(thread);

return thread->unwind_libunwind_ops->prepare_access(thread);
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 9e4f545..43f9f66 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -46,6 +46,16 @@ static inline void
register_local_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}
#endif

+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+int register_x86_32_unwind_libunwind_ops(struct thread *thread);
+#else
+static inline int
+register_x86_32_unwind_libunwind_ops(struct thread *thread __maybe_unused)
+{
+ return -1;
+}

He Kuang

unread,
May 28, 2016, 8:10:07 AM5/28/16
to
v5 url:
http://thread.gmane.org/gmane.linux.kernel/2226821

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
v6:
By following the advises from Jiri Olsa:

- Introducing struct unwind_libunwind_ops for local unwind
- Move unwind__prepare_access from thread_new into thread__insert_map
- Extract local libunwind code out of unwind-libunwind.c
- Other changes mentioned in v5 mails.

Thanks.

He Kuang (11):
perf tools: Use LIBUNWIND_DIR for remote libunwind feature check
perf tools: Decouple thread->address_space on libunwind
perf tools: Introducing struct unwind_libunwind_ops for local unwind
perf tools: Move unwind__prepare_access from thread_new into
thread__insert_map
perf tools: Separate local/remote libunwind config
perf tools: Extract local libunwind code out of unwind-libunwind.c
perf tools: Export normalize_arch() function
perf tools: Show warnings for unsupported cross-platform unwind
perf tools: Change fixed name of libunwind__arch_reg_id to macro
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

tools/perf/arch/Build | 2 +
tools/perf/arch/arm/util/Build | 2 +-
tools/perf/arch/arm64/util/Build | 2 +-
tools/perf/arch/arm64/util/unwind-libunwind.c | 4 +-
.../perf/arch/arm64/util/unwind-libunwind_arm64.c | 22 +
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
tools/perf/arch/x86/util/Build | 2 +-
tools/perf/arch/x86/util/unwind-libunwind.c | 7 +-
tools/perf/arch/x86/util/unwind-libunwind_x86_32.c | 21 +
tools/perf/config/Makefile | 50 +-
tools/perf/util/Build | 1 +
tools/perf/util/thread.c | 5 +-
tools/perf/util/thread.h | 17 +-
tools/perf/util/unwind-libunwind-local.c | 681 ++++++++++++++++++++
tools/perf/util/unwind-libunwind.c | 697 ++-------------------
tools/perf/util/unwind.h | 41 +-
17 files changed, 884 insertions(+), 673 deletions(-)
create mode 100644 tools/perf/arch/arm64/util/unwind-libunwind_arm64.c
create mode 100644 tools/perf/arch/x86/util/unwind-libunwind_x86_32.c
create mode 100644 tools/perf/util/unwind-libunwind-local.c

--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
Export normalize_arch() function, so other part of perf can get
normalized form of arch string.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index e83c8ce..fa090a9 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -102,7 +102,7 @@ static int lookup_triplets(const char *const *triplets, const char *name)
* Return architecture name in a normalized form.
* The conversion logic comes from the Makefile.
*/
-static const char *normalize_arch(char *arch)
+const char *normalize_arch(char *arch)
{
if (!strcmp(arch, "x86_64"))
return "x86";
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h
index 7529cfb..6b01c73 100644
--- a/tools/perf/arch/common.h
+++ b/tools/perf/arch/common.h
@@ -6,5 +6,6 @@
extern const char *objdump_path;

int perf_env__lookup_objdump(struct perf_env *env);
+const char *normalize_arch(char *arch);

#endif /* ARCH_PERF_COMMON_H */
--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
Currently, perf script uses host unwind methods to parse perf.data
callchain info regardless of the target architecture. So we get wrong
result without any warnings when unwinding callchains of x86(32-bit)
on x86(64-bit) machine.

This patch shows warning messages when we do remote unwind x86(32-bit)
on other machines. Same thing for other platforms will be added in
next patches.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 8 ++++++++
tools/perf/util/thread.c | 2 +-
tools/perf/util/unwind-libunwind.c | 30 +++++++++++++++++++++++++++++-
tools/perf/util/unwind.h | 5 +++--
4 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 3a304a3..e156f76 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -355,6 +355,14 @@ 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
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 045477d..7ffee25 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -203,7 +203,7 @@ void thread__insert_map(struct thread *thread, struct map *map)
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);

- unwind__prepare_access(thread);
+ unwind__prepare_access(thread, map);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 40d0453..037ee72 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -1,8 +1,36 @@
#include "unwind.h"
#include "thread.h"
+#include "session.h"
+#include "debug.h"
+#include "arch/common.h"

-int unwind__prepare_access(struct thread *thread)
+int unwind__prepare_access(struct thread *thread, struct map *map)
{
+ const char *arch;
+ enum dso_type dso_type;
+
+ if (!thread->mg->machine->env)
+ return -1;
+
+ dso_type = dso__type(map->dso, thread->mg->machine);
+ if (dso_type == DSO__TYPE_UNKNOWN)
+ return -1;
+
+ 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);
+
+ arch = normalize_arch(thread->mg->machine->env->arch);
+
+ if (!strcmp(arch, "x86")) {
+ 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_local_unwind_libunwind_ops(thread);

return thread->unwind_libunwind_ops->prepare_access(thread);
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 0122797..4de423c 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -22,11 +22,12 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
int libunwind__arch_reg_id(int regnum);
-int unwind__prepare_access(struct thread *thread);
+int unwind__prepare_access(struct thread *thread, struct map *map);
void unwind__flush_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
#else
-static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
+static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
+ struct map *map __maybe_unused)
{
return 0;
}
--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
For determine the libunwind methods to use, we should get the
32bit/64bit information from maps of a thread. When a thread is newly
created, the information is not prepared. This patch moves
unwind__prepare_access() into thread__insert_map() so we can get the
information we need from maps.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.c | 7 ++-----
tools/perf/util/unwind-libunwind.c | 7 +++----
2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 6d3900c..045477d 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -43,11 +43,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);

- register_local_unwind_libunwind_ops(thread);
-
- if (unwind__prepare_access(thread) < 0)
- goto err_thread;
-
comm_str = malloc(32);
if (!comm_str)
goto err_thread;
@@ -207,6 +202,8 @@ void thread__insert_map(struct thread *thread, struct map *map)
{
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);
+
+ unwind__prepare_access(thread);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 0277b22..93d2d8e 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -695,10 +695,9 @@ void register_local_unwind_libunwind_ops(struct thread *thread)

int unwind__prepare_access(struct thread *thread)
{
- if (thread->unwind_libunwind_ops)
- return thread->unwind_libunwind_ops->prepare_access(thread);
- else
- return 0;
+ register_local_unwind_libunwind_ops(thread);
+
+ return thread->unwind_libunwind_ops->prepare_access(thread);
}

void unwind__flush_access(struct thread *thread)
--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/Build | 1 +
tools/perf/arch/arm64/util/unwind-libunwind.c | 4 +++-
.../perf/arch/arm64/util/unwind-libunwind_arm64.c | 22 ++++++++++++++++++++++
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/unwind-libunwind.c | 6 +++++-
tools/perf/util/unwind.h | 10 ++++++++++
6 files changed, 53 insertions(+), 2 deletions(-)
create mode 100644 tools/perf/arch/arm64/util/unwind-libunwind_arm64.c

diff --git a/tools/perf/arch/Build b/tools/perf/arch/Build
index 3fc4af1..bc49295 100644
--- a/tools/perf/arch/Build
+++ b/tools/perf/arch/Build
@@ -1,3 +1,4 @@
libperf-y += common.o
libperf-y += $(ARCH)/
libperf-$(CONFIG_LIBUNWIND_X86) += x86/util/unwind-libunwind_x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += arm64/util/unwind-libunwind_arm64.o
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..c116b71 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,13 @@

+#ifndef REMOTE_UNWIND_LIBUNWIND
#include <errno.h>
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
+#endif

-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
switch (regnum) {
case UNW_AARCH64_X0:
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind_arm64.c b/tools/perf/arch/arm64/util/unwind-libunwind_arm64.c
new file mode 100644
index 0000000..ea9e7d1
--- /dev/null
+++ b/tools/perf/arch/arm64/util/unwind-libunwind_arm64.c
@@ -0,0 +1,22 @@
+#define REMOTE_UNWIND_LIBUNWIND
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__arm64_reg_id
+
+#include "unwind.h"
+#include "debug.h"
+#include "libunwind-aarch64.h"
+#include <../../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+#include "unwind-libunwind.c"
+
+#undef NO_LIBUNWIND_DEBUG_FRAME
+#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
+#define NO_LIBUNWIND_DEBUG_FRAME
+#endif
+
+#include "util/unwind-libunwind-local.c"
+
+int register_arm64_unwind_libunwind_ops(struct thread *thread)
+{
+ thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
+ return 0;
+}
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index e156f76..bb5aea8 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -363,6 +363,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index c1d9d36..5930865 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -25,9 +25,13 @@ int unwind__prepare_access(struct thread *thread, struct map *map)

arch = normalize_arch(thread->mg->machine->env->arch);

- if (!strcmp(arch, "x86"))
+ if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT)
register_func = register_x86_32_unwind_libunwind_ops;
+ } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
+ if (dso_type == DSO__TYPE_64BIT)
+ register_func = register_arm64_unwind_libunwind_ops;
+ }

if (register_func) {
ret = register_func(thread);
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 43f9f66..e203aa9 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -56,6 +56,16 @@ register_x86_32_unwind_libunwind_ops(struct thread *thread __maybe_unused)
}
#endif

+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+int register_arm64_unwind_libunwind_ops(struct thread *thread);
+#else
+static inline int
+register_arm64_unwind_libunwind_ops(struct thread *thread __maybe_unused)

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
Pass LIBUNWIND_DIR to feature check flags for remote libunwind
tests. So perf can be able to detect remote libunwind libraries from
arbitrary directory.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..6f9f566 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile

He Kuang

unread,
May 28, 2016, 8:10:08 AM5/28/16
to
For local libunwind, it uses the fixed methods to convert register id
according to the host platform, but in remote libunwind, this convert
function should be the one for remote architechture. This patch
changes the fixed name to macro and code for each remote platform can
be compiled indivadually.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/unwind-libunwind-local.c | 2 +-
tools/perf/util/unwind.h | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 5599adc..b391e3e 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -490,7 +490,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;

diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 4de423c..9e4f545 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -21,7 +21,10 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,

/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
-int libunwind__arch_reg_id(int regnum);
+#ifndef LIBUNWIND__ARCH_REG_ID
+#define LIBUNWIND__ARCH_REG_ID libunwind__arch_reg_id
+#endif
+int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct thread *thread, struct map *map);
void unwind__flush_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:09 AM5/28/16
to
This patch extracts codes related to specific arithecture out of
unwind-libunwind.c. The extrated part are only built if local
libunwind is supported.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/unwind-libunwind-local.c | 677 ++++++++++++++++++++++++++++++
tools/perf/util/unwind-libunwind.c | 694 +------------------------------
3 files changed, 679 insertions(+), 693 deletions(-)
create mode 100644 tools/perf/util/unwind-libunwind-local.c

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8c6c8a0..004fb1d 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -99,6 +99,7 @@ libperf-$(CONFIG_DWARF) += probe-finder.o
libperf-$(CONFIG_DWARF) += dwarf-aux.o

libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
new file mode 100644
index 0000000..5599adc
--- /dev/null
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -0,0 +1,677 @@
+#include <elf.h>
+#include <gelf.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <linux/list.h>
+#include <libunwind.h>
+#include "callchain.h"
+#include "thread.h"
+#include "session.h"
+#include "perf_regs.h"
+#include "unwind.h"
+#include "symbol.h"
+#include "util.h"
+#include "debug.h"
+#include "asm/bug.h"
+
+extern int
+UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+
+extern int
+UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+
+#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
+#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
+
+/* Pointer-encoding formats: */
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
+#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
+#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
+#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
+#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
+
+/* Pointer-encoding application: */
+#define DW_EH_PE_absptr 0x00 /* absolute value */
+#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
+
+/*
+ * The following are not documented by LSB v1.3, yet they are used by
+ * GCC, presumably they aren't documented by LSB since they aren't
+ * used on Linux:
+ */
+#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
+#define DW_EH_PE_aligned 0x50 /* aligned pointer */
+
+/* Flags intentionaly not handled, since they're not needed:
+ * #define DW_EH_PE_indirect 0x80
+ * #define DW_EH_PE_uleb128 0x01
+ * #define DW_EH_PE_udata2 0x02
+ * #define DW_EH_PE_sleb128 0x09
+ * #define DW_EH_PE_sdata2 0x0a
+ * #define DW_EH_PE_textrel 0x20
+ * #define DW_EH_PE_datarel 0x30
+ */
+
+struct unwind_info {
+ struct perf_sample *sample;
+ struct machine *machine;
+ struct thread *thread;
+};
+
+#define dw_read(ptr, type, end) ({ \
+ type *__p = (type *) ptr; \
+ type __v; \
+ if ((__p + 1) > (type *) end) \
+ return -EINVAL; \
+ __v = *__p++; \
+ ptr = (typeof(ptr)) __p; \
+ __v; \
+ })
+
+static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
+ u8 encoding)
+{
+ u8 *cur = *p;
+ *val = 0;
+
+ switch (encoding) {
+ case DW_EH_PE_omit:
+ *val = 0;
+ goto out;
+ case DW_EH_PE_ptr:
+ *val = dw_read(cur, unsigned long, end);
+ goto out;
+ default:
+ break;
+ }
+
+ switch (encoding & DW_EH_PE_APPL_MASK) {
+ case DW_EH_PE_absptr:
+ break;
+ case DW_EH_PE_pcrel:
+ *val = (unsigned long) cur;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((encoding & 0x07) == 0x00)
+ encoding |= DW_EH_PE_udata4;
+
+ switch (encoding & DW_EH_PE_FORMAT_MASK) {
+ case DW_EH_PE_sdata4:
+ *val += dw_read(cur, s32, end);
+ break;
+ case DW_EH_PE_udata4:
+ *val += dw_read(cur, u32, end);
+ break;
+ case DW_EH_PE_sdata8:
+ *val += dw_read(cur, s64, end);
+ break;
+ case DW_EH_PE_udata8:
+ *val += dw_read(cur, u64, end);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ out:
+ *p = cur;
+ return 0;
+}
+
+#define dw_read_encoded_value(ptr, end, enc) ({ \
+ u64 __v; \
+ if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
+ return -EINVAL; \
+ } \
+ __v; \
+ })
+
+static u64 elf_section_offset(int fd, const char *name)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ u64 offset = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return 0;
+
+ do {
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ break;
+
+ if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
+ break;
+
+ offset = shdr.sh_offset;
+ } while (0);
+
+ elf_end(elf);
+ return offset;
+}
+
+#ifndef NO_LIBUNWIND_DEBUG_FRAME
+static int elf_is_exec(int fd, const char *name)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ int retval = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return 0;
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out;
+
+ retval = (ehdr.e_type == ET_EXEC);
+
+out:
+ elf_end(elf);
+ pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
+ return retval;
+}
+#endif
+
+struct table_entry {
+ u32 start_ip_offset;
+ u32 fde_offset;
+};
+
+struct eh_frame_hdr {
+ unsigned char version;
+ unsigned char eh_frame_ptr_enc;
+ unsigned char fde_count_enc;
+ unsigned char table_enc;
+
+ /*
+ * The rest of the header is variable-length and consists of the
+ * following members:
+ *
+ * encoded_t eh_frame_ptr;
+ * encoded_t fde_count;
+ */
+
+ /* A single encoded pointer should not be more than 8 bytes. */
+ u64 enc[2];
+
+ /*
+ * struct {
+ * encoded_t start_ip;
+ * encoded_t fde_addr;
+ * } binary_search_table[fde_count];
+ */
+ char data[0];
+} __packed;
+
+static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
+ u64 offset, u64 *table_data, u64 *segbase,
+ u64 *fde_count)
+{
+ struct eh_frame_hdr hdr;
+ u8 *enc = (u8 *) &hdr.enc;
+ u8 *end = (u8 *) &hdr.data;
+ ssize_t r;
+
+ r = dso__data_read_offset(dso, machine, offset,
+ (u8 *) &hdr, sizeof(hdr));
+ if (r != sizeof(hdr))
+ return -EINVAL;
+
+ /* We dont need eh_frame_ptr, just skip it. */
+ dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
+
+ *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
+ *segbase = offset;
+ *table_data = (enc - (u8 *) &hdr) + offset;
+ return 0;
+}
+
+static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
+ u64 *table_data, u64 *segbase,
+ u64 *fde_count)
+{
+ int ret = -EINVAL, fd;
+ u64 offset = dso->data.eh_frame_hdr_offset;
+
+ if (offset == 0) {
+ fd = dso__data_get_fd(dso, machine);
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Check the .eh_frame section for unwinding info */
+ offset = elf_section_offset(fd, ".eh_frame_hdr");
+ dso->data.eh_frame_hdr_offset = offset;
+ dso__data_put_fd(dso);
+ }
+
+ if (offset)
+ ret = unwind_spec_ehframe(dso, machine, offset,
+ table_data, segbase,
+ fde_count);
+
+ return ret;
+}
+
+#ifndef NO_LIBUNWIND_DEBUG_FRAME
+static int read_unwind_spec_debug_frame(struct dso *dso,
+ struct machine *machine, u64 *offset)
+{
+ int fd;
+ u64 ofs = dso->data.debug_frame_offset;
+
+ if (ofs == 0) {
+ fd = dso__data_get_fd(dso, machine);
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Check the .debug_frame section for unwinding info */
+ ofs = elf_section_offset(fd, ".debug_frame");
+ dso->data.debug_frame_offset = ofs;
+ dso__data_put_fd(dso);
+ }
+
+ *offset = ofs;
+ if (*offset)
+ return 0;
+
+ return -EINVAL;
+}
+#endif
+
+static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
+{
+ struct addr_location al;
+
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
+ MAP__FUNCTION, ip, &al);
+ if (!al.map) {
+ /*
+ * We've seen cases (softice) where DWARF unwinder went
+ * through non executable mmaps, which we need to lookup
+ * in MAP__VARIABLE tree.
+ */
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
+ MAP__VARIABLE, ip, &al);
+ }
+ return al.map;
+}
+
+static int
+find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct map *map;
+ unw_dyn_info_t di;
+ u64 table_data, segbase, fde_count;
+ int ret = -EINVAL;
+
+ map = find_map(ip, ui);
+ if (!map || !map->dso)
+ return -EINVAL;
+
+ pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
+
+ /* Check the .eh_frame section for unwinding info */
+ if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
+ &table_data, &segbase, &fde_count)) {
+ memset(&di, 0, sizeof(di));
+ di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+ di.start_ip = map->start;
+ di.end_ip = map->end;
+ di.u.rti.segbase = map->start + segbase;
+ di.u.rti.table_data = map->start + table_data;
+ di.u.rti.table_len = fde_count * sizeof(struct table_entry)
+ / sizeof(unw_word_t);
+ ret = dwarf_search_unwind_table(as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+
+#ifndef NO_LIBUNWIND_DEBUG_FRAME
+ /* Check the .debug_frame section for unwinding info */
+ if (ret < 0 &&
+ !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
+ int fd = dso__data_get_fd(map->dso, ui->machine);
+ int is_exec = elf_is_exec(fd, map->dso->name);
+ unw_word_t base = is_exec ? 0 : map->start;
+ const char *symfile;
+
+ if (fd >= 0)
+ dso__data_put_fd(map->dso);
+
+ symfile = map->dso->symsrc_filename ?: map->dso->name;
+
+ memset(&di, 0, sizeof(di));
+ if (dwarf_find_debug_frame(0, &di, ip, base, symfile,
+ map->start, map->end))
+ return dwarf_search_unwind_table(as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+#endif
+
+ return ret;
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int
+get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
+ unw_word_t *data)
+{
+ struct map *map;
+ ssize_t size;
+
+ map = find_map(addr, ui);
+ if (!map) {
+ pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
+ return -1;
+ }
+
+ if (!map->dso)
+ return -1;
+
+ size = dso__data_read_addr(map->dso, map, ui->machine,
+ addr, (u8 *) data, sizeof(*data));
+
+ return !(size == sizeof(*data));
+}
+
+static int access_mem(unw_addr_space_t __maybe_unused as,
+ unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct stack_dump *stack = &ui->sample->user_stack;
+ u64 start, end;
+ int offset;
+ int ret;
+
+ /* Don't support write, probably not needed. */
+ if (__write || !stack || !ui->sample->user_regs.regs) {
+ *valp = 0;
+ return 0;
+ }
+
+ ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
+ if (ret)
+ return ret;
+
+ end = start + stack->size;
+
+ /* Check overflow. */
+ if (addr + sizeof(unw_word_t) < addr)
+ return -EINVAL;
+
+ if (addr < start || addr + sizeof(unw_word_t) >= end) {
+ ret = access_dso_mem(ui, addr, valp);
+ if (ret) {
+ pr_debug("unwind: access_mem %p not inside range"
+ " 0x%" PRIx64 "-0x%" PRIx64 "\n",
+ (void *) (uintptr_t) addr, start, end);
+ *valp = 0;
+ return ret;
+ }
+ return 0;
+ }
+
+ offset = addr - start;
+ *valp = *(unw_word_t *)&stack->data[offset];
+ pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
+ (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
+ return 0;
+}
+
+static int access_reg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ int id, ret;
+ u64 val;
+
+ /* Don't support write, I suspect we don't need it. */
+ if (__write) {
+ pr_err("unwind: access_reg w %d\n", regnum);
+ return 0;
+ }
+
+ if (!ui->sample->user_regs.regs) {
+ *valp = 0;
+ return 0;
+ }
+
+ id = libunwind__arch_reg_id(regnum);
+ if (id < 0)
+ return -EINVAL;
+
+ ret = perf_reg_value(&val, &ui->sample->user_regs, id);
+ if (ret) {
+ pr_err("unwind: can't read reg %d\n", regnum);
+ return ret;
+ }
+
+ *valp = (unw_word_t) val;
+ pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
+ return 0;
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int entry(u64 ip, struct thread *thread,
+ unwind_entry_cb_t cb, void *arg)
+{
+ struct unwind_entry e;
+ struct addr_location al;
+
+ thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
+ MAP__FUNCTION, ip, &al);
+
+ e.ip = ip;
+ e.map = al.map;
+ e.sym = al.sym;
+
+ pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
+ al.sym ? al.sym->name : "''",
+ ip,
+ al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
+
+ return cb(&e, arg);
+}
+
+static void display_error(int err)
+{
+ switch (err) {
+ case UNW_EINVAL:
+ pr_err("unwind: Only supports local.\n");
+ break;
+ case UNW_EUNSPEC:
+ pr_err("unwind: Unspecified error.\n");
+ break;
+ case UNW_EBADREG:
+ pr_err("unwind: Register unavailable.\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+};
+
+static int _unwind__prepare_access(struct thread *thread)
+{
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return 0;
+
+ thread->addr_space = unw_create_addr_space(&accessors, 0);
+ if (!thread->addr_space) {
+ pr_err("unwind: Can't create unwind address space.\n");
+ return -ENOMEM;
+ }
+
+ unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL);
+ return 0;
+}
+
+static void _unwind__flush_access(struct thread *thread)
+{
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return;
+
+ unw_flush_cache(thread->addr_space, 0, 0);
+}
+
+static void _unwind__finish_access(struct thread *thread)
+{
+ if (callchain_param.record_mode != CALLCHAIN_DWARF)
+ return;
+
+ unw_destroy_addr_space(thread->addr_space);
+}
+
+static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
+ void *arg, int max_stack)
+{
+ u64 val;
+ unw_word_t ips[max_stack];
+ unw_addr_space_t addr_space;
+ unw_cursor_t c;
+ int ret, i = 0;
+
+ ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP);
+ if (ret)
+ return ret;
+
+ ips[i++] = (unw_word_t) val;
+
+ /*
+ * If we need more than one entry, do the DWARF
+ * unwind itself.
+ */
+ if (max_stack - 1 > 0) {
+ WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
+ addr_space = ui->thread->addr_space;
+
+ if (addr_space == NULL)
+ return -1;
+
+ ret = unw_init_remote(&c, addr_space, ui);
+ if (ret)
+ display_error(ret);
+
+ while (!ret && (unw_step(&c) > 0) && i < max_stack) {
+ unw_get_reg(&c, UNW_REG_IP, &ips[i]);
+ ++i;
+ }
+
+ max_stack = i;
+ }
+
+ /*
+ * Display what we got based on the order setup.
+ */
+ for (i = 0; i < max_stack && !ret; i++) {
+ int j = i;
+
+ if (callchain_param.order == ORDER_CALLER)
+ j = max_stack - i - 1;
+ ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
+ }
+
+ return ret;
+}
+
+static int _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,
+ .thread = thread,
+ .machine = thread->mg->machine,
+ };
+
+ if (!data->user_regs.regs)
+ return -EINVAL;
+
+ if (max_stack <= 0)
+ return -EINVAL;
+
+ return get_entries(&ui, cb, arg, max_stack);
+}
+
+static struct unwind_libunwind_ops
+_unwind_libunwind_ops = {
+ .prepare_access = _unwind__prepare_access,
+ .flush_access = _unwind__flush_access,
+ .finish_access = _unwind__finish_access,
+ .get_entries = _unwind__get_entries,
+};
+
+void register_local_unwind_libunwind_ops(struct thread *thread)
+{
+ thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
+}
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 93d2d8e..40d0453 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -1,697 +1,5 @@
-/*
- * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
- *
- * Lots of this code have been borrowed or heavily inspired from parts of
- * the libunwind 0.99 code which are (amongst other contributors I may have
- * forgotten):
- *
- * Copyright (C) 2002-2007 Hewlett-Packard Co
- * Contributed by David Mosberger-Tang <dav...@hpl.hp.com>
- *
- * And the bugs have been added by:
- *
- * Copyright (C) 2010, Frederic Weisbecker <fwei...@gmail.com>
- * Copyright (C) 2012, Jiri Olsa <jo...@redhat.com>
- *
- */
-
-#include <elf.h>
-#include <gelf.h>
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <linux/list.h>
-#include <libunwind.h>
-#include "callchain.h"
-#include "thread.h"
-#include "session.h"
-#include "perf_regs.h"
#include "unwind.h"
-#include "symbol.h"
-#include "util.h"
-#include "debug.h"
-#include "asm/bug.h"
-
-extern int
-UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip,
- unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
-#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
-
-/* Pointer-encoding formats: */
-#define DW_EH_PE_omit 0xff
-#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
-#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
-#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
-#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
-#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
-
-/* Pointer-encoding application: */
-#define DW_EH_PE_absptr 0x00 /* absolute value */
-#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
-
-/*
- * The following are not documented by LSB v1.3, yet they are used by
- * GCC, presumably they aren't documented by LSB since they aren't
- * used on Linux:
- */
-#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
-#define DW_EH_PE_aligned 0x50 /* aligned pointer */
-
-/* Flags intentionaly not handled, since they're not needed:
- * #define DW_EH_PE_indirect 0x80
- * #define DW_EH_PE_uleb128 0x01
- * #define DW_EH_PE_udata2 0x02
- * #define DW_EH_PE_sleb128 0x09
- * #define DW_EH_PE_sdata2 0x0a
- * #define DW_EH_PE_textrel 0x20
- * #define DW_EH_PE_datarel 0x30
- */
-
-struct unwind_info {
- struct perf_sample *sample;
- struct machine *machine;
- struct thread *thread;
-};
-
-#define dw_read(ptr, type, end) ({ \
- type *__p = (type *) ptr; \
- type __v; \
- if ((__p + 1) > (type *) end) \
- return -EINVAL; \
- __v = *__p++; \
- ptr = (typeof(ptr)) __p; \
- __v; \
- })
-
-static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
- u8 encoding)
-{
- u8 *cur = *p;
- *val = 0;
-
- switch (encoding) {
- case DW_EH_PE_omit:
- *val = 0;
- goto out;
- case DW_EH_PE_ptr:
- *val = dw_read(cur, unsigned long, end);
- goto out;
- default:
- break;
- }
-
- switch (encoding & DW_EH_PE_APPL_MASK) {
- case DW_EH_PE_absptr:
- break;
- case DW_EH_PE_pcrel:
- *val = (unsigned long) cur;
- break;
- default:
- return -EINVAL;
- }
-
- if ((encoding & 0x07) == 0x00)
- encoding |= DW_EH_PE_udata4;
-
- switch (encoding & DW_EH_PE_FORMAT_MASK) {
- case DW_EH_PE_sdata4:
- *val += dw_read(cur, s32, end);
- break;
- case DW_EH_PE_udata4:
- *val += dw_read(cur, u32, end);
- break;
- case DW_EH_PE_sdata8:
- *val += dw_read(cur, s64, end);
- break;
- case DW_EH_PE_udata8:
- *val += dw_read(cur, u64, end);
- break;
- default:
- return -EINVAL;
- }
-
- out:
- *p = cur;
- return 0;
-}
-
-#define dw_read_encoded_value(ptr, end, enc) ({ \
- u64 __v; \
- if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
- return -EINVAL; \
- } \
- __v; \
- })
-
-static u64 elf_section_offset(int fd, const char *name)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- u64 offset = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return 0;
-
- do {
- if (gelf_getehdr(elf, &ehdr) == NULL)
- break;
-
- if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
- break;
-
- offset = shdr.sh_offset;
- } while (0);
-
- elf_end(elf);
- return offset;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int elf_is_exec(int fd, const char *name)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- int retval = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return 0;
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out;
-
- retval = (ehdr.e_type == ET_EXEC);
-
-out:
- elf_end(elf);
- pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
- return retval;
-}
-#endif
-
-struct table_entry {
- u32 start_ip_offset;
- u32 fde_offset;
-};
-
-struct eh_frame_hdr {
- unsigned char version;
- unsigned char eh_frame_ptr_enc;
- unsigned char fde_count_enc;
- unsigned char table_enc;
-
- /*
- * The rest of the header is variable-length and consists of the
- * following members:
- *
- * encoded_t eh_frame_ptr;
- * encoded_t fde_count;
- */
-
- /* A single encoded pointer should not be more than 8 bytes. */
- u64 enc[2];
-
- /*
- * struct {
- * encoded_t start_ip;
- * encoded_t fde_addr;
- * } binary_search_table[fde_count];
- */
- char data[0];
-} __packed;
-
-static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
- u64 offset, u64 *table_data, u64 *segbase,
- u64 *fde_count)
-{
- struct eh_frame_hdr hdr;
- u8 *enc = (u8 *) &hdr.enc;
- u8 *end = (u8 *) &hdr.data;
- ssize_t r;
-
- r = dso__data_read_offset(dso, machine, offset,
- (u8 *) &hdr, sizeof(hdr));
- if (r != sizeof(hdr))
- return -EINVAL;
-
- /* We dont need eh_frame_ptr, just skip it. */
- dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
-
- *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
- *segbase = offset;
- *table_data = (enc - (u8 *) &hdr) + offset;
- return 0;
-}
-
-static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
- u64 *table_data, u64 *segbase,
- u64 *fde_count)
-{
- int ret = -EINVAL, fd;
- u64 offset = dso->data.eh_frame_hdr_offset;
-
- if (offset == 0) {
- fd = dso__data_get_fd(dso, machine);
- if (fd < 0)
- return -EINVAL;
-
- /* Check the .eh_frame section for unwinding info */
- offset = elf_section_offset(fd, ".eh_frame_hdr");
- dso->data.eh_frame_hdr_offset = offset;
- dso__data_put_fd(dso);
- }
-
- if (offset)
- ret = unwind_spec_ehframe(dso, machine, offset,
- table_data, segbase,
- fde_count);
-
- return ret;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int read_unwind_spec_debug_frame(struct dso *dso,
- struct machine *machine, u64 *offset)
-{
- int fd;
- u64 ofs = dso->data.debug_frame_offset;
-
- if (ofs == 0) {
- fd = dso__data_get_fd(dso, machine);
- if (fd < 0)
- return -EINVAL;
-
- /* Check the .debug_frame section for unwinding info */
- ofs = elf_section_offset(fd, ".debug_frame");
- dso->data.debug_frame_offset = ofs;
- dso__data_put_fd(dso);
- }
-
- *offset = ofs;
- if (*offset)
- return 0;
-
- return -EINVAL;
-}
-#endif
-
-static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
-{
- struct addr_location al;
-
- thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
- MAP__FUNCTION, ip, &al);
- if (!al.map) {
- /*
- * We've seen cases (softice) where DWARF unwinder went
- * through non executable mmaps, which we need to lookup
- * in MAP__VARIABLE tree.
- */
- thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
- MAP__VARIABLE, ip, &al);
- }
- return al.map;
-}
-
-static int
-find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
- int need_unwind_info, void *arg)
-{
- struct unwind_info *ui = arg;
- struct map *map;
- unw_dyn_info_t di;
- u64 table_data, segbase, fde_count;
- int ret = -EINVAL;
-
- map = find_map(ip, ui);
- if (!map || !map->dso)
- return -EINVAL;
-
- pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
-
- /* Check the .eh_frame section for unwinding info */
- if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
- &table_data, &segbase, &fde_count)) {
- memset(&di, 0, sizeof(di));
- di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
- di.start_ip = map->start;
- di.end_ip = map->end;
- di.u.rti.segbase = map->start + segbase;
- di.u.rti.table_data = map->start + table_data;
- di.u.rti.table_len = fde_count * sizeof(struct table_entry)
- / sizeof(unw_word_t);
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
- /* Check the .debug_frame section for unwinding info */
- if (ret < 0 &&
- !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
- int fd = dso__data_get_fd(map->dso, ui->machine);
- int is_exec = elf_is_exec(fd, map->dso->name);
- unw_word_t base = is_exec ? 0 : map->start;
- const char *symfile;
-
- if (fd >= 0)
- dso__data_put_fd(map->dso);
-
- symfile = map->dso->symsrc_filename ?: map->dso->name;
-
- memset(&di, 0, sizeof(di));
- if (dwarf_find_debug_frame(0, &di, ip, base, symfile,
- map->start, map->end))
- return dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-#endif
-
- return ret;
-}
-
-static int access_fpreg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t __maybe_unused num,
- unw_fpreg_t __maybe_unused *val,
- int __maybe_unused __write,
- void __maybe_unused *arg)
-{
- pr_err("unwind: access_fpreg unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused *dil_addr,
- void __maybe_unused *arg)
-{
- return -UNW_ENOINFO;
-}
-
-static int resume(unw_addr_space_t __maybe_unused as,
- unw_cursor_t __maybe_unused *cu,
- void __maybe_unused *arg)
-{
- pr_err("unwind: resume unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int
-get_proc_name(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused addr,
- char __maybe_unused *bufp, size_t __maybe_unused buf_len,
- unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
-{
- pr_err("unwind: get_proc_name unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
- unw_word_t *data)
-{
- struct map *map;
- ssize_t size;
-
- map = find_map(addr, ui);
- if (!map) {
- pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
- return -1;
- }
-
- if (!map->dso)
- return -1;
-
- size = dso__data_read_addr(map->dso, map, ui->machine,
- addr, (u8 *) data, sizeof(*data));
-
- return !(size == sizeof(*data));
-}
-
-static int access_mem(unw_addr_space_t __maybe_unused as,
- unw_word_t addr, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- struct stack_dump *stack = &ui->sample->user_stack;
- u64 start, end;
- int offset;
- int ret;
-
- /* Don't support write, probably not needed. */
- if (__write || !stack || !ui->sample->user_regs.regs) {
- *valp = 0;
- return 0;
- }
-
- ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
- if (ret)
- return ret;
-
- end = start + stack->size;
-
- /* Check overflow. */
- if (addr + sizeof(unw_word_t) < addr)
- return -EINVAL;
-
- if (addr < start || addr + sizeof(unw_word_t) >= end) {
- ret = access_dso_mem(ui, addr, valp);
- if (ret) {
- pr_debug("unwind: access_mem %p not inside range"
- " 0x%" PRIx64 "-0x%" PRIx64 "\n",
- (void *) (uintptr_t) addr, start, end);
- *valp = 0;
- return ret;
- }
- return 0;
- }
-
- offset = addr - start;
- *valp = *(unw_word_t *)&stack->data[offset];
- pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
- (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
- return 0;
-}
-
-static int access_reg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t regnum, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- int id, ret;
- u64 val;
-
- /* Don't support write, I suspect we don't need it. */
- if (__write) {
- pr_err("unwind: access_reg w %d\n", regnum);
- return 0;
- }
-
- if (!ui->sample->user_regs.regs) {
- *valp = 0;
- return 0;
- }
-
- id = libunwind__arch_reg_id(regnum);
- if (id < 0)
- return -EINVAL;
-
- ret = perf_reg_value(&val, &ui->sample->user_regs, id);
- if (ret) {
- pr_err("unwind: can't read reg %d\n", regnum);
- return ret;
- }
-
- *valp = (unw_word_t) val;
- pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
- return 0;
-}
-
-static void put_unwind_info(unw_addr_space_t __maybe_unused as,
- unw_proc_info_t *pi __maybe_unused,
- void *arg __maybe_unused)
-{
- pr_debug("unwind: put_unwind_info called\n");
-}
-
-static int entry(u64 ip, struct thread *thread,
- unwind_entry_cb_t cb, void *arg)
-{
- struct unwind_entry e;
- struct addr_location al;
-
- thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
- MAP__FUNCTION, ip, &al);
-
- e.ip = ip;
- e.map = al.map;
- e.sym = al.sym;
-
- pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
- ip,
- al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
-
- return cb(&e, arg);
-}
-
-static void display_error(int err)
-{
- switch (err) {
- case UNW_EINVAL:
- pr_err("unwind: Only supports local.\n");
- break;
- case UNW_EUNSPEC:
- pr_err("unwind: Unspecified error.\n");
- break;
- case UNW_EBADREG:
- pr_err("unwind: Register unavailable.\n");
- break;
- default:
- break;
- }
-}
-
-static unw_accessors_t accessors = {
- .find_proc_info = find_proc_info,
- .put_unwind_info = put_unwind_info,
- .get_dyn_info_list_addr = get_dyn_info_list_addr,
- .access_mem = access_mem,
- .access_reg = access_reg,
- .access_fpreg = access_fpreg,
- .resume = resume,
- .get_proc_name = get_proc_name,
-};
-
-static int _unwind__prepare_access(struct thread *thread)
-{
- if (callchain_param.record_mode != CALLCHAIN_DWARF)
- return 0;
-
- thread->addr_space = unw_create_addr_space(&accessors, 0);
- if (!thread->addr_space) {
- pr_err("unwind: Can't create unwind address space.\n");
- return -ENOMEM;
- }
-
- unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL);
- return 0;
-}
-
-static void _unwind__flush_access(struct thread *thread)
-{
- if (callchain_param.record_mode != CALLCHAIN_DWARF)
- return;
-
- unw_flush_cache(thread->addr_space, 0, 0);
-}
-
-static void _unwind__finish_access(struct thread *thread)
-{
- if (callchain_param.record_mode != CALLCHAIN_DWARF)
- return;
-
- unw_destroy_addr_space(thread->addr_space);
-}
-
-static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
- void *arg, int max_stack)
-{
- u64 val;
- unw_word_t ips[max_stack];
- unw_addr_space_t addr_space;
- unw_cursor_t c;
- int ret, i = 0;
-
- ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP);
- if (ret)
- return ret;
-
- ips[i++] = (unw_word_t) val;
-
- /*
- * If we need more than one entry, do the DWARF
- * unwind itself.
- */
- if (max_stack - 1 > 0) {
- WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = ui->thread->addr_space;
-
- if (addr_space == NULL)
- return -1;
-
- ret = unw_init_remote(&c, addr_space, ui);
- if (ret)
- display_error(ret);
-
- while (!ret && (unw_step(&c) > 0) && i < max_stack) {
- unw_get_reg(&c, UNW_REG_IP, &ips[i]);
- ++i;
- }
-
- max_stack = i;
- }
-
- /*
- * Display what we got based on the order setup.
- */
- for (i = 0; i < max_stack && !ret; i++) {
- int j = i;
-
- if (callchain_param.order == ORDER_CALLER)
- j = max_stack - i - 1;
- ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
- }
-
- return ret;
-}
-
-static int _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,
- .thread = thread,
- .machine = thread->mg->machine,
- };
-
- if (!data->user_regs.regs)
- return -EINVAL;
-
- if (max_stack <= 0)
- return -EINVAL;
-
- return get_entries(&ui, cb, arg, max_stack);
-}
-
-static struct unwind_libunwind_ops
-_unwind_libunwind_ops = {
- .prepare_access = _unwind__prepare_access,
- .flush_access = _unwind__flush_access,
- .finish_access = _unwind__finish_access,
- .get_entries = _unwind__get_entries,
-};
-
-void register_local_unwind_libunwind_ops(struct thread *thread)
-{
- thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
-}
+#include "thread.h"

int unwind__prepare_access(struct thread *thread)
{
--
1.8.5.2

He Kuang

unread,
May 28, 2016, 8:10:09 AM5/28/16
to
Currently, libunwind operations are fixed, and they are chosen
according to the host architecture. This will lead a problem that if a
thread is run as x86_32 on x86_64 machine, perf will use libunwind
methods for x86_64 to parse the callchain and get wrong result.

This patch changes the fixed methods of libunwind operations to
thread/map related, and each thread can have indivadual libunwind
operations. Local libunwind methods are registered as default value.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.c | 2 ++
tools/perf/util/thread.h | 14 +++++++++-
tools/perf/util/unwind-libunwind.c | 55 ++++++++++++++++++++++++++++++++++----
tools/perf/util/unwind.h | 5 ++++
4 files changed, 70 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 45fcb71..6d3900c 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -43,6 +43,8 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);

+ register_local_unwind_libunwind_ops(thread);
+
if (unwind__prepare_access(thread) < 0)
goto err_thread;

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index aa3a8ff..647b011 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -12,6 +12,17 @@

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;
@@ -33,7 +44,8 @@ struct thread {
void *priv;
struct thread_stack *ts;
#ifdef HAVE_LIBUNWIND_SUPPORT
- void *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..0277b22 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -23,7 +23,6 @@
#include <sys/mman.h>
#include <linux/list.h>
#include <libunwind.h>
-#include <libunwind-ptrace.h>
#include "callchain.h"
#include "thread.h"
#include "session.h"
@@ -579,7 +578,7 @@ static unw_accessors_t accessors = {
.get_proc_name = get_proc_name,
};

-int unwind__prepare_access(struct thread *thread)
+static int _unwind__prepare_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return 0;
@@ -594,7 +593,7 @@ int unwind__prepare_access(struct thread *thread)
return 0;
}

-void unwind__flush_access(struct thread *thread)
+static void _unwind__flush_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return;
@@ -602,7 +601,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 _unwind__finish_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return;
@@ -662,7 +661,7 @@ 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,
+static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack)
{
@@ -680,3 +679,49 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,

return get_entries(&ui, cb, arg, max_stack);
}
+
+static struct unwind_libunwind_ops
+_unwind_libunwind_ops = {
+ .prepare_access = _unwind__prepare_access,
+ .flush_access = _unwind__flush_access,
+ .finish_access = _unwind__finish_access,
+ .get_entries = _unwind__get_entries,
+};
+
+void register_local_unwind_libunwind_ops(struct thread *thread)
+{
+ thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
+}
+
+int unwind__prepare_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ return thread->unwind_libunwind_ops->prepare_access(thread);
+ else
+ return 0;
+}
+
+void unwind__flush_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ thread->unwind_libunwind_ops->flush_access(thread);
+}
+
+void unwind__finish_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ thread->unwind_libunwind_ops->finish_access(thread);
+}
+
+int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct thread *thread,
+ struct perf_sample *data, int max_stack)
+{
+ if (thread->unwind_libunwind_ops)
+ return thread->unwind_libunwind_ops->get_entries(cb, arg,
+ thread,
+ data,
+ max_stack);
+ else
+ return 0;
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 12790cf..5f36415 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -24,6 +24,7 @@ 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 register_local_unwind_libunwind_ops(struct thread *thread);
#else
static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
{
@@ -32,6 +33,8 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void
+register_local_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}
#endif
#else
static inline int
@@ -51,5 +54,7 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void
+register_local_unwind_libunwind_ops(struct thread *thread __maybe_unused) {}

He Kuang

unread,
May 28, 2016, 8:10:13 AM5/28/16
to
Currently, the type of thread->addr_space is unw_addr_space_t, which
is a pointer defined in libunwind headers. For local libunwind, we can
simple include "libunwind.h", but for remote libunwind, the header
file is depends on the target libunwind platform. This patch uses
'void *' instead to decouple the dependence on libunwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 45fba13..aa3a8ff 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -9,9 +9,6 @@
#include "symbol.h"
#include <strlist.h>
#include <intlist.h>
-#ifdef HAVE_LIBUNWIND_SUPPORT
-#include <libunwind.h>
-#endif

struct thread_stack;

@@ -36,7 +33,7 @@ struct thread {
void *priv;
struct thread_stack *ts;
#ifdef HAVE_LIBUNWIND_SUPPORT
- unw_addr_space_t addr_space;
+ void *addr_space;
#endif
};

--
1.8.5.2

Wangnan (F)

unread,
May 28, 2016, 8:20:05 AM5/28/16
to


On 2016/5/28 19:59, He Kuang wrote:
> This patch extracts codes related to specific arithecture out of
> unwind-libunwind.c. The extrated part are only built if local
> libunwind is supported.
>
> Signed-off-by: He Kuang <hek...@huawei.com>
> ---
> tools/perf/util/Build | 1 +
> tools/perf/util/unwind-libunwind-local.c | 677 ++++++++++++++++++++++++++++++
> tools/perf/util/unwind-libunwind.c | 694 +------------------------------
> 3 files changed, 679 insertions(+), 693 deletions(-)
> create mode 100644 tools/perf/util/unwind-libunwind-local.c
>
Please use 'git format-patch -M' to avoid such a large patch.

Thank you.

He Kuang

unread,
May 29, 2016, 11:30:05 PM5/29/16
to
Since unwind-libunwind.c contains code for specific arithecture, we
change it's name to unwind-libunwind-local.c, and let it only be built
if local libunwind is supported.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/Build | 2 +-
tools/perf/util/{unwind-libunwind.c => unwind-libunwind-local.c} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename tools/perf/util/{unwind-libunwind.c => unwind-libunwind-local.c} (100%)

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8c6c8a0..5e23d85 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-local.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind-local.c
similarity index 100%
rename from tools/perf/util/unwind-libunwind.c
rename to tools/perf/util/unwind-libunwind-local.c
--
1.8.5.2

He Kuang

unread,
May 29, 2016, 11:30:06 PM5/29/16
to
This patch extracts common unwind-libunwind APIs out of
unwind-libunwind-local.c, this part will be used by both local and
remote libunwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/unwind-libunwind-local.c | 32 ------------------------------
tools/perf/util/unwind-libunwind.c | 34 ++++++++++++++++++++++++++++++++
3 files changed, 35 insertions(+), 32 deletions(-)
create mode 100644 tools/perf/util/unwind-libunwind.c

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 5e23d85..004fb1d 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -100,6 +100,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o

libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
+libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 93d2d8e..37e53f8 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -692,35 +692,3 @@ void register_local_unwind_libunwind_ops(struct thread *thread)
{
thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
}
-
-int unwind__prepare_access(struct thread *thread)
-{
- register_local_unwind_libunwind_ops(thread);
-
- return thread->unwind_libunwind_ops->prepare_access(thread);
-}
-
-void unwind__flush_access(struct thread *thread)
-{
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->flush_access(thread);
-}
-
-void unwind__finish_access(struct thread *thread)
-{
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->finish_access(thread);
-}
-
-int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack)
-{
- if (thread->unwind_libunwind_ops)
- return thread->unwind_libunwind_ops->get_entries(cb, arg,
- thread,
- data,
- max_stack);
- else
- return 0;
-}
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
new file mode 100644
index 0000000..40d0453
--- /dev/null
+++ b/tools/perf/util/unwind-libunwind.c
@@ -0,0 +1,34 @@
+#include "unwind.h"
+#include "thread.h"
+
+int unwind__prepare_access(struct thread *thread)
+{
+ register_local_unwind_libunwind_ops(thread);
+
+ return thread->unwind_libunwind_ops->prepare_access(thread);
+}
--
1.8.5.2

Hekuang

unread,
May 29, 2016, 11:30:08 PM5/29/16
to
Currently is not a 100% similar rename, I split this patch into a
rename and a small diff.
>
> Thank you.
>
>

Jiri Olsa

unread,
May 30, 2016, 5:00:06 AM5/30/16
to
this should rather go to util/unwind.h

thanks,
jirka

Jiri Olsa

unread,
May 30, 2016, 5:00:07 AM5/30/16
to
On Sat, May 28, 2016 at 11:59:59AM +0000, He Kuang wrote:

SNIP

> diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
> index b391e3e..849fec1 100644
> --- a/tools/perf/util/unwind-libunwind-local.c
> +++ b/tools/perf/util/unwind-libunwind-local.c
> @@ -5,7 +5,9 @@
> #include <unistd.h>
> #include <sys/mman.h>
> #include <linux/list.h>
> +#ifndef REMOTE_UNWIND_LIBUNWIND
> #include <libunwind.h>
> +#endif
> #include "callchain.h"
> #include "thread.h"
> #include "session.h"
> @@ -671,7 +673,9 @@ _unwind_libunwind_ops = {
> .get_entries = _unwind__get_entries,
> };
>
> +#ifndef REMOTE_UNWIND_LIBUNWIND
> void register_local_unwind_libunwind_ops(struct thread *thread)
> {
> thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
> }
> +#endif

above hunks should go to separate patch

however I still think it'd be more clear if we separate the code like:

code template - util/unwind-libunwind.c
arch template - arch/x86/util/unwind-libunwind.c

wrapper for local - util/unwind-libunwind-local.c
wrapper for x86_32 - arch/x86/util/unwind-libunwind-x86_32.c
wrapper for arm64 - arch/x86/util/unwind-libunwind-arm64.c

jirka

Jiri Olsa

unread,
May 30, 2016, 5:00:07 AM5/30/16
to
On Sat, May 28, 2016 at 11:59:59AM +0000, He Kuang wrote:
> Support x86(32-bit) cross platform callchain unwind.
>
> Signed-off-by: He Kuang <hek...@huawei.com>
> ---
> tools/perf/arch/Build | 1 +
> tools/perf/arch/x86/util/unwind-libunwind.c | 7 ++++---
> tools/perf/arch/x86/util/unwind-libunwind_x86_32.c | 21 +++++++++++++++++++++
> tools/perf/util/unwind-libunwind-local.c | 4 ++++
> tools/perf/util/unwind-libunwind.c | 19 +++++++++++++------
> tools/perf/util/unwind.h | 10 ++++++++++
> 6 files changed, 53 insertions(+), 9 deletions(-)
> create mode 100644 tools/perf/arch/x86/util/unwind-libunwind_x86_32.c
>
> diff --git a/tools/perf/arch/Build b/tools/perf/arch/Build
> index 109eb75..3fc4af1 100644
> --- a/tools/perf/arch/Build
> +++ b/tools/perf/arch/Build
> @@ -1,2 +1,3 @@
> libperf-y += common.o
> libperf-y += $(ARCH)/
> +libperf-$(CONFIG_LIBUNWIND_X86) += x86/util/unwind-libunwind_x86_32.o

we have Build file directly in arch/x86/util/

if you do it like this to include generic file easily
we better fix the include then

jirka

Jiri Olsa

unread,
May 30, 2016, 5:00:07 AM5/30/16
to
this hunk together with the ifdef in
unwind__prepare_access should go to patch:

perf callchain: Support x86 target platform

thanks
jirka

SNIP

> + arch = normalize_arch(thread->mg->machine->env->arch);
> +
> + if (!strcmp(arch, "x86")) {
> + 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

SNIP

Jiri Olsa

unread,
May 30, 2016, 5:00:08 AM5/30/16
to
On Sat, May 28, 2016 at 11:59:59AM +0000, He Kuang wrote:

SNIP

> --- /dev/null
> +++ b/tools/perf/arch/x86/util/unwind-libunwind_x86_32.c
> @@ -0,0 +1,21 @@
> +#define REMOTE_UNWIND_LIBUNWIND
> +
> +#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
> +
> +#include "unwind.h"
> +#include "debug.h"
> +#include "libunwind-x86.h"
> +#include <../../../../../arch/x86/include/uapi/asm/perf_regs.h>
> +
> +#undef HAVE_ARCH_X86_64_SUPPORT
> +#include "unwind-libunwind.c"
> +
> +#undef NO_LIBUNWIND_DEBUG_FRAME
> +#define NO_LIBUNWIND_DEBUG_FRAME
> +#include "util/unwind-libunwind-local.c"
> +
> +int register_x86_32_unwind_libunwind_ops(struct thread *thread)
> +{
> + thread->unwind_libunwind_ops = &_unwind_libunwind_ops;
> + return 0;
> +}

hum, how about export the arch ops:

struct unwind_libunwind_ops *x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;

and have single unwind__register_ops(ops) function

jirka

Jiri Olsa

unread,
May 30, 2016, 5:00:08 AM5/30/16
to
so thread__insert_map does not return value,
I think we should change it now when it calls
unwind__prepare_access

I was also thinking to keepcall unwind__prepare_access
separatelly but this function seems to fit better

thanks,
jirka

Hekuang

unread,
May 30, 2016, 5:20:07 AM5/30/16
to
hi
This is because "libperf-y += $(ARCH)" will only sink into $(ARCH) folder,
for example on x86_64, only tools/perf/arch/x86 will be built. But for
remote libunwind, we also need
'tools/perf/arch/arm64/util/unwind-libunwind.o', while arm64 folder is
not added to libperf-y. Is there a gracefull to deal with this?

> jirka
>

Jiri Olsa

unread,
May 30, 2016, 5:40:11 AM5/30/16
to
you just need to include the file, right?

I think it's ok to include arch/arm/....c
from arch/x86/util/unwind-libunwind-arm64.c

jirka

Hekuang

unread,
May 30, 2016, 7:10:05 AM5/30/16
to
By following your advise, if ARCH=x86, the file tree will
be like this:

arch/x86
- arch/x86/util/unwind-libunwind-arm64.c
- arch/x86/util/unwind-libunwind-x86_32.c
- arch/x86/util/unwind-libunwind-x86_64.c
- arch/x86/util/unwind-libunwind-arm.c

And for ARCH=arm (host machine is arm, it should be considered)
arch/arm
- arch/arm/util/unwind-libunwind-arm64.c
- arch/arm/util/unwind-libunwind-x86_32.c
- arch/arm/util/unwind-libunwind-x86_64.c
- arch/arm/util/unwind-libunwind-arm.c

For arm64:
arch/arm64
- arch/arm64/util/unwind-libunwind-arm64.c
- arch/arm64/util/unwind-libunwind-x86_32.c
- arch/arm64/util/unwind-libunwind-x86_64.c
- arch/arm64/util/unwind-libunwind-arm.c

But in my patch, the file tree is like this:

arch
- arch/arm64/util/unwind-libunwind-arm64.c
- arch/x86/util/unwind-libunwind-x86_64.c
- arch/x86/util/unwind-libunwind-x86_32.c
- arch/arm/util/unwind-libunwind-arm.c

I admit that

+libperf-$(CONFIG_LIBUNWIND_X86) += x86/util/unwind-libunwind_x86_32.o

is not so good, but do you think the above file tree is
too redunctant?

Thank you.
>

Jiri Olsa

unread,
May 30, 2016, 10:30:07 AM5/30/16
to
On Mon, May 30, 2016 at 06:58:40PM +0800, Hekuang wrote:

SNIP
i see.. we could leave it like that, I just wish
it'd be more clear.. one last thought:

how about moving libunwind arch files into special folder:

util/libunwind/arm64.c
util/libunwind/x86_32.c
util/libunwind/x86_64.c
util/libunwind/arm.c

thanks,
jirka

He Kuang

unread,
May 31, 2016, 7:20:06 AM5/31/16
to
For determine the libunwind methods to use, we should get the
32bit/64bit information from maps of a thread. When a thread is newly
created, the information is not prepared. This patch moves
unwind__prepare_access() into thread__insert_map() so we can get the
information we need from maps. Meanwhile, let thread__insert_map()
return value and show messages on error.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/machine.c | 10 ++++++++--
tools/perf/util/thread.c | 13 +++----------
tools/perf/util/thread.h | 2 +-
tools/perf/util/unwind-libunwind.c | 7 +++----
4 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index bdc33ce..0bd6aac 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1353,7 +1353,10 @@ int machine__process_mmap2_event(struct machine *machine,
if (map == NULL)
goto out_problem_map;

- thread__insert_map(thread, map);
+ ret = thread__insert_map(thread, map);
+ if (ret)
+ pr_err("Thread insert map error\n");
+
thread__put(thread);
map__put(map);
return 0;
@@ -1403,7 +1406,10 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
if (map == NULL)
goto out_problem_map;

- thread__insert_map(thread, map);
+ ret = thread__insert_map(thread, map);
+ if (ret)
+ pr_err("Thread insert map error\n");
+
thread__put(thread);
map__put(map);
return 0;
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 95ff1b8..0615ef3 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -43,15 +43,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);

-#ifdef HAVE_LIBUNWIND_SUPPORT
- unwind__register_ops(thread, local_unwind_libunwind_ops);
-#else
- unwind__register_ops(thread, NULL);
-#endif
-
- if (unwind__prepare_access(thread) < 0)
- goto err_thread;
-
comm_str = malloc(32);
if (!comm_str)
goto err_thread;
@@ -207,10 +198,12 @@ size_t thread__fprintf(struct thread *thread, FILE *fp)
map_groups__fprintf(thread->mg, fp);
}

-void thread__insert_map(struct thread *thread, struct map *map)
+int thread__insert_map(struct thread *thread, struct map *map)
{
map_groups__fixup_overlappings(thread->mg, map, stderr);
map_groups__insert(thread->mg, map);
+
+ return unwind__prepare_access(thread);
}

static int thread__clone_map_groups(struct thread *thread,
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 152fb9a..e7a82a3 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -76,7 +76,7 @@ int thread__comm_len(struct thread *thread);
struct comm *thread__comm(const struct thread *thread);
struct comm *thread__exec_comm(const struct thread *thread);
const char *thread__comm_str(const struct thread *thread);
-void thread__insert_map(struct thread *thread, struct map *map);
+int thread__insert_map(struct thread *thread, struct map *map);
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);
size_t thread__fprintf(struct thread *thread, FILE *fp);

diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 77b8521..e35c5bfda 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -700,10 +700,9 @@ void unwind__register_ops(struct thread *thread,

int unwind__prepare_access(struct thread *thread)
{
- if (thread->unwind_libunwind_ops)
- return thread->unwind_libunwind_ops->prepare_access(thread);
- else
- return 0;
+ unwind__register_ops(thread, local_unwind_libunwind_ops);
+
+ return thread->unwind_libunwind_ops->prepare_access(thread);
}

He Kuang

unread,
May 31, 2016, 7:30:07 AM5/31/16
to
v6 url:
http://thread.gmane.org/gmane.linux.kernel/2229396

Currently, perf script uses host unwind methods(local unwind) to parse
perf.data callchain info regardless of the target architecture. So we
v7:
By following the advises from Jiri Olsa:

- Move unwind ops struct to unwind.h
- Add return value to function thread__insert_map()
- Split "Separate local/remote libunwind config" into two patches,
make commit message consistent with patch content.
- Move not supported warnings to "Support x86 target platform"
- Export $(ARCH)_unwind_libunwind_ops variables instead of
functions.
- Move arch/$(ARCH)/util/unwind-libunwind-$(ARCH).c into
util/libunwind/ folder.

Thanks.

He Kuang (14):
perf tools: Use LIBUNWIND_DIR for remote libunwind feature check
perf tools: Decouple thread->address_space on libunwind
perf tools: Introducing struct unwind_libunwind_ops for local unwind
perf tools: Move unwind__prepare_access from thread_new into
thread__insert_map
perf tools: Don't mix LIBUNWIND_LIBS into LIBUNWIND_LDFLAGS
perf tools: Separate local/remote libunwind config
perf tools: Rename unwind-libunwind.c to unwind-libunwind-local.c
perf tools: Extract common API out of unwind-libunwind-local.c
perf tools: Export normalize_arch() function
perf tools: Check the target platform before assigning unwind methods
perf tools: Change fixed name of libunwind__arch_reg_id to macro
perf tools: Introduce flag to separate local/remote unwind compilation
perf callchain: Support x86 target platform
perf callchain: Support aarch64 cross-platform

tools/perf/arch/arm/util/Build | 2 +-
tools/perf/arch/arm64/util/Build | 2 +-
tools/perf/arch/arm64/util/unwind-libunwind.c | 4 +-
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
tools/perf/arch/x86/util/Build | 2 +-
tools/perf/arch/x86/util/unwind-libunwind.c | 6 +-
tools/perf/config/Makefile | 53 +-
tools/perf/util/Build | 3 +
tools/perf/util/libunwind/arm64.c | 18 +
tools/perf/util/libunwind/x86_32.c | 18 +
tools/perf/util/machine.c | 10 +-
tools/perf/util/thread.c | 7 +-
tools/perf/util/thread.h | 9 +-
tools/perf/util/unwind-libunwind-local.c | 697 ++++++++++++++++++++++++++
tools/perf/util/unwind-libunwind.c | 694 ++-----------------------
tools/perf/util/unwind.h | 27 +-
17 files changed, 880 insertions(+), 675 deletions(-)
create mode 100644 tools/perf/util/libunwind/arm64.c
create mode 100644 tools/perf/util/libunwind/x86_32.c

He Kuang

unread,
May 31, 2016, 7:30:07 AM5/31/16
to
Pass LIBUNWIND_DIR to feature check flags for remote libunwind
tests. So perf can be able to detect remote libunwind libraries from
arbitrary directory.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/config/Makefile | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 1e46277..6f9f566 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile

He Kuang

unread,
May 31, 2016, 7:30:07 AM5/31/16
to
Currently, the type of thread->addr_space is unw_addr_space_t, which
is a pointer defined in libunwind headers. For local libunwind, we can
simple include "libunwind.h", but for remote libunwind, the header
file is depends on the target libunwind platform. This patch uses
'void *' instead to decouple the dependence on libunwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 45fba13..aa3a8ff 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h

He Kuang

unread,
May 31, 2016, 7:30:07 AM5/31/16
to
This patch extracts common unwind-libunwind APIs out of
unwind-libunwind-local.c, this part will be used by both local and
remote libunwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/unwind-libunwind-local.c | 38 -----------------------------
tools/perf/util/unwind-libunwind.c | 42 ++++++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 2 --
4 files changed, 43 insertions(+), 40 deletions(-)
create mode 100644 tools/perf/util/unwind-libunwind.c

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 5e23d85..004fb1d 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -100,6 +100,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o

libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
+libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index e35c5bfda..9c70486 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -691,41 +691,3 @@ _unwind_libunwind_ops = {

struct unwind_libunwind_ops *
local_unwind_libunwind_ops = &_unwind_libunwind_ops;
-
-void unwind__register_ops(struct thread *thread,
- struct unwind_libunwind_ops *ops)
-{
- thread->unwind_libunwind_ops = ops;
-}
-
-int unwind__prepare_access(struct thread *thread)
-{
- unwind__register_ops(thread, local_unwind_libunwind_ops);
-
- return thread->unwind_libunwind_ops->prepare_access(thread);
-}
-
-void unwind__flush_access(struct thread *thread)
-{
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->flush_access(thread);
-}
-
-void unwind__finish_access(struct thread *thread)
-{
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->finish_access(thread);
-}
-
-int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack)
-{
- if (thread->unwind_libunwind_ops)
- return thread->unwind_libunwind_ops->get_entries(cb, arg,
- thread,
- data,
- max_stack);
- else
- return 0;
-}
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
new file mode 100644
index 0000000..787adbb
--- /dev/null
+++ b/tools/perf/util/unwind-libunwind.c
@@ -0,0 +1,42 @@
+#include "unwind.h"
+#include "thread.h"
+
+struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
+
+void unwind__register_ops(struct thread *thread,
+ struct unwind_libunwind_ops *ops)
+{
+ thread->unwind_libunwind_ops = ops;
+}
+
+int unwind__prepare_access(struct thread *thread)
+{
+ unwind__register_ops(thread, local_unwind_libunwind_ops);
+
+ return thread->unwind_libunwind_ops->prepare_access(thread);
+}
+
+void unwind__flush_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ thread->unwind_libunwind_ops->flush_access(thread);
+}
+
+void unwind__finish_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ thread->unwind_libunwind_ops->finish_access(thread);
+}
+
+int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct thread *thread,
+ struct perf_sample *data, int max_stack)
+{
+ if (thread->unwind_libunwind_ops)
+ return thread->unwind_libunwind_ops->get_entries(cb, arg,
+ thread,
+ data,
+ max_stack);
+ else
+ return 0;
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index bd7377b..25001be 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -23,8 +23,6 @@ struct unwind_libunwind_ops {
struct perf_sample *data, int max_stack);
};

-struct unwind_libunwind_ops *local_unwind_libunwind_ops;
-
#ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:08 AM5/31/16
to
Export normalize_arch() function, so other part of perf can get
normalized form of arch string.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/common.c | 2 +-
tools/perf/arch/common.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index e83c8ce..fa090a9 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -102,7 +102,7 @@ static int lookup_triplets(const char *const *triplets, const char *name)
* Return architecture name in a normalized form.
* The conversion logic comes from the Makefile.
*/
-static const char *normalize_arch(char *arch)
+const char *normalize_arch(char *arch)
{
if (!strcmp(arch, "x86_64"))
return "x86";
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h
index 7529cfb..6b01c73 100644
--- a/tools/perf/arch/common.h
+++ b/tools/perf/arch/common.h
@@ -6,5 +6,6 @@
extern const char *objdump_path;

int perf_env__lookup_objdump(struct perf_env *env);
+const char *normalize_arch(char *arch);

#endif /* ARCH_PERF_COMMON_H */
--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:08 AM5/31/16
to
Support x86(32-bit) cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/x86/util/unwind-libunwind.c | 6 ++++--
tools/perf/config/Makefile | 8 ++++++++
tools/perf/util/Build | 1 +
tools/perf/util/libunwind/x86_32.c | 18 ++++++++++++++++++
tools/perf/util/unwind-libunwind.c | 9 ++++++++-
5 files changed, 39 insertions(+), 3 deletions(-)
create mode 100644 tools/perf/util/libunwind/x86_32.c

diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
index db25e93..4f16661 100644
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ b/tools/perf/arch/x86/util/unwind-libunwind.c
@@ -1,12 +1,14 @@

+#ifndef REMOTE_UNWIND_LIBUNWIND
#include <errno.h>
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
+#endif

#ifdef HAVE_ARCH_X86_64_SUPPORT
-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

@@ -70,7 +72,7 @@ int libunwind__arch_reg_id(int regnum)
return id;
}
#else
-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
int id;

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 2a8915d..b8d8a77 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -354,6 +354,14 @@ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 004fb1d..7746e09 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -101,6 +101,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
new file mode 100644
index 0000000..46b4111
--- /dev/null
+++ b/tools/perf/util/libunwind/x86_32.c
@@ -0,0 +1,18 @@
+#define REMOTE_UNWIND_LIBUNWIND
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
+
+#include "unwind.h"
+#include "debug.h"
+#include "libunwind-x86.h"
+#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+#undef HAVE_ARCH_X86_64_SUPPORT
+#include "../../arch/x86/util/unwind-libunwind.c"
+
+#undef NO_LIBUNWIND_DEBUG_FRAME
+#define NO_LIBUNWIND_DEBUG_FRAME
+#include "util/unwind-libunwind-local.c"
+
+struct unwind_libunwind_ops *
+x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index e183390..5774317 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -5,6 +5,7 @@
#include "arch/common.h"

struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
+struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;

void unwind__register_ops(struct thread *thread,
struct unwind_libunwind_ops *ops)
@@ -30,7 +31,13 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
dso_type == DSO__TYPE_64BIT, map->dso->name);

arch = normalize_arch(thread->mg->machine->env->arch);
- pr_debug("unwind: target platform=%s\n", arch);
+
+ if (!strcmp(arch, "x86"))
+ if (dso_type != DSO__TYPE_64BIT)
+ ops = x86_32_unwind_libunwind_ops;
+
+ if (!ops)
+ pr_err("unwind: target platform=%s is not supported\n", arch);

unwind__register_ops(thread, ops);

--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:08 AM5/31/16
to
Since unwind-libunwind.c contains code for specific arithecture, we
change it's name to unwind-libunwind-local.c, and let it only be built
if local libunwind is supported.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/Build | 2 +-
tools/perf/util/{unwind-libunwind.c => unwind-libunwind-local.c} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename tools/perf/util/{unwind-libunwind.c => unwind-libunwind-local.c} (100%)

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 8c6c8a0..5e23d85 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-local.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

He Kuang

unread,
May 31, 2016, 7:30:08 AM5/31/16
to
For local libunwind, it uses the fixed methods to convert register id
according to the host platform, but in remote libunwind, this convert
function should be the one for remote architecture. This patch changes
the fixed name to macro and code for each remote platform can be
compiled indivadually.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/unwind-libunwind-local.c | 2 +-
tools/perf/util/unwind.h | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 9c70486..631b40d 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -508,7 +508,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;

diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index dda2156..c155f8b 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -29,7 +29,10 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct perf_sample *data, int max_stack);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
-int libunwind__arch_reg_id(int regnum);
+#ifndef LIBUNWIND__ARCH_REG_ID
+#define LIBUNWIND__ARCH_REG_ID libunwind__arch_reg_id
+#endif
+int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct thread *thread, struct map *map);
void unwind__flush_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:09 AM5/31/16
to
Support aarch64 cross platform callchain unwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/arm64/util/unwind-libunwind.c | 4 +++-
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/Build | 1 +
tools/perf/util/libunwind/arm64.c | 18 ++++++++++++++++++
tools/perf/util/unwind-libunwind.c | 7 ++++++-
5 files changed, 40 insertions(+), 2 deletions(-)
create mode 100644 tools/perf/util/libunwind/arm64.c

diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
index a87afa9..c116b71 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -1,11 +1,13 @@

+#ifndef REMOTE_UNWIND_LIBUNWIND
#include <errno.h>
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
#include "../../util/debug.h"
+#endif

-int libunwind__arch_reg_id(int regnum)
+int LIBUNWIND__ARCH_REG_ID(int regnum)
{
switch (regnum) {
case UNW_AARCH64_X0:
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index b8d8a77..00bd141 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -362,6 +362,18 @@ ifndef NO_LIBUNWIND
have_libunwind = 1
endif

+ ifeq ($(feature-libunwind-aarch64), 1)
+ $(call detected,CONFIG_LIBUNWIND_AARCH64)
+ CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
+ LDFLAGS += -lunwind-aarch64
+ have_libunwind = 1
+ $(call feature_check,libunwind-debug-frame-aarch64)
+ ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
+ msg := $(warning No debug_frame support found in libunwind-aarch64);
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
+ endif
+ 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
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 7746e09..fced833 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -102,6 +102,7 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o

libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o

diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
new file mode 100644
index 0000000..99c0d42
--- /dev/null
+++ b/tools/perf/util/libunwind/arm64.c
@@ -0,0 +1,18 @@
+#define REMOTE_UNWIND_LIBUNWIND
+
+#define LIBUNWIND__ARCH_REG_ID libunwind__arm64_reg_id
+
+#include "unwind.h"
+#include "debug.h"
+#include "libunwind-aarch64.h"
+#include <../../../../arch/arm64/include/uapi/asm/perf_regs.h>
+#include "../../arch/arm64/util/unwind-libunwind.c"
+
+#undef NO_LIBUNWIND_DEBUG_FRAME
+#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
+#define NO_LIBUNWIND_DEBUG_FRAME
+#endif
+#include "util/unwind-libunwind-local.c"
+
+struct unwind_libunwind_ops *
+arm64_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 5774317..b5f6ca2 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -6,6 +6,7 @@

struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
+struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;

void unwind__register_ops(struct thread *thread,
struct unwind_libunwind_ops *ops)
@@ -32,9 +33,13 @@ int unwind__prepare_access(struct thread *thread, struct map *map)

arch = normalize_arch(thread->mg->machine->env->arch);

- if (!strcmp(arch, "x86"))
+ if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT)
ops = x86_32_unwind_libunwind_ops;
+ } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
+ if (dso_type == DSO__TYPE_64BIT)
+ ops = arm64_unwind_libunwind_ops;
+ }

if (!ops)
pr_err("unwind: target platform=%s is not supported\n", arch);
--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:09 AM5/31/16
to
CONFIG_LIBUNWIND/NO_LIBUNWIND are changed to
CONFIG_LOCAL_LIBUNWIND/NO_LOCAL_LIBUNWIND for retaining local unwind
features. The new CONFIG_LIBUNWIND stands for either local or remote
or both unwind are supported, and NO_LIBUNWIND means that neither
local nor remote unwind is supported.

LIBUNWIND_LIBS is eliminated in LDFLAGS if local libunwind is not
supported.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/arch/arm/util/Build | 2 +-
tools/perf/arch/arm64/util/Build | 2 +-
tools/perf/arch/x86/util/Build | 2 +-
tools/perf/config/Makefile | 21 ++++++++++++++++++---
4 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index d22e3d0..f98da17 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -1,4 +1,4 @@
libperf-$(CONFIG_DWARF) += dwarf-regs.o

-libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index e58123a8..02f41db 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -1,2 +1,2 @@
libperf-$(CONFIG_DWARF) += dwarf-regs.o
-libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
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 118df2d..2a8915d 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -353,10 +353,21 @@ ifeq ($(ARCH),powerpc)
endif

ifndef NO_LIBUNWIND
+ have_libunwind :=
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
endif
+else
+ NO_LOCAL_LIBUNWIND := 1
endif

ifndef NO_LIBBPF
@@ -394,7 +405,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)
@@ -405,10 +416,14 @@ 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)
+ LDFLAGS += $(LIBUNWIND_LIBS)
+endif
+
+ifndef NO_LIBUNWIND
+ CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
CFLAGS += $(LIBUNWIND_CFLAGS)
- LDFLAGS += $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
+ LDFLAGS += $(LIBUNWIND_LDFLAGS)
endif

ifndef NO_LIBAUDIT
--
1.8.5.2

He Kuang

unread,
May 31, 2016, 7:30:15 AM5/31/16
to
Currently, libunwind operations are fixed, and they are chosen
according to the host architecture. This will lead a problem that if a
thread is run as x86_32 on x86_64 machine, perf will use libunwind
methods for x86_64 to parse the callchain and get wrong result.

This patch changes the fixed methods of libunwind operations to
thread/map related, and each thread can have indivadual libunwind
operations. Local libunwind methods are registered as default value.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.c | 6 ++++
tools/perf/util/thread.h | 4 ++-
tools/perf/util/unwind-libunwind.c | 58 +++++++++++++++++++++++++++++++++++---
tools/perf/util/unwind.h | 19 +++++++++++++
4 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 45fcb71..95ff1b8 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -43,6 +43,12 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);

+#ifdef HAVE_LIBUNWIND_SUPPORT
+ unwind__register_ops(thread, local_unwind_libunwind_ops);
+#else
+ unwind__register_ops(thread, NULL);
+#endif
+
if (unwind__prepare_access(thread) < 0)
goto err_thread;

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index aa3a8ff..152fb9a 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -11,6 +11,7 @@
#include <intlist.h>

struct thread_stack;
+struct unwind_libunwind_ops;

struct thread {
union {
@@ -33,7 +34,8 @@ struct thread {
void *priv;
struct thread_stack *ts;
#ifdef HAVE_LIBUNWIND_SUPPORT
- void *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..77b8521 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -579,7 +579,7 @@ static unw_accessors_t accessors = {
.get_proc_name = get_proc_name,
};

-int unwind__prepare_access(struct thread *thread)
+static int _unwind__prepare_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return 0;
@@ -594,7 +594,7 @@ int unwind__prepare_access(struct thread *thread)
return 0;
}

-void unwind__flush_access(struct thread *thread)
+static void _unwind__flush_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return;
@@ -602,7 +602,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 _unwind__finish_access(struct thread *thread)
{
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return;
@@ -662,7 +662,7 @@ 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,
+static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack)
{
@@ -680,3 +680,53 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,

return get_entries(&ui, cb, arg, max_stack);
}
+
+static struct unwind_libunwind_ops
+_unwind_libunwind_ops = {
+ .prepare_access = _unwind__prepare_access,
+ .flush_access = _unwind__flush_access,
+ .finish_access = _unwind__finish_access,
+ .get_entries = _unwind__get_entries,
+};
+
+struct unwind_libunwind_ops *
+local_unwind_libunwind_ops = &_unwind_libunwind_ops;
+
+void unwind__register_ops(struct thread *thread,
+ struct unwind_libunwind_ops *ops)
+{
+ thread->unwind_libunwind_ops = ops;
+}
+
+int unwind__prepare_access(struct thread *thread)
+{
+ if (thread->unwind_libunwind_ops)
+ return thread->unwind_libunwind_ops->prepare_access(thread);
+ else
+ return 0;
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 12790cf..bd7377b 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -14,6 +14,17 @@ 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 unwind_libunwind_ops *local_unwind_libunwind_ops;
+
#ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
@@ -24,6 +35,8 @@ 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__register_ops(struct thread *thread,
+ struct unwind_libunwind_ops *ops);
#else
static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
{
@@ -32,6 +45,9 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void
+unwind__register_ops(struct thread *thread __maybe_unused,
+ struct unwind_libunwind_ops *ops __maybe_unused) {}
#endif
#else
static inline int
@@ -51,5 +67,8 @@ static inline int unwind__prepare_access(struct thread *thread __maybe_unused)

static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void
+unwind__register_ops(struct thread *thread __maybe_unused,
+ struct unwind_libunwind_ops *ops __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */
--
1.8.5.2

Jiri Olsa

unread,
Jun 1, 2016, 4:40:07 AM6/1/16
to
I think we should fail the function, not just display error

> thread__put(thread);
> map__put(map);
> return 0;
> @@ -1403,7 +1406,10 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
> if (map == NULL)
> goto out_problem_map;
>
> - thread__insert_map(thread, map);
> + ret = thread__insert_map(thread, map);
> + if (ret)
> + pr_err("Thread insert map error\n");

same here

jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:40:07 AM6/1/16
to
is it needed to register NULL? it's there by init right?

jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:40:09 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:04AM +0000, He Kuang wrote:

SNIP

> ifndef NO_LIBUNWIND
> + have_libunwind :=
> 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

I dont see this defined being used..

jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:50:06 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:11AM +0000, He Kuang wrote:

SNIP

> +
> + 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
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 004fb1d..7746e09 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -101,6 +101,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
> libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
> libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
> +libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o

seems odd but I dont have any better idea.. let's see what
others have to say ;-)

thanks,
jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:50:06 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:01AM +0000, He Kuang wrote:
I think we should put unwind__register_ops call into unwind__prepare_access
in this patch (like you do in patch 4) and without the #else clause

this way you can keep unwind__register_ops local

thanks,
jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:50:07 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:12AM +0000, He Kuang wrote:

SNIP

> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 7746e09..fced833 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -102,6 +102,7 @@ libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
> libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
> libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
> +libperf-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
>
> libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
>
> diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
> new file mode 100644
> index 0000000..99c0d42
> --- /dev/null
> +++ b/tools/perf/util/libunwind/arm64.c
> @@ -0,0 +1,18 @@

also please add some comments in here describing how this file works
like that it setups defines to compile arch specific binary from the
generic one, which is then represented by the arm64_unwind_libunwind_ops,
which get assigned for each arm64 thread

or something along those lines

thanks,
jirka

> +#define REMOTE_UNWIND_LIBUNWIND
> +
> +#define LIBUNWIND__ARCH_REG_ID libunwind__arm64_reg_id
> +
> +#include "unwind.h"
> +#include "debug.h"
> +#include "libunwind-aarch64.h"
> +#include <../../../../arch/arm64/include/uapi/asm/perf_regs.h>
> +#include "../../arch/arm64/util/unwind-libunwind.c"
> +
> +#undef NO_LIBUNWIND_DEBUG_FRAME
> +#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
> +#define NO_LIBUNWIND_DEBUG_FRAME
> +#endif
> +#include "util/unwind-libunwind-local.c"
> +
> +struct unwind_libunwind_ops *
> +arm64_unwind_libunwind_ops = &_unwind_libunwind_ops;

SNIP

Jiri Olsa

unread,
Jun 1, 2016, 4:50:07 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:11AM +0000, He Kuang wrote:

SNIP

> diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
> new file mode 100644
> index 0000000..46b4111
> --- /dev/null
> +++ b/tools/perf/util/libunwind/x86_32.c
> @@ -0,0 +1,18 @@
> +#define REMOTE_UNWIND_LIBUNWIND
> +
> +#define LIBUNWIND__ARCH_REG_ID libunwind__x86_reg_id
> +
> +#include "unwind.h"
> +#include "debug.h"
> +#include "libunwind-x86.h"
> +#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
> +
> +#undef HAVE_ARCH_X86_64_SUPPORT
> +#include "../../arch/x86/util/unwind-libunwind.c"
> +
> +#undef NO_LIBUNWIND_DEBUG_FRAME
> +#define NO_LIBUNWIND_DEBUG_FRAME

so debug_frame is explicitly switched off for remote unwind? 2 things:

- could we have some comments/reasonning for un/setting defines in here
(and the other arch files)
- could you please use the #ifndef XXX #define XXX #endif way

thanks,
jirka

Jiri Olsa

unread,
Jun 1, 2016, 4:50:08 AM6/1/16
to
On Tue, May 31, 2016 at 11:19:11AM +0000, He Kuang wrote:

SNIP

> diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
> index e183390..5774317 100644
> --- a/tools/perf/util/unwind-libunwind.c
> +++ b/tools/perf/util/unwind-libunwind.c
> @@ -5,6 +5,7 @@
> #include "arch/common.h"
>
> struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
> +struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
>
> void unwind__register_ops(struct thread *thread,
> struct unwind_libunwind_ops *ops)
> @@ -30,7 +31,13 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
> dso_type == DSO__TYPE_64BIT, map->dso->name);
>
> arch = normalize_arch(thread->mg->machine->env->arch);
> - pr_debug("unwind: target platform=%s\n", arch);
> +
> + if (!strcmp(arch, "x86"))
> + if (dso_type != DSO__TYPE_64BIT)
> + ops = x86_32_unwind_libunwind_ops;
> +
> + if (!ops)
> + pr_err("unwind: target platform=%s is not supported\n", arch);

how could ops become NULL in here? it starts with local_unwind_libunwind_ops
I dont think this check is needed in here

jirka

Hekuang

unread,
Jun 1, 2016, 6:00:13 AM6/1/16
to
x86_32_unwind_libunwind_ops is a null pointer when x86_32
libunwind is not supported.

There's a weak defination if CONFIG_LIBUNWIND_X86 is not set.

struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;

In this case, ops is null and "x86_32 is not supported" error
message is showed up.

> jirka
>

Hekuang

unread,
Jun 1, 2016, 6:10:06 AM6/1/16
to


在 2016/6/1 16:40, Jiri Olsa 写道:
ok

Arnaldo Carvalho de Melo

unread,
Jun 1, 2016, 8:50:06 AM6/1/16
to
Em Wed, Jun 01, 2016 at 10:40:15AM +0200, Jiri Olsa escreveu:
> On Tue, May 31, 2016 at 11:19:11AM +0000, He Kuang wrote:

> SNIP
> > + 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
> > +++ b/tools/perf/util/Build
> > @@ -101,6 +101,7 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
> > libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
> > libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
> > +libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
>
> seems odd but I dont have any better idea.. let's see what
> others have to say ;-)

There was a lot of discussion in this patchkit, so I lost track of why I
should consider the above odd :-)

I.e. I take the above as: if x86 libunwind was detected or explicitely
selected, link support for it when generating the perf tool in any
architecture, which seems sensible, no?

- Arnaldo

Jiri Olsa

unread,
Jun 1, 2016, 9:20:08 AM6/1/16
to
yep.. the x86_32 name under generic dir is what seems odd to me,
but it's like you said.. anyway we can always change if we find
some better solution ;-)

jirka

He Kuang

unread,
Jun 2, 2016, 6:00:07 AM6/2/16
to
Currently, the type of thread->addr_space is unw_addr_space_t, which
is a pointer defined in libunwind headers. For local libunwind, we can
simple include "libunwind.h", but for remote libunwind, the header
file is depends on the target libunwind platform. This patch uses
'void *' instead to decouple the dependence on libunwind.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/thread.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 45fba13..aa3a8ff 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -9,9 +9,6 @@
#include "symbol.h"
#include <strlist.h>
#include <intlist.h>
-#ifdef HAVE_LIBUNWIND_SUPPORT
-#include <libunwind.h>
-#endif

struct thread_stack;

@@ -36,7 +33,7 @@ struct thread {
void *priv;
struct thread_stack *ts;
#ifdef HAVE_LIBUNWIND_SUPPORT

He Kuang

unread,
Jun 2, 2016, 6:00:07 AM6/2/16
to
For local libunwind, it uses the fixed methods to convert register id
according to the host platform, but in remote libunwind, this convert
function should be the one for remote architecture. This patch changes
the fixed name to macro and code for each remote platform can be
compiled indivadually.

Signed-off-by: He Kuang <hek...@huawei.com>
---
tools/perf/util/unwind-libunwind-local.c | 2 +-
tools/perf/util/unwind.h | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 9c70486..631b40d 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -508,7 +508,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;

diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index dda2156..a77b3dc 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -29,7 +29,10 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct perf_sample *data, int max_stack);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
-int libunwind__arch_reg_id(int regnum);
+#ifndef LIBUNWIND__ARCH_REG_ID
+#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
+#endif
+int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct thread *thread, struct map *map);
void unwind__flush_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
--
1.8.5.2
It is loading more messages.
0 new messages