Merge tag 'perf-core-for-mingo-20160607' of git://git.kernel.org/pub/scm/linux/kernel...
authorIngo Molnar <mingo@kernel.org>
Wed, 8 Jun 2016 07:34:15 +0000 (09:34 +0200)
committerIngo Molnar <mingo@kernel.org>
Wed, 8 Jun 2016 07:34:15 +0000 (09:34 +0200)
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

- Support cross unwinding, i.e. collecting '--call-graph dwarf' perf.data files
  in one machine and then doing analysis in another machine of a different
  hardware architecture. This enables, for instance, to do:

perf record -a --call-graph dwarf

  on a x86-32 or aarch64 system and then do 'perf report' on it on a
  x86_64 workstation. (He Kuang)

- Fix crash in build_id_cache__kallsyms_path(), recent regression (Wang Nan)

Infrastructure changes:

- Make tools/lib/bpf use the IS_ERR return facility consistently and also stop
  using the _get_ term for non-reference count methods (Arnaldo Carvalho de Melo)

- 'perf config' refactorings (Taeung Song)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
22 files changed:
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/perf/arch/arm/util/Build
tools/perf/arch/arm64/util/Build
tools/perf/arch/arm64/util/unwind-libunwind.c
tools/perf/arch/common.c
tools/perf/arch/common.h
tools/perf/arch/x86/util/Build
tools/perf/arch/x86/util/unwind-libunwind.c
tools/perf/config/Makefile
tools/perf/util/Build
tools/perf/util/bpf-loader.c
tools/perf/util/build-id.c
tools/perf/util/config.c
tools/perf/util/libunwind/arm64.c [new file with mode: 0644]
tools/perf/util/libunwind/x86_32.c [new file with mode: 0644]
tools/perf/util/machine.c
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/unwind-libunwind-local.c [new file with mode: 0644]
tools/perf/util/unwind-libunwind.c
tools/perf/util/unwind.h

index 7e543c3..462e526 100644 (file)
@@ -1186,20 +1186,14 @@ bpf_object__next(struct bpf_object *prev)
        return next;
 }
 
-const char *
-bpf_object__get_name(struct bpf_object *obj)
+const char *bpf_object__name(struct bpf_object *obj)
 {
-       if (!obj)
-               return ERR_PTR(-EINVAL);
-       return obj->path;
+       return obj ? obj->path : ERR_PTR(-EINVAL);
 }
 
-unsigned int
-bpf_object__get_kversion(struct bpf_object *obj)
+unsigned int bpf_object__kversion(struct bpf_object *obj)
 {
-       if (!obj)
-               return 0;
-       return obj->kern_version;
+       return obj ? obj->kern_version : 0;
 }
 
 struct bpf_program *
@@ -1224,9 +1218,8 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
        return &obj->programs[idx];
 }
 
-int bpf_program__set_private(struct bpf_program *prog,
-                            void *priv,
-                            bpf_program_clear_priv_t clear_priv)
+int bpf_program__set_priv(struct bpf_program *prog, void *priv,
+                         bpf_program_clear_priv_t clear_priv)
 {
        if (prog->priv && prog->clear_priv)
                prog->clear_priv(prog, prog->priv);
@@ -1236,10 +1229,9 @@ int bpf_program__set_private(struct bpf_program *prog,
        return 0;
 }
 
-int bpf_program__get_private(struct bpf_program *prog, void **ppriv)
+void *bpf_program__priv(struct bpf_program *prog)
 {
-       *ppriv = prog->priv;
-       return 0;
+       return prog ? prog->priv : ERR_PTR(-EINVAL);
 }
 
 const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
@@ -1311,32 +1303,23 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
        return fd;
 }
 
-int bpf_map__get_fd(struct bpf_map *map)
+int bpf_map__fd(struct bpf_map *map)
 {
-       if (!map)
-               return -EINVAL;
-
-       return map->fd;
+       return map ? map->fd : -EINVAL;
 }
 
-int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef)
+const struct bpf_map_def *bpf_map__def(struct bpf_map *map)
 {
-       if (!map || !pdef)
-               return -EINVAL;
-
-       *pdef = map->def;
-       return 0;
+       return map ? &map->def : ERR_PTR(-EINVAL);
 }
 
-const char *bpf_map__get_name(struct bpf_map *map)
+const char *bpf_map__name(struct bpf_map *map)
 {
-       if (!map)
-               return NULL;
-       return map->name;
+       return map ? map->name : NULL;
 }
 
-int bpf_map__set_private(struct bpf_map *map, void *priv,
-                        bpf_map_clear_priv_t clear_priv)
+int bpf_map__set_priv(struct bpf_map *map, void *priv,
+                    bpf_map_clear_priv_t clear_priv)
 {
        if (!map)
                return -EINVAL;
@@ -1351,14 +1334,9 @@ int bpf_map__set_private(struct bpf_map *map, void *priv,
        return 0;
 }
 
-int bpf_map__get_private(struct bpf_map *map, void **ppriv)
+void *bpf_map__priv(struct bpf_map *map)
 {
-       if (!map)
-               return -EINVAL;
-
-       if (ppriv)
-               *ppriv = map->priv;
-       return 0;
+       return map ? map->priv : ERR_PTR(-EINVAL);
 }
 
 struct bpf_map *
@@ -1389,7 +1367,7 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
 }
 
 struct bpf_map *
-bpf_object__get_map_by_name(struct bpf_object *obj, const char *name)
+bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
 {
        struct bpf_map *pos;
 
index a51594c..722f46b 100644 (file)
@@ -55,8 +55,8 @@ void bpf_object__close(struct bpf_object *object);
 /* Load/unload object into/from kernel */
 int bpf_object__load(struct bpf_object *obj);
 int bpf_object__unload(struct bpf_object *obj);
-const char *bpf_object__get_name(struct bpf_object *obj);
-unsigned int bpf_object__get_kversion(struct bpf_object *obj);
+const char *bpf_object__name(struct bpf_object *obj);
+unsigned int bpf_object__kversion(struct bpf_object *obj);
 
 struct bpf_object *bpf_object__next(struct bpf_object *prev);
 #define bpf_object__for_each_safe(pos, tmp)                    \
@@ -78,11 +78,10 @@ struct bpf_program *bpf_program__next(struct bpf_program *prog,
 typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
                                         void *);
 
-int bpf_program__set_private(struct bpf_program *prog, void *priv,
-                            bpf_program_clear_priv_t clear_priv);
+int bpf_program__set_priv(struct bpf_program *prog, void *priv,
+                         bpf_program_clear_priv_t clear_priv);
 
-int bpf_program__get_private(struct bpf_program *prog,
-                            void **ppriv);
+void *bpf_program__priv(struct bpf_program *prog);
 
 const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
 
@@ -171,7 +170,7 @@ struct bpf_map_def {
  */
 struct bpf_map;
 struct bpf_map *
-bpf_object__get_map_by_name(struct bpf_object *obj, const char *name);
+bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
 
 struct bpf_map *
 bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
@@ -180,13 +179,13 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
             (pos) != NULL;                             \
             (pos) = bpf_map__next((pos), (obj)))
 
-int bpf_map__get_fd(struct bpf_map *map);
-int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef);
-const char *bpf_map__get_name(struct bpf_map *map);
+int bpf_map__fd(struct bpf_map *map);
+const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
+const char *bpf_map__name(struct bpf_map *map);
 
 typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
-int bpf_map__set_private(struct bpf_map *map, void *priv,
-                        bpf_map_clear_priv_t clear_priv);
-int bpf_map__get_private(struct bpf_map *map, void **ppriv);
+int bpf_map__set_priv(struct bpf_map *map, void *priv,
+                     bpf_map_clear_priv_t clear_priv);
+void *bpf_map__priv(struct bpf_map *map);
 
 #endif
index d22e3d0..f98da17 100644 (file)
@@ -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
index e58123a..02f41db 100644 (file)
@@ -1,2 +1,2 @@
 libperf-$(CONFIG_DWARF)     += dwarf-regs.o
-libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
index a87afa9..c116b71 100644 (file)
@@ -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:
index e83c8ce..fa090a9 100644 (file)
@@ -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";
index 7529cfb..6b01c73 100644 (file)
@@ -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 */
index 4cd8a16..f95e6f4 100644 (file)
@@ -8,7 +8,7 @@ libperf-y += group.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
index db25e93..4f16661 100644 (file)
@@ -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;
 
index 5ad0255..098874b 100644 (file)
@@ -73,17 +73,25 @@ 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)
 
 # Set per-feature check compilation flags
 FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS)
-FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS)
+FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
 FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS)
-FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS)
+FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
 
 ifeq ($(NO_PERF_REGS),0)
   CFLAGS += -DHAVE_PERF_REGS_SUPPORT
@@ -351,10 +359,40 @@ 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
+
+  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
+  else
+    have_libunwind := 1
+    $(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 +430,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,8 +441,12 @@ 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)
 endif
index 8c6c8a0..fced833 100644 (file)
@@ -99,7 +99,10 @@ 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_LIBUNWIND_X86)      += libunwind/x86_32.o
+libperf-$(CONFIG_LIBUNWIND_AARCH64)  += libunwind/arm64.o
 
 libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
 
index 493307d..dcc8845 100644 (file)
@@ -339,7 +339,7 @@ config_bpf_program(struct bpf_program *prog)
        }
        pr_debug("bpf: config '%s' is ok\n", config_str);
 
-       err = bpf_program__set_private(prog, priv, clear_prog_priv);
+       err = bpf_program__set_priv(prog, priv, clear_prog_priv);
        if (err) {
                pr_debug("Failed to set priv for program '%s'\n", config_str);
                goto errout;
@@ -380,15 +380,14 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
                     struct bpf_insn *orig_insns, int orig_insns_cnt,
                     struct bpf_prog_prep_result *res)
 {
+       struct bpf_prog_priv *priv = bpf_program__priv(prog);
        struct probe_trace_event *tev;
        struct perf_probe_event *pev;
-       struct bpf_prog_priv *priv;
        struct bpf_insn *buf;
        size_t prologue_cnt = 0;
        int i, err;
 
-       err = bpf_program__get_private(prog, (void **)&priv);
-       if (err || !priv)
+       if (IS_ERR(priv) || !priv)
                goto errout;
 
        pev = &priv->pev;
@@ -535,13 +534,12 @@ static int map_prologue(struct perf_probe_event *pev, int *mapping,
 
 static int hook_load_preprocessor(struct bpf_program *prog)
 {
+       struct bpf_prog_priv *priv = bpf_program__priv(prog);
        struct perf_probe_event *pev;
-       struct bpf_prog_priv *priv;
        bool need_prologue = false;
        int err, i;
 
-       err = bpf_program__get_private(prog, (void **)&priv);
-       if (err || !priv) {
+       if (IS_ERR(priv) || !priv) {
                pr_debug("Internal error when hook preprocessor\n");
                return -BPF_LOADER_ERRNO__INTERNAL;
        }
@@ -607,9 +605,11 @@ int bpf__probe(struct bpf_object *obj)
                if (err)
                        goto out;
 
-               err = bpf_program__get_private(prog, (void **)&priv);
-               if (err || !priv)
+               priv = bpf_program__priv(prog);
+               if (IS_ERR(priv) || !priv) {
+                       err = PTR_ERR(priv);
                        goto out;
+               }
                pev = &priv->pev;
 
                err = convert_perf_probe_events(pev, 1);
@@ -645,13 +645,12 @@ int bpf__unprobe(struct bpf_object *obj)
 {
        int err, ret = 0;
        struct bpf_program *prog;
-       struct bpf_prog_priv *priv;
 
        bpf_object__for_each_program(prog, obj) {
+               struct bpf_prog_priv *priv = bpf_program__priv(prog);
                int i;
 
-               err = bpf_program__get_private(prog, (void **)&priv);
-               if (err || !priv)
+               if (IS_ERR(priv) || !priv)
                        continue;
 
                for (i = 0; i < priv->pev.ntevs; i++) {
@@ -702,14 +701,12 @@ int bpf__foreach_tev(struct bpf_object *obj,
        int err;
 
        bpf_object__for_each_program(prog, obj) {
+               struct bpf_prog_priv *priv = bpf_program__priv(prog);
                struct probe_trace_event *tev;
                struct perf_probe_event *pev;
-               struct bpf_prog_priv *priv;
                int i, fd;
 
-               err = bpf_program__get_private(prog,
-                               (void **)&priv);
-               if (err || !priv) {
+               if (IS_ERR(priv) || !priv) {
                        pr_debug("bpf: failed to get private field\n");
                        return -BPF_LOADER_ERRNO__INTERNAL;
                }
@@ -897,15 +894,12 @@ bpf_map_priv__clone(struct bpf_map_priv *priv)
 static int
 bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
 {
-       struct bpf_map_priv *priv;
-       const char *map_name;
-       int err;
+       const char *map_name = bpf_map__name(map);
+       struct bpf_map_priv *priv = bpf_map__priv(map);
 
-       map_name = bpf_map__get_name(map);
-       err = bpf_map__get_private(map, (void **)&priv);
-       if (err) {
+       if (IS_ERR(priv)) {
                pr_debug("Failed to get private from map %s\n", map_name);
-               return err;
+               return PTR_ERR(priv);
        }
 
        if (!priv) {
@@ -916,7 +910,7 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
                }
                INIT_LIST_HEAD(&priv->ops_list);
 
-               if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) {
+               if (bpf_map__set_priv(map, priv, bpf_map_priv__clear)) {
                        free(priv);
                        return -BPF_LOADER_ERRNO__INTERNAL;
                }
@@ -948,30 +942,26 @@ static int
 __bpf_map__config_value(struct bpf_map *map,
                        struct parse_events_term *term)
 {
-       struct bpf_map_def def;
        struct bpf_map_op *op;
-       const char *map_name;
-       int err;
+       const char *map_name = bpf_map__name(map);
+       const struct bpf_map_def *def = bpf_map__def(map);
 
-       map_name = bpf_map__get_name(map);
-
-       err = bpf_map__get_def(map, &def);
-       if (err) {
+       if (IS_ERR(def)) {
                pr_debug("Unable to get map definition from '%s'\n",
                         map_name);
                return -BPF_LOADER_ERRNO__INTERNAL;
        }
 
-       if (def.type != BPF_MAP_TYPE_ARRAY) {
+       if (def->type != BPF_MAP_TYPE_ARRAY) {
                pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n",
                         map_name);
                return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
        }
-       if (def.key_size < sizeof(unsigned int)) {
+       if (def->key_size < sizeof(unsigned int)) {
                pr_debug("Map %s has incorrect key size\n", map_name);
                return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE;
        }
-       switch (def.value_size) {
+       switch (def->value_size) {
        case 1:
        case 2:
        case 4:
@@ -1014,12 +1004,10 @@ __bpf_map__config_event(struct bpf_map *map,
                        struct perf_evlist *evlist)
 {
        struct perf_evsel *evsel;
-       struct bpf_map_def def;
+       const struct bpf_map_def *def;
        struct bpf_map_op *op;
-       const char *map_name;
-       int err;
+       const char *map_name = bpf_map__name(map);
 
-       map_name = bpf_map__get_name(map);
        evsel = perf_evlist__find_evsel_by_str(evlist, term->val.str);
        if (!evsel) {
                pr_debug("Event (for '%s') '%s' doesn't exist\n",
@@ -1027,18 +1015,18 @@ __bpf_map__config_event(struct bpf_map *map,
                return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT;
        }
 
-       err = bpf_map__get_def(map, &def);
-       if (err) {
+       def = bpf_map__def(map);
+       if (IS_ERR(def)) {
                pr_debug("Unable to get map definition from '%s'\n",
                         map_name);
-               return err;
+               return PTR_ERR(def);
        }
 
        /*
         * No need to check key_size and value_size:
         * kernel has already checked them.
         */
-       if (def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
+       if (def->type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
                pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
                         map_name);
                return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
@@ -1087,9 +1075,8 @@ config_map_indices_range_check(struct parse_events_term *term,
                               const char *map_name)
 {
        struct parse_events_array *array = &term->array;
-       struct bpf_map_def def;
+       const struct bpf_map_def *def;
        unsigned int i;
-       int err;
 
        if (!array->nr_ranges)
                return 0;
@@ -1099,8 +1086,8 @@ config_map_indices_range_check(struct parse_events_term *term,
                return -BPF_LOADER_ERRNO__INTERNAL;
        }
 
-       err = bpf_map__get_def(map, &def);
-       if (err) {
+       def = bpf_map__def(map);
+       if (IS_ERR(def)) {
                pr_debug("ERROR: Unable to get map definition from '%s'\n",
                         map_name);
                return -BPF_LOADER_ERRNO__INTERNAL;
@@ -1111,7 +1098,7 @@ config_map_indices_range_check(struct parse_events_term *term,
                size_t length = array->ranges[i].length;
                unsigned int idx = start + length - 1;
 
-               if (idx >= def.max_entries) {
+               if (idx >= def->max_entries) {
                        pr_debug("ERROR: index %d too large\n", idx);
                        return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG;
                }
@@ -1147,7 +1134,7 @@ bpf__obj_config_map(struct bpf_object *obj,
                goto out;
        }
 
-       map = bpf_object__get_map_by_name(obj, map_name);
+       map = bpf_object__find_map_by_name(obj, map_name);
        if (!map) {
                pr_debug("ERROR: Map %s doesn't exist\n", map_name);
                err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST;
@@ -1204,14 +1191,14 @@ out:
 }
 
 typedef int (*map_config_func_t)(const char *name, int map_fd,
-                                struct bpf_map_def *pdef,
+                                const struct bpf_map_def *pdef,
                                 struct bpf_map_op *op,
                                 void *pkey, void *arg);
 
 static int
 foreach_key_array_all(map_config_func_t func,
                      void *arg, const char *name,
-                     int map_fd, struct bpf_map_def *pdef,
+                     int map_fd, const struct bpf_map_def *pdef,
                      struct bpf_map_op *op)
 {
        unsigned int i;
@@ -1231,7 +1218,7 @@ foreach_key_array_all(map_config_func_t func,
 static int
 foreach_key_array_ranges(map_config_func_t func, void *arg,
                         const char *name, int map_fd,
-                        struct bpf_map_def *pdef,
+                        const struct bpf_map_def *pdef,
                         struct bpf_map_op *op)
 {
        unsigned int i, j;
@@ -1261,15 +1248,12 @@ bpf_map_config_foreach_key(struct bpf_map *map,
                           void *arg)
 {
        int err, map_fd;
-       const char *name;
        struct bpf_map_op *op;
-       struct bpf_map_def def;
-       struct bpf_map_priv *priv;
+       const struct bpf_map_def *def;
+       const char *name = bpf_map__name(map);
+       struct bpf_map_priv *priv = bpf_map__priv(map);
 
-       name = bpf_map__get_name(map);
-
-       err = bpf_map__get_private(map, (void **)&priv);
-       if (err) {
+       if (IS_ERR(priv)) {
                pr_debug("ERROR: failed to get private from map %s\n", name);
                return -BPF_LOADER_ERRNO__INTERNAL;
        }
@@ -1278,29 +1262,29 @@ bpf_map_config_foreach_key(struct bpf_map *map,
                return 0;
        }
 
-       err = bpf_map__get_def(map, &def);
-       if (err) {
+       def = bpf_map__def(map);
+       if (IS_ERR(def)) {
                pr_debug("ERROR: failed to get definition from map %s\n", name);
                return -BPF_LOADER_ERRNO__INTERNAL;
        }
-       map_fd = bpf_map__get_fd(map);
+       map_fd = bpf_map__fd(map);
        if (map_fd < 0) {
                pr_debug("ERROR: failed to get fd from map %s\n", name);
                return map_fd;
        }
 
        list_for_each_entry(op, &priv->ops_list, list) {
-               switch (def.type) {
+               switch (def->type) {
                case BPF_MAP_TYPE_ARRAY:
                case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
                        switch (op->key_type) {
                        case BPF_MAP_KEY_ALL:
                                err = foreach_key_array_all(func, arg, name,
-                                                           map_fd, &def, op);
+                                                           map_fd, def, op);
                                break;
                        case BPF_MAP_KEY_RANGES:
                                err = foreach_key_array_ranges(func, arg, name,
-                                                              map_fd, &def,
+                                                              map_fd, def,
                                                               op);
                                break;
                        default:
@@ -1410,7 +1394,7 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
 
 static int
 apply_obj_config_map_for_key(const char *name, int map_fd,
-                            struct bpf_map_def *pdef __maybe_unused,
+                            const struct bpf_map_def *pdef,
                             struct bpf_map_op *op,
                             void *pkey, void *arg __maybe_unused)
 {
@@ -1475,9 +1459,9 @@ int bpf__apply_obj_config(void)
 
 #define bpf__for_each_stdout_map(pos, obj, objtmp)     \
        bpf__for_each_map(pos, obj, objtmp)             \
-               if (bpf_map__get_name(pos) &&           \
+               if (bpf_map__name(pos) &&               \
                        (strcmp("__bpf_stdout__",       \
-                               bpf_map__get_name(pos)) == 0))
+                               bpf_map__name(pos)) == 0))
 
 int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
 {
@@ -1489,10 +1473,9 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
        bool need_init = false;
 
        bpf__for_each_stdout_map(map, obj, tmp) {
-               struct bpf_map_priv *priv;
+               struct bpf_map_priv *priv = bpf_map__priv(map);
 
-               err = bpf_map__get_private(map, (void **)&priv);
-               if (err)
+               if (IS_ERR(priv))
                        return -BPF_LOADER_ERRNO__INTERNAL;
 
                /*
@@ -1520,10 +1503,9 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
        }
 
        bpf__for_each_stdout_map(map, obj, tmp) {
-               struct bpf_map_priv *priv;
+               struct bpf_map_priv *priv = bpf_map__priv(map);
 
-               err = bpf_map__get_private(map, (void **)&priv);
-               if (err)
+               if (IS_ERR(priv))
                        return -BPF_LOADER_ERRNO__INTERNAL;
                if (priv)
                        continue;
@@ -1533,7 +1515,7 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
                        if (!priv)
                                return -ENOMEM;
 
-                       err = bpf_map__set_private(map, priv, bpf_map_priv__clear);
+                       err = bpf_map__set_priv(map, priv, bpf_map_priv__clear);
                        if (err) {
                                bpf_map_priv__clear(map, priv);
                                return err;
@@ -1677,7 +1659,7 @@ int bpf__strerror_load(struct bpf_object *obj,
 {
        bpf__strerror_head(err, buf, size);
        case LIBBPF_ERRNO__KVER: {
-               unsigned int obj_kver = bpf_object__get_kversion(obj);
+               unsigned int obj_kver = bpf_object__kversion(obj);
                unsigned int real_kver;
 
                if (fetch_kernel_version(&real_kver, NULL, 0)) {
index 67f986c..20aef90 100644 (file)
@@ -147,20 +147,17 @@ static int asnprintf(char **strp, size_t size, const char *fmt, ...)
 char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
                                    size_t size)
 {
-       bool is_alloc = !!bf;
        bool retry_old = true;
 
-       asnprintf(&bf, size, "%s/%s/%s/kallsyms",
-                 buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
+       snprintf(bf, size, "%s/%s/%s/kallsyms",
+                buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
 retry:
        if (!access(bf, F_OK))
                return bf;
-       if (is_alloc)
-               free(bf);
        if (retry_old) {
                /* Try old style kallsyms cache */
-               asnprintf(&bf, size, "%s/%s/%s",
-                         buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
+               snprintf(bf, size, "%s/%s/%s",
+                        buildid_dir, DSO__NAME_KALLSYMS, sbuild_id);
                retry_old = false;
                goto retry;
        }
index c73f1c4..8749eca 100644 (file)
@@ -643,17 +643,64 @@ static int collect_config(const char *var, const char *value,
 
 out_free:
        free(key);
-       perf_config_set__delete(set);
        return -1;
 }
 
+static int perf_config_set__init(struct perf_config_set *set)
+{
+       int ret = -1;
+       const char *home = NULL;
+
+       /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
+       if (config_exclusive_filename)
+               return perf_config_from_file(collect_config, config_exclusive_filename, set);
+       if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
+               if (perf_config_from_file(collect_config, perf_etc_perfconfig(), set) < 0)
+                       goto out;
+       }
+
+       home = getenv("HOME");
+       if (perf_config_global() && home) {
+               char *user_config = strdup(mkpath("%s/.perfconfig", home));
+               struct stat st;
+
+               if (user_config == NULL) {
+                       warning("Not enough memory to process %s/.perfconfig, "
+                               "ignoring it.", home);
+                       goto out;
+               }
+
+               if (stat(user_config, &st) < 0)
+                       goto out_free;
+
+               if (st.st_uid && (st.st_uid != geteuid())) {
+                       warning("File %s not owned by current user or root, "
+                               "ignoring it.", user_config);
+                       goto out_free;
+               }
+
+               if (!st.st_size)
+                       goto out_free;
+
+               ret = perf_config_from_file(collect_config, user_config, set);
+
+out_free:
+               free(user_config);
+       }
+out:
+       return ret;
+}
+
 struct perf_config_set *perf_config_set__new(void)
 {
        struct perf_config_set *set = zalloc(sizeof(*set));
 
        if (set) {
                INIT_LIST_HEAD(&set->sections);
-               perf_config(collect_config, set);
+               if (perf_config_set__init(set) < 0) {
+                       perf_config_set__delete(set);
+                       set = NULL;
+               }
        }
 
        return set;
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
new file mode 100644 (file)
index 0000000..4fb5395
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * This file setups defines to compile arch specific binary from the
+ * generic one.
+ *
+ * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
+ * name and the defination of this function is included directly from
+ * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function
+ * is defined no matter what arch the host is.
+ *
+ * Finally, the arch specific unwind methods are exported which will
+ * be assigned to each arm64 thread.
+ */
+
+#define REMOTE_UNWIND_LIBUNWIND
+
+#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum)
+
+#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"
+
+/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind,
+ * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64
+ * unwind methods.
+ */
+#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/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
new file mode 100644 (file)
index 0000000..d98c17e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file setups defines to compile arch specific binary from the
+ * generic one.
+ *
+ * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
+ * name and the defination of this function is included directly from
+ * 'arch/x86/util/unwind-libunwind.c', to make sure that this function
+ * is defined no matter what arch the host is.
+ *
+ * Finally, the arch specific unwind methods are exported which will
+ * be assigned to each x86 thread.
+ */
+
+#define REMOTE_UNWIND_LIBUNWIND
+#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum)
+
+#include "unwind.h"
+#include "debug.h"
+#include "libunwind-x86.h"
+#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
+
+/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c'
+ * for x86_32, we undef it to compile code for x86_32 only.
+ */
+#undef HAVE_ARCH_X86_64_SUPPORT
+#include "../../arch/x86/util/unwind-libunwind.c"
+
+/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
+ * dwarf_find_debug_frame() function.
+ */
+#ifndef NO_LIBUNWIND_DEBUG_FRAME
+#define NO_LIBUNWIND_DEBUG_FRAME
+#endif
+#include "util/unwind-libunwind-local.c"
+
+struct unwind_libunwind_ops *
+x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;
index b177218..a0c186a 100644 (file)
@@ -1353,11 +1353,16 @@ 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)
+               goto out_problem_insert;
+
        thread__put(thread);
        map__put(map);
        return 0;
 
+out_problem_insert:
+       map__put(map);
 out_problem_map:
        thread__put(thread);
 out_problem:
@@ -1403,11 +1408,16 @@ 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)
+               goto out_problem_insert;
+
        thread__put(thread);
        map__put(map);
        return 0;
 
+out_problem_insert:
+       map__put(map);
 out_problem_map:
        thread__put(thread);
 out_problem:
index ada58e6..f30f956 100644 (file)
@@ -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;
@@ -201,10 +198,18 @@ 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)
 {
+       int ret;
+
+       ret = unwind__prepare_access(thread, map);
+       if (ret)
+               return ret;
+
        map_groups__fixup_overlappings(thread->mg, map, stderr);
        map_groups__insert(thread->mg, map);
+
+       return 0;
 }
 
 static int thread__clone_map_groups(struct thread *thread,
index 08fcb14..99263cb 100644 (file)
@@ -9,11 +9,9 @@
 #include "symbol.h"
 #include <strlist.h>
 #include <intlist.h>
-#ifdef HAVE_LIBUNWIND_SUPPORT
-#include <libunwind.h>
-#endif
 
 struct thread_stack;
+struct unwind_libunwind_ops;
 
 struct thread {
        union {
@@ -36,7 +34,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
 };
 
@@ -77,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-local.c b/tools/perf/util/unwind-libunwind-local.c
new file mode 100644 (file)
index 0000000..01c2e86
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ * 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 <davidm@hpl.hp.com>
+ *
+ * And the bugs have been added by:
+ *
+ * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
+ * Copyright (C) 2012, Jiri Olsa <jolsa@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>
+#ifndef REMOTE_UNWIND_LIBUNWIND
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+#endif
+#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,
+};
+
+#ifndef REMOTE_UNWIND_LIBUNWIND
+struct unwind_libunwind_ops *
+local_unwind_libunwind_ops = &_unwind_libunwind_ops;
+#endif
index 63687d3..8547119 100644 (file)
-/*
- * 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 <davidm@hpl.hp.com>
- *
- * And the bugs have been added by:
- *
- * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
- * Copyright (C) 2012, Jiri Olsa <jolsa@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 <libunwind-ptrace.h>
-#include "callchain.h"
+#include "unwind.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 */
+#include "arch/common.h"
 
-/* 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_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;
 
-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)
+static void unwind__register_ops(struct thread *thread,
+                         struct unwind_libunwind_ops *ops)
 {
-       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;
+       thread->unwind_libunwind_ops = ops;
 }
 
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int elf_is_exec(int fd, const char *name)
+int unwind__prepare_access(struct thread *thread, struct map *map)
 {
-       Elf *elf;
-       GElf_Ehdr ehdr;
-       int retval = 0;
+       const char *arch;
+       enum dso_type dso_type;
+       struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
 
-       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
-       if (elf == NULL)
+       if (thread->addr_space) {
+               pr_debug("unwind: thread map already set, dso=%s\n",
+                        map->dso->name);
                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);
+       /* env->arch is NULL for live-mode (i.e. perf top) */
+       if (!thread->mg->machine->env || !thread->mg->machine->env->arch)
+               goto out_register;
 
-       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)
+       dso_type = dso__type(map->dso, thread->mg->machine);
+       if (dso_type == DSO__TYPE_UNKNOWN)
                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;
-}
+       arch = normalize_arch(thread->mg->machine->env->arch);
 
-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 (!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 (!map->dso)
+       if (!ops) {
+               pr_err("unwind: target platform=%s is not supported\n", arch);
                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,
-};
-
-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;
        }
+out_register:
+       unwind__register_ops(thread, ops);
 
-       unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL);
-       return 0;
+       return thread->unwind_libunwind_ops->prepare_access(thread);
 }
 
 void unwind__flush_access(struct thread *thread)
 {
-       if (callchain_param.record_mode != CALLCHAIN_DWARF)
-               return;
-
-       unw_flush_cache(thread->addr_space, 0, 0);
+       if (thread->unwind_libunwind_ops)
+               thread->unwind_libunwind_ops->flush_access(thread);
 }
 
 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;
+       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)
+                        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);
+       if (thread->unwind_libunwind_ops)
+               return thread->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack);
+       return 0;
 }
index 12790cf..b074662 100644 (file)
@@ -14,18 +14,31 @@ 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);
+};
+
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
 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);
+#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);
 #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;
 }
@@ -44,7 +57,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
        return 0;
 }
 
-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;
 }