diff options
| author | Ingo Molnar <mingo@kernel.org> | 2016-06-08 03:34:15 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2016-06-08 03:34:15 -0400 |
| commit | b8ab92201a64886fd3d8078fe46e51f658379823 (patch) | |
| tree | 120b6304d64e732b65ecbfccbeb14587bac1063c | |
| parent | aa3a655b159f11b1afe0dcdac5fb5b172f02b778 (diff) | |
| parent | 057fbfb25cde4a368418f3f720cdc31d48800c4d (diff) | |
Merge tag 'perf-core-for-mingo-20160607' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- 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>
| -rw-r--r-- | tools/lib/bpf/libbpf.c | 60 | ||||
| -rw-r--r-- | tools/lib/bpf/libbpf.h | 25 | ||||
| -rw-r--r-- | tools/perf/arch/arm/util/Build | 2 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/util/Build | 2 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/util/unwind-libunwind.c | 4 | ||||
| -rw-r--r-- | tools/perf/arch/common.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/common.h | 1 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/Build | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/unwind-libunwind.c | 6 | ||||
| -rw-r--r-- | tools/perf/config/Makefile | 52 | ||||
| -rw-r--r-- | tools/perf/util/Build | 3 | ||||
| -rw-r--r-- | tools/perf/util/bpf-loader.c | 132 | ||||
| -rw-r--r-- | tools/perf/util/build-id.c | 11 | ||||
| -rw-r--r-- | tools/perf/util/config.c | 51 | ||||
| -rw-r--r-- | tools/perf/util/libunwind/arm64.c | 35 | ||||
| -rw-r--r-- | tools/perf/util/libunwind/x86_32.c | 37 | ||||
| -rw-r--r-- | tools/perf/util/machine.c | 14 | ||||
| -rw-r--r-- | tools/perf/util/thread.c | 13 | ||||
| -rw-r--r-- | tools/perf/util/thread.h | 9 | ||||
| -rw-r--r-- | tools/perf/util/unwind-libunwind-local.c | 697 | ||||
| -rw-r--r-- | tools/perf/util/unwind-libunwind.c | 688 | ||||
| -rw-r--r-- | tools/perf/util/unwind.h | 22 |
22 files changed, 1056 insertions, 812 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7e543c3102d4..462e526a4465 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
| @@ -1186,20 +1186,14 @@ bpf_object__next(struct bpf_object *prev) | |||
| 1186 | return next; | 1186 | return next; |
| 1187 | } | 1187 | } |
| 1188 | 1188 | ||
| 1189 | const char * | 1189 | const char *bpf_object__name(struct bpf_object *obj) |
| 1190 | bpf_object__get_name(struct bpf_object *obj) | ||
| 1191 | { | 1190 | { |
| 1192 | if (!obj) | 1191 | return obj ? obj->path : ERR_PTR(-EINVAL); |
| 1193 | return ERR_PTR(-EINVAL); | ||
| 1194 | return obj->path; | ||
| 1195 | } | 1192 | } |
| 1196 | 1193 | ||
| 1197 | unsigned int | 1194 | unsigned int bpf_object__kversion(struct bpf_object *obj) |
| 1198 | bpf_object__get_kversion(struct bpf_object *obj) | ||
| 1199 | { | 1195 | { |
| 1200 | if (!obj) | 1196 | return obj ? obj->kern_version : 0; |
| 1201 | return 0; | ||
| 1202 | return obj->kern_version; | ||
| 1203 | } | 1197 | } |
| 1204 | 1198 | ||
| 1205 | struct bpf_program * | 1199 | struct bpf_program * |
| @@ -1224,9 +1218,8 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) | |||
| 1224 | return &obj->programs[idx]; | 1218 | return &obj->programs[idx]; |
| 1225 | } | 1219 | } |
| 1226 | 1220 | ||
| 1227 | int bpf_program__set_private(struct bpf_program *prog, | 1221 | int bpf_program__set_priv(struct bpf_program *prog, void *priv, |
| 1228 | void *priv, | 1222 | bpf_program_clear_priv_t clear_priv) |
| 1229 | bpf_program_clear_priv_t clear_priv) | ||
| 1230 | { | 1223 | { |
| 1231 | if (prog->priv && prog->clear_priv) | 1224 | if (prog->priv && prog->clear_priv) |
| 1232 | prog->clear_priv(prog, prog->priv); | 1225 | prog->clear_priv(prog, prog->priv); |
| @@ -1236,10 +1229,9 @@ int bpf_program__set_private(struct bpf_program *prog, | |||
| 1236 | return 0; | 1229 | return 0; |
| 1237 | } | 1230 | } |
| 1238 | 1231 | ||
| 1239 | int bpf_program__get_private(struct bpf_program *prog, void **ppriv) | 1232 | void *bpf_program__priv(struct bpf_program *prog) |
| 1240 | { | 1233 | { |
| 1241 | *ppriv = prog->priv; | 1234 | return prog ? prog->priv : ERR_PTR(-EINVAL); |
| 1242 | return 0; | ||
| 1243 | } | 1235 | } |
| 1244 | 1236 | ||
| 1245 | const char *bpf_program__title(struct bpf_program *prog, bool needs_copy) | 1237 | 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) | |||
| 1311 | return fd; | 1303 | return fd; |
| 1312 | } | 1304 | } |
| 1313 | 1305 | ||
| 1314 | int bpf_map__get_fd(struct bpf_map *map) | 1306 | int bpf_map__fd(struct bpf_map *map) |
| 1315 | { | 1307 | { |
| 1316 | if (!map) | 1308 | return map ? map->fd : -EINVAL; |
| 1317 | return -EINVAL; | ||
| 1318 | |||
| 1319 | return map->fd; | ||
| 1320 | } | 1309 | } |
| 1321 | 1310 | ||
| 1322 | int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef) | 1311 | const struct bpf_map_def *bpf_map__def(struct bpf_map *map) |
| 1323 | { | 1312 | { |
| 1324 | if (!map || !pdef) | 1313 | return map ? &map->def : ERR_PTR(-EINVAL); |
| 1325 | return -EINVAL; | ||
| 1326 | |||
| 1327 | *pdef = map->def; | ||
| 1328 | return 0; | ||
| 1329 | } | 1314 | } |
| 1330 | 1315 | ||
| 1331 | const char *bpf_map__get_name(struct bpf_map *map) | 1316 | const char *bpf_map__name(struct bpf_map *map) |
| 1332 | { | 1317 | { |
| 1333 | if (!map) | 1318 | return map ? map->name : NULL; |
| 1334 | return NULL; | ||
| 1335 | return map->name; | ||
| 1336 | } | 1319 | } |
| 1337 | 1320 | ||
| 1338 | int bpf_map__set_private(struct bpf_map *map, void *priv, | 1321 | int bpf_map__set_priv(struct bpf_map *map, void *priv, |
| 1339 | bpf_map_clear_priv_t clear_priv) | 1322 | bpf_map_clear_priv_t clear_priv) |
| 1340 | { | 1323 | { |
| 1341 | if (!map) | 1324 | if (!map) |
| 1342 | return -EINVAL; | 1325 | return -EINVAL; |
| @@ -1351,14 +1334,9 @@ int bpf_map__set_private(struct bpf_map *map, void *priv, | |||
| 1351 | return 0; | 1334 | return 0; |
| 1352 | } | 1335 | } |
| 1353 | 1336 | ||
| 1354 | int bpf_map__get_private(struct bpf_map *map, void **ppriv) | 1337 | void *bpf_map__priv(struct bpf_map *map) |
| 1355 | { | 1338 | { |
| 1356 | if (!map) | 1339 | return map ? map->priv : ERR_PTR(-EINVAL); |
| 1357 | return -EINVAL; | ||
| 1358 | |||
| 1359 | if (ppriv) | ||
| 1360 | *ppriv = map->priv; | ||
| 1361 | return 0; | ||
| 1362 | } | 1340 | } |
| 1363 | 1341 | ||
| 1364 | struct bpf_map * | 1342 | struct bpf_map * |
| @@ -1389,7 +1367,7 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj) | |||
| 1389 | } | 1367 | } |
| 1390 | 1368 | ||
| 1391 | struct bpf_map * | 1369 | struct bpf_map * |
| 1392 | bpf_object__get_map_by_name(struct bpf_object *obj, const char *name) | 1370 | bpf_object__find_map_by_name(struct bpf_object *obj, const char *name) |
| 1393 | { | 1371 | { |
| 1394 | struct bpf_map *pos; | 1372 | struct bpf_map *pos; |
| 1395 | 1373 | ||
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index a51594c7b518..722f46b2d553 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h | |||
| @@ -55,8 +55,8 @@ void bpf_object__close(struct bpf_object *object); | |||
| 55 | /* Load/unload object into/from kernel */ | 55 | /* Load/unload object into/from kernel */ |
| 56 | int bpf_object__load(struct bpf_object *obj); | 56 | int bpf_object__load(struct bpf_object *obj); |
| 57 | int bpf_object__unload(struct bpf_object *obj); | 57 | int bpf_object__unload(struct bpf_object *obj); |
| 58 | const char *bpf_object__get_name(struct bpf_object *obj); | 58 | const char *bpf_object__name(struct bpf_object *obj); |
| 59 | unsigned int bpf_object__get_kversion(struct bpf_object *obj); | 59 | unsigned int bpf_object__kversion(struct bpf_object *obj); |
| 60 | 60 | ||
| 61 | struct bpf_object *bpf_object__next(struct bpf_object *prev); | 61 | struct bpf_object *bpf_object__next(struct bpf_object *prev); |
| 62 | #define bpf_object__for_each_safe(pos, tmp) \ | 62 | #define bpf_object__for_each_safe(pos, tmp) \ |
| @@ -78,11 +78,10 @@ struct bpf_program *bpf_program__next(struct bpf_program *prog, | |||
| 78 | typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, | 78 | typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, |
| 79 | void *); | 79 | void *); |
| 80 | 80 | ||
| 81 | int bpf_program__set_private(struct bpf_program *prog, void *priv, | 81 | int bpf_program__set_priv(struct bpf_program *prog, void *priv, |
| 82 | bpf_program_clear_priv_t clear_priv); | 82 | bpf_program_clear_priv_t clear_priv); |
| 83 | 83 | ||
| 84 | int bpf_program__get_private(struct bpf_program *prog, | 84 | void *bpf_program__priv(struct bpf_program *prog); |
| 85 | void **ppriv); | ||
| 86 | 85 | ||
| 87 | const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); | 86 | const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); |
| 88 | 87 | ||
| @@ -171,7 +170,7 @@ struct bpf_map_def { | |||
| 171 | */ | 170 | */ |
| 172 | struct bpf_map; | 171 | struct bpf_map; |
| 173 | struct bpf_map * | 172 | struct bpf_map * |
| 174 | bpf_object__get_map_by_name(struct bpf_object *obj, const char *name); | 173 | bpf_object__find_map_by_name(struct bpf_object *obj, const char *name); |
| 175 | 174 | ||
| 176 | struct bpf_map * | 175 | struct bpf_map * |
| 177 | bpf_map__next(struct bpf_map *map, struct bpf_object *obj); | 176 | 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); | |||
| 180 | (pos) != NULL; \ | 179 | (pos) != NULL; \ |
| 181 | (pos) = bpf_map__next((pos), (obj))) | 180 | (pos) = bpf_map__next((pos), (obj))) |
| 182 | 181 | ||
| 183 | int bpf_map__get_fd(struct bpf_map *map); | 182 | int bpf_map__fd(struct bpf_map *map); |
| 184 | int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef); | 183 | const struct bpf_map_def *bpf_map__def(struct bpf_map *map); |
| 185 | const char *bpf_map__get_name(struct bpf_map *map); | 184 | const char *bpf_map__name(struct bpf_map *map); |
| 186 | 185 | ||
| 187 | typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); | 186 | typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); |
| 188 | int bpf_map__set_private(struct bpf_map *map, void *priv, | 187 | int bpf_map__set_priv(struct bpf_map *map, void *priv, |
| 189 | bpf_map_clear_priv_t clear_priv); | 188 | bpf_map_clear_priv_t clear_priv); |
| 190 | int bpf_map__get_private(struct bpf_map *map, void **ppriv); | 189 | void *bpf_map__priv(struct bpf_map *map); |
| 191 | 190 | ||
| 192 | #endif | 191 | #endif |
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build index d22e3d07de3d..f98da17357c0 100644 --- a/tools/perf/arch/arm/util/Build +++ b/tools/perf/arch/arm/util/Build | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | libperf-$(CONFIG_DWARF) += dwarf-regs.o | 1 | libperf-$(CONFIG_DWARF) += dwarf-regs.o |
| 2 | 2 | ||
| 3 | libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o | 3 | libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o |
| 4 | libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o | 4 | 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 e58123a8912b..02f41dba4f4f 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | libperf-$(CONFIG_DWARF) += dwarf-regs.o | 1 | libperf-$(CONFIG_DWARF) += dwarf-regs.o |
| 2 | libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o | 2 | libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o |
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c index a87afa91a99e..c116b713f7f7 100644 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c | |||
| @@ -1,11 +1,13 @@ | |||
| 1 | 1 | ||
| 2 | #ifndef REMOTE_UNWIND_LIBUNWIND | ||
| 2 | #include <errno.h> | 3 | #include <errno.h> |
| 3 | #include <libunwind.h> | 4 | #include <libunwind.h> |
| 4 | #include "perf_regs.h" | 5 | #include "perf_regs.h" |
| 5 | #include "../../util/unwind.h" | 6 | #include "../../util/unwind.h" |
| 6 | #include "../../util/debug.h" | 7 | #include "../../util/debug.h" |
| 8 | #endif | ||
| 7 | 9 | ||
| 8 | int libunwind__arch_reg_id(int regnum) | 10 | int LIBUNWIND__ARCH_REG_ID(int regnum) |
| 9 | { | 11 | { |
| 10 | switch (regnum) { | 12 | switch (regnum) { |
| 11 | case UNW_AARCH64_X0: | 13 | case UNW_AARCH64_X0: |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index e83c8ce24303..fa090a9eaa38 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) | |||
| 102 | * Return architecture name in a normalized form. | 102 | * Return architecture name in a normalized form. |
| 103 | * The conversion logic comes from the Makefile. | 103 | * The conversion logic comes from the Makefile. |
| 104 | */ | 104 | */ |
| 105 | static const char *normalize_arch(char *arch) | 105 | const char *normalize_arch(char *arch) |
| 106 | { | 106 | { |
| 107 | if (!strcmp(arch, "x86_64")) | 107 | if (!strcmp(arch, "x86_64")) |
| 108 | return "x86"; | 108 | return "x86"; |
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h index 7529cfb143ce..6b01c736b7d9 100644 --- a/tools/perf/arch/common.h +++ b/tools/perf/arch/common.h | |||
| @@ -6,5 +6,6 @@ | |||
| 6 | extern const char *objdump_path; | 6 | extern const char *objdump_path; |
| 7 | 7 | ||
| 8 | int perf_env__lookup_objdump(struct perf_env *env); | 8 | int perf_env__lookup_objdump(struct perf_env *env); |
| 9 | const char *normalize_arch(char *arch); | ||
| 9 | 10 | ||
| 10 | #endif /* ARCH_PERF_COMMON_H */ | 11 | #endif /* ARCH_PERF_COMMON_H */ |
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index 4cd8a16b1b7b..f95e6f46ef0d 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build | |||
| @@ -8,7 +8,7 @@ libperf-y += group.o | |||
| 8 | libperf-$(CONFIG_DWARF) += dwarf-regs.o | 8 | libperf-$(CONFIG_DWARF) += dwarf-regs.o |
| 9 | libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o | 9 | libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o |
| 10 | 10 | ||
| 11 | libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o | 11 | libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o |
| 12 | libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o | 12 | libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o |
| 13 | 13 | ||
| 14 | libperf-$(CONFIG_AUXTRACE) += auxtrace.o | 14 | libperf-$(CONFIG_AUXTRACE) += auxtrace.o |
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c index db25e93d989c..4f16661cbdbb 100644 --- a/tools/perf/arch/x86/util/unwind-libunwind.c +++ b/tools/perf/arch/x86/util/unwind-libunwind.c | |||
| @@ -1,12 +1,14 @@ | |||
| 1 | 1 | ||
| 2 | #ifndef REMOTE_UNWIND_LIBUNWIND | ||
| 2 | #include <errno.h> | 3 | #include <errno.h> |
| 3 | #include <libunwind.h> | 4 | #include <libunwind.h> |
| 4 | #include "perf_regs.h" | 5 | #include "perf_regs.h" |
| 5 | #include "../../util/unwind.h" | 6 | #include "../../util/unwind.h" |
| 6 | #include "../../util/debug.h" | 7 | #include "../../util/debug.h" |
| 8 | #endif | ||
| 7 | 9 | ||
| 8 | #ifdef HAVE_ARCH_X86_64_SUPPORT | 10 | #ifdef HAVE_ARCH_X86_64_SUPPORT |
| 9 | int libunwind__arch_reg_id(int regnum) | 11 | int LIBUNWIND__ARCH_REG_ID(int regnum) |
| 10 | { | 12 | { |
| 11 | int id; | 13 | int id; |
| 12 | 14 | ||
| @@ -70,7 +72,7 @@ int libunwind__arch_reg_id(int regnum) | |||
| 70 | return id; | 72 | return id; |
| 71 | } | 73 | } |
| 72 | #else | 74 | #else |
| 73 | int libunwind__arch_reg_id(int regnum) | 75 | int LIBUNWIND__ARCH_REG_ID(int regnum) |
| 74 | { | 76 | { |
| 75 | int id; | 77 | int id; |
| 76 | 78 | ||
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 5ad0255f8756..098874b99981 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
| @@ -73,17 +73,25 @@ endif | |||
| 73 | # | 73 | # |
| 74 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | 74 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ |
| 75 | # | 75 | # |
| 76 | |||
| 77 | libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code)) | ||
| 78 | define libunwind_arch_set_flags_code | ||
| 79 | FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include | ||
| 80 | FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib | ||
| 81 | endef | ||
| 82 | |||
| 76 | ifdef LIBUNWIND_DIR | 83 | ifdef LIBUNWIND_DIR |
| 77 | LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include | 84 | LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include |
| 78 | LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib | 85 | LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib |
| 86 | LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 | ||
| 87 | $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch))) | ||
| 79 | endif | 88 | endif |
| 80 | LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS) | ||
| 81 | 89 | ||
| 82 | # Set per-feature check compilation flags | 90 | # Set per-feature check compilation flags |
| 83 | FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) | 91 | FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) |
| 84 | FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) | 92 | FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) |
| 85 | FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) | 93 | FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) |
| 86 | FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) | 94 | FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) |
| 87 | 95 | ||
| 88 | ifeq ($(NO_PERF_REGS),0) | 96 | ifeq ($(NO_PERF_REGS),0) |
| 89 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT | 97 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT |
| @@ -351,10 +359,40 @@ ifeq ($(ARCH),powerpc) | |||
| 351 | endif | 359 | endif |
| 352 | 360 | ||
| 353 | ifndef NO_LIBUNWIND | 361 | ifndef NO_LIBUNWIND |
| 362 | have_libunwind := | ||
| 363 | |||
| 364 | ifeq ($(feature-libunwind-x86), 1) | ||
| 365 | $(call detected,CONFIG_LIBUNWIND_X86) | ||
| 366 | CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT | ||
| 367 | LDFLAGS += -lunwind-x86 | ||
| 368 | have_libunwind = 1 | ||
| 369 | endif | ||
| 370 | |||
| 371 | ifeq ($(feature-libunwind-aarch64), 1) | ||
| 372 | $(call detected,CONFIG_LIBUNWIND_AARCH64) | ||
| 373 | CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT | ||
| 374 | LDFLAGS += -lunwind-aarch64 | ||
| 375 | have_libunwind = 1 | ||
| 376 | $(call feature_check,libunwind-debug-frame-aarch64) | ||
| 377 | ifneq ($(feature-libunwind-debug-frame-aarch64), 1) | ||
| 378 | msg := $(warning No debug_frame support found in libunwind-aarch64); | ||
| 379 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64 | ||
| 380 | endif | ||
| 381 | endif | ||
| 382 | |||
| 354 | ifneq ($(feature-libunwind), 1) | 383 | ifneq ($(feature-libunwind), 1) |
| 355 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); | 384 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); |
| 385 | NO_LOCAL_LIBUNWIND := 1 | ||
| 386 | else | ||
| 387 | have_libunwind := 1 | ||
| 388 | $(call detected,CONFIG_LOCAL_LIBUNWIND) | ||
| 389 | endif | ||
| 390 | |||
| 391 | ifneq ($(have_libunwind), 1) | ||
| 356 | NO_LIBUNWIND := 1 | 392 | NO_LIBUNWIND := 1 |
| 357 | endif | 393 | endif |
| 394 | else | ||
| 395 | NO_LOCAL_LIBUNWIND := 1 | ||
| 358 | endif | 396 | endif |
| 359 | 397 | ||
| 360 | ifndef NO_LIBBPF | 398 | ifndef NO_LIBBPF |
| @@ -392,7 +430,7 @@ else | |||
| 392 | NO_DWARF_UNWIND := 1 | 430 | NO_DWARF_UNWIND := 1 |
| 393 | endif | 431 | endif |
| 394 | 432 | ||
| 395 | ifndef NO_LIBUNWIND | 433 | ifndef NO_LOCAL_LIBUNWIND |
| 396 | ifeq ($(ARCH),$(filter $(ARCH),arm arm64)) | 434 | ifeq ($(ARCH),$(filter $(ARCH),arm arm64)) |
| 397 | $(call feature_check,libunwind-debug-frame) | 435 | $(call feature_check,libunwind-debug-frame) |
| 398 | ifneq ($(feature-libunwind-debug-frame), 1) | 436 | ifneq ($(feature-libunwind-debug-frame), 1) |
| @@ -403,8 +441,12 @@ ifndef NO_LIBUNWIND | |||
| 403 | # non-ARM has no dwarf_find_debug_frame() function: | 441 | # non-ARM has no dwarf_find_debug_frame() function: |
| 404 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME | 442 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME |
| 405 | endif | 443 | endif |
| 406 | CFLAGS += -DHAVE_LIBUNWIND_SUPPORT | ||
| 407 | EXTLIBS += $(LIBUNWIND_LIBS) | 444 | EXTLIBS += $(LIBUNWIND_LIBS) |
| 445 | LDFLAGS += $(LIBUNWIND_LIBS) | ||
| 446 | endif | ||
| 447 | |||
| 448 | ifndef NO_LIBUNWIND | ||
| 449 | CFLAGS += -DHAVE_LIBUNWIND_SUPPORT | ||
| 408 | CFLAGS += $(LIBUNWIND_CFLAGS) | 450 | CFLAGS += $(LIBUNWIND_CFLAGS) |
| 409 | LDFLAGS += $(LIBUNWIND_LDFLAGS) | 451 | LDFLAGS += $(LIBUNWIND_LDFLAGS) |
| 410 | endif | 452 | endif |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 8c6c8a0ca642..fced8336e5fd 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
| @@ -99,7 +99,10 @@ libperf-$(CONFIG_DWARF) += probe-finder.o | |||
| 99 | libperf-$(CONFIG_DWARF) += dwarf-aux.o | 99 | libperf-$(CONFIG_DWARF) += dwarf-aux.o |
| 100 | 100 | ||
| 101 | libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o | 101 | libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o |
| 102 | libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o | ||
| 102 | libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o | 103 | libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o |
| 104 | libperf-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o | ||
| 105 | libperf-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o | ||
| 103 | 106 | ||
| 104 | libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o | 107 | libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o |
| 105 | 108 | ||
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 493307d1414c..dcc8845881ae 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
| @@ -339,7 +339,7 @@ config_bpf_program(struct bpf_program *prog) | |||
| 339 | } | 339 | } |
| 340 | pr_debug("bpf: config '%s' is ok\n", config_str); | 340 | pr_debug("bpf: config '%s' is ok\n", config_str); |
| 341 | 341 | ||
| 342 | err = bpf_program__set_private(prog, priv, clear_prog_priv); | 342 | err = bpf_program__set_priv(prog, priv, clear_prog_priv); |
| 343 | if (err) { | 343 | if (err) { |
| 344 | pr_debug("Failed to set priv for program '%s'\n", config_str); | 344 | pr_debug("Failed to set priv for program '%s'\n", config_str); |
| 345 | goto errout; | 345 | goto errout; |
| @@ -380,15 +380,14 @@ preproc_gen_prologue(struct bpf_program *prog, int n, | |||
| 380 | struct bpf_insn *orig_insns, int orig_insns_cnt, | 380 | struct bpf_insn *orig_insns, int orig_insns_cnt, |
| 381 | struct bpf_prog_prep_result *res) | 381 | struct bpf_prog_prep_result *res) |
| 382 | { | 382 | { |
| 383 | struct bpf_prog_priv *priv = bpf_program__priv(prog); | ||
| 383 | struct probe_trace_event *tev; | 384 | struct probe_trace_event *tev; |
| 384 | struct perf_probe_event *pev; | 385 | struct perf_probe_event *pev; |
| 385 | struct bpf_prog_priv *priv; | ||
| 386 | struct bpf_insn *buf; | 386 | struct bpf_insn *buf; |
| 387 | size_t prologue_cnt = 0; | 387 | size_t prologue_cnt = 0; |
| 388 | int i, err; | 388 | int i, err; |
| 389 | 389 | ||
| 390 | err = bpf_program__get_private(prog, (void **)&priv); | 390 | if (IS_ERR(priv) || !priv) |
| 391 | if (err || !priv) | ||
| 392 | goto errout; | 391 | goto errout; |
| 393 | 392 | ||
| 394 | pev = &priv->pev; | 393 | pev = &priv->pev; |
| @@ -535,13 +534,12 @@ static int map_prologue(struct perf_probe_event *pev, int *mapping, | |||
| 535 | 534 | ||
| 536 | static int hook_load_preprocessor(struct bpf_program *prog) | 535 | static int hook_load_preprocessor(struct bpf_program *prog) |
| 537 | { | 536 | { |
| 537 | struct bpf_prog_priv *priv = bpf_program__priv(prog); | ||
| 538 | struct perf_probe_event *pev; | 538 | struct perf_probe_event *pev; |
| 539 | struct bpf_prog_priv *priv; | ||
| 540 | bool need_prologue = false; | 539 | bool need_prologue = false; |
| 541 | int err, i; | 540 | int err, i; |
| 542 | 541 | ||
| 543 | err = bpf_program__get_private(prog, (void **)&priv); | 542 | if (IS_ERR(priv) || !priv) { |
| 544 | if (err || !priv) { | ||
| 545 | pr_debug("Internal error when hook preprocessor\n"); | 543 | pr_debug("Internal error when hook preprocessor\n"); |
| 546 | return -BPF_LOADER_ERRNO__INTERNAL; | 544 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 547 | } | 545 | } |
| @@ -607,9 +605,11 @@ int bpf__probe(struct bpf_object *obj) | |||
| 607 | if (err) | 605 | if (err) |
| 608 | goto out; | 606 | goto out; |
| 609 | 607 | ||
| 610 | err = bpf_program__get_private(prog, (void **)&priv); | 608 | priv = bpf_program__priv(prog); |
| 611 | if (err || !priv) | 609 | if (IS_ERR(priv) || !priv) { |
| 610 | err = PTR_ERR(priv); | ||
| 612 | goto out; | 611 | goto out; |
| 612 | } | ||
| 613 | pev = &priv->pev; | 613 | pev = &priv->pev; |
| 614 | 614 | ||
| 615 | err = convert_perf_probe_events(pev, 1); | 615 | err = convert_perf_probe_events(pev, 1); |
| @@ -645,13 +645,12 @@ int bpf__unprobe(struct bpf_object *obj) | |||
| 645 | { | 645 | { |
| 646 | int err, ret = 0; | 646 | int err, ret = 0; |
| 647 | struct bpf_program *prog; | 647 | struct bpf_program *prog; |
| 648 | struct bpf_prog_priv *priv; | ||
| 649 | 648 | ||
| 650 | bpf_object__for_each_program(prog, obj) { | 649 | bpf_object__for_each_program(prog, obj) { |
| 650 | struct bpf_prog_priv *priv = bpf_program__priv(prog); | ||
| 651 | int i; | 651 | int i; |
| 652 | 652 | ||
| 653 | err = bpf_program__get_private(prog, (void **)&priv); | 653 | if (IS_ERR(priv) || !priv) |
| 654 | if (err || !priv) | ||
| 655 | continue; | 654 | continue; |
| 656 | 655 | ||
| 657 | for (i = 0; i < priv->pev.ntevs; i++) { | 656 | for (i = 0; i < priv->pev.ntevs; i++) { |
| @@ -702,14 +701,12 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
| 702 | int err; | 701 | int err; |
| 703 | 702 | ||
| 704 | bpf_object__for_each_program(prog, obj) { | 703 | bpf_object__for_each_program(prog, obj) { |
| 704 | struct bpf_prog_priv *priv = bpf_program__priv(prog); | ||
| 705 | struct probe_trace_event *tev; | 705 | struct probe_trace_event *tev; |
| 706 | struct perf_probe_event *pev; | 706 | struct perf_probe_event *pev; |
| 707 | struct bpf_prog_priv *priv; | ||
| 708 | int i, fd; | 707 | int i, fd; |
| 709 | 708 | ||
| 710 | err = bpf_program__get_private(prog, | 709 | if (IS_ERR(priv) || !priv) { |
| 711 | (void **)&priv); | ||
| 712 | if (err || !priv) { | ||
| 713 | pr_debug("bpf: failed to get private field\n"); | 710 | pr_debug("bpf: failed to get private field\n"); |
| 714 | return -BPF_LOADER_ERRNO__INTERNAL; | 711 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 715 | } | 712 | } |
| @@ -897,15 +894,12 @@ bpf_map_priv__clone(struct bpf_map_priv *priv) | |||
| 897 | static int | 894 | static int |
| 898 | bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) | 895 | bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) |
| 899 | { | 896 | { |
| 900 | struct bpf_map_priv *priv; | 897 | const char *map_name = bpf_map__name(map); |
| 901 | const char *map_name; | 898 | struct bpf_map_priv *priv = bpf_map__priv(map); |
| 902 | int err; | ||
| 903 | 899 | ||
| 904 | map_name = bpf_map__get_name(map); | 900 | if (IS_ERR(priv)) { |
| 905 | err = bpf_map__get_private(map, (void **)&priv); | ||
| 906 | if (err) { | ||
| 907 | pr_debug("Failed to get private from map %s\n", map_name); | 901 | pr_debug("Failed to get private from map %s\n", map_name); |
| 908 | return err; | 902 | return PTR_ERR(priv); |
| 909 | } | 903 | } |
| 910 | 904 | ||
| 911 | if (!priv) { | 905 | if (!priv) { |
| @@ -916,7 +910,7 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) | |||
| 916 | } | 910 | } |
| 917 | INIT_LIST_HEAD(&priv->ops_list); | 911 | INIT_LIST_HEAD(&priv->ops_list); |
| 918 | 912 | ||
| 919 | if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) { | 913 | if (bpf_map__set_priv(map, priv, bpf_map_priv__clear)) { |
| 920 | free(priv); | 914 | free(priv); |
| 921 | return -BPF_LOADER_ERRNO__INTERNAL; | 915 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 922 | } | 916 | } |
| @@ -948,30 +942,26 @@ static int | |||
| 948 | __bpf_map__config_value(struct bpf_map *map, | 942 | __bpf_map__config_value(struct bpf_map *map, |
| 949 | struct parse_events_term *term) | 943 | struct parse_events_term *term) |
| 950 | { | 944 | { |
| 951 | struct bpf_map_def def; | ||
| 952 | struct bpf_map_op *op; | 945 | struct bpf_map_op *op; |
| 953 | const char *map_name; | 946 | const char *map_name = bpf_map__name(map); |
| 954 | int err; | 947 | const struct bpf_map_def *def = bpf_map__def(map); |
| 955 | 948 | ||
| 956 | map_name = bpf_map__get_name(map); | 949 | if (IS_ERR(def)) { |
| 957 | |||
| 958 | err = bpf_map__get_def(map, &def); | ||
| 959 | if (err) { | ||
| 960 | pr_debug("Unable to get map definition from '%s'\n", | 950 | pr_debug("Unable to get map definition from '%s'\n", |
| 961 | map_name); | 951 | map_name); |
| 962 | return -BPF_LOADER_ERRNO__INTERNAL; | 952 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 963 | } | 953 | } |
| 964 | 954 | ||
| 965 | if (def.type != BPF_MAP_TYPE_ARRAY) { | 955 | if (def->type != BPF_MAP_TYPE_ARRAY) { |
| 966 | pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n", | 956 | pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n", |
| 967 | map_name); | 957 | map_name); |
| 968 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; | 958 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; |
| 969 | } | 959 | } |
| 970 | if (def.key_size < sizeof(unsigned int)) { | 960 | if (def->key_size < sizeof(unsigned int)) { |
| 971 | pr_debug("Map %s has incorrect key size\n", map_name); | 961 | pr_debug("Map %s has incorrect key size\n", map_name); |
| 972 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE; | 962 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE; |
| 973 | } | 963 | } |
| 974 | switch (def.value_size) { | 964 | switch (def->value_size) { |
| 975 | case 1: | 965 | case 1: |
| 976 | case 2: | 966 | case 2: |
| 977 | case 4: | 967 | case 4: |
| @@ -1014,12 +1004,10 @@ __bpf_map__config_event(struct bpf_map *map, | |||
| 1014 | struct perf_evlist *evlist) | 1004 | struct perf_evlist *evlist) |
| 1015 | { | 1005 | { |
| 1016 | struct perf_evsel *evsel; | 1006 | struct perf_evsel *evsel; |
| 1017 | struct bpf_map_def def; | 1007 | const struct bpf_map_def *def; |
| 1018 | struct bpf_map_op *op; | 1008 | struct bpf_map_op *op; |
| 1019 | const char *map_name; | 1009 | const char *map_name = bpf_map__name(map); |
| 1020 | int err; | ||
| 1021 | 1010 | ||
| 1022 | map_name = bpf_map__get_name(map); | ||
| 1023 | evsel = perf_evlist__find_evsel_by_str(evlist, term->val.str); | 1011 | evsel = perf_evlist__find_evsel_by_str(evlist, term->val.str); |
| 1024 | if (!evsel) { | 1012 | if (!evsel) { |
| 1025 | pr_debug("Event (for '%s') '%s' doesn't exist\n", | 1013 | pr_debug("Event (for '%s') '%s' doesn't exist\n", |
| @@ -1027,18 +1015,18 @@ __bpf_map__config_event(struct bpf_map *map, | |||
| 1027 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT; | 1015 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT; |
| 1028 | } | 1016 | } |
| 1029 | 1017 | ||
| 1030 | err = bpf_map__get_def(map, &def); | 1018 | def = bpf_map__def(map); |
| 1031 | if (err) { | 1019 | if (IS_ERR(def)) { |
| 1032 | pr_debug("Unable to get map definition from '%s'\n", | 1020 | pr_debug("Unable to get map definition from '%s'\n", |
| 1033 | map_name); | 1021 | map_name); |
| 1034 | return err; | 1022 | return PTR_ERR(def); |
| 1035 | } | 1023 | } |
| 1036 | 1024 | ||
| 1037 | /* | 1025 | /* |
| 1038 | * No need to check key_size and value_size: | 1026 | * No need to check key_size and value_size: |
| 1039 | * kernel has already checked them. | 1027 | * kernel has already checked them. |
| 1040 | */ | 1028 | */ |
| 1041 | if (def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { | 1029 | if (def->type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { |
| 1042 | pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", | 1030 | pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", |
| 1043 | map_name); | 1031 | map_name); |
| 1044 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; | 1032 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; |
| @@ -1087,9 +1075,8 @@ config_map_indices_range_check(struct parse_events_term *term, | |||
| 1087 | const char *map_name) | 1075 | const char *map_name) |
| 1088 | { | 1076 | { |
| 1089 | struct parse_events_array *array = &term->array; | 1077 | struct parse_events_array *array = &term->array; |
| 1090 | struct bpf_map_def def; | 1078 | const struct bpf_map_def *def; |
| 1091 | unsigned int i; | 1079 | unsigned int i; |
| 1092 | int err; | ||
| 1093 | 1080 | ||
| 1094 | if (!array->nr_ranges) | 1081 | if (!array->nr_ranges) |
| 1095 | return 0; | 1082 | return 0; |
| @@ -1099,8 +1086,8 @@ config_map_indices_range_check(struct parse_events_term *term, | |||
| 1099 | return -BPF_LOADER_ERRNO__INTERNAL; | 1086 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 1100 | } | 1087 | } |
| 1101 | 1088 | ||
| 1102 | err = bpf_map__get_def(map, &def); | 1089 | def = bpf_map__def(map); |
| 1103 | if (err) { | 1090 | if (IS_ERR(def)) { |
| 1104 | pr_debug("ERROR: Unable to get map definition from '%s'\n", | 1091 | pr_debug("ERROR: Unable to get map definition from '%s'\n", |
| 1105 | map_name); | 1092 | map_name); |
| 1106 | return -BPF_LOADER_ERRNO__INTERNAL; | 1093 | return -BPF_LOADER_ERRNO__INTERNAL; |
| @@ -1111,7 +1098,7 @@ config_map_indices_range_check(struct parse_events_term *term, | |||
| 1111 | size_t length = array->ranges[i].length; | 1098 | size_t length = array->ranges[i].length; |
| 1112 | unsigned int idx = start + length - 1; | 1099 | unsigned int idx = start + length - 1; |
| 1113 | 1100 | ||
| 1114 | if (idx >= def.max_entries) { | 1101 | if (idx >= def->max_entries) { |
| 1115 | pr_debug("ERROR: index %d too large\n", idx); | 1102 | pr_debug("ERROR: index %d too large\n", idx); |
| 1116 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG; | 1103 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG; |
| 1117 | } | 1104 | } |
| @@ -1147,7 +1134,7 @@ bpf__obj_config_map(struct bpf_object *obj, | |||
| 1147 | goto out; | 1134 | goto out; |
| 1148 | } | 1135 | } |
| 1149 | 1136 | ||
| 1150 | map = bpf_object__get_map_by_name(obj, map_name); | 1137 | map = bpf_object__find_map_by_name(obj, map_name); |
| 1151 | if (!map) { | 1138 | if (!map) { |
| 1152 | pr_debug("ERROR: Map %s doesn't exist\n", map_name); | 1139 | pr_debug("ERROR: Map %s doesn't exist\n", map_name); |
| 1153 | err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST; | 1140 | err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST; |
| @@ -1204,14 +1191,14 @@ out: | |||
| 1204 | } | 1191 | } |
| 1205 | 1192 | ||
| 1206 | typedef int (*map_config_func_t)(const char *name, int map_fd, | 1193 | typedef int (*map_config_func_t)(const char *name, int map_fd, |
| 1207 | struct bpf_map_def *pdef, | 1194 | const struct bpf_map_def *pdef, |
| 1208 | struct bpf_map_op *op, | 1195 | struct bpf_map_op *op, |
| 1209 | void *pkey, void *arg); | 1196 | void *pkey, void *arg); |
| 1210 | 1197 | ||
| 1211 | static int | 1198 | static int |
| 1212 | foreach_key_array_all(map_config_func_t func, | 1199 | foreach_key_array_all(map_config_func_t func, |
| 1213 | void *arg, const char *name, | 1200 | void *arg, const char *name, |
| 1214 | int map_fd, struct bpf_map_def *pdef, | 1201 | int map_fd, const struct bpf_map_def *pdef, |
| 1215 | struct bpf_map_op *op) | 1202 | struct bpf_map_op *op) |
| 1216 | { | 1203 | { |
| 1217 | unsigned int i; | 1204 | unsigned int i; |
| @@ -1231,7 +1218,7 @@ foreach_key_array_all(map_config_func_t func, | |||
| 1231 | static int | 1218 | static int |
| 1232 | foreach_key_array_ranges(map_config_func_t func, void *arg, | 1219 | foreach_key_array_ranges(map_config_func_t func, void *arg, |
| 1233 | const char *name, int map_fd, | 1220 | const char *name, int map_fd, |
| 1234 | struct bpf_map_def *pdef, | 1221 | const struct bpf_map_def *pdef, |
| 1235 | struct bpf_map_op *op) | 1222 | struct bpf_map_op *op) |
| 1236 | { | 1223 | { |
| 1237 | unsigned int i, j; | 1224 | unsigned int i, j; |
| @@ -1261,15 +1248,12 @@ bpf_map_config_foreach_key(struct bpf_map *map, | |||
| 1261 | void *arg) | 1248 | void *arg) |
| 1262 | { | 1249 | { |
| 1263 | int err, map_fd; | 1250 | int err, map_fd; |
| 1264 | const char *name; | ||
| 1265 | struct bpf_map_op *op; | 1251 | struct bpf_map_op *op; |
| 1266 | struct bpf_map_def def; | 1252 | const struct bpf_map_def *def; |
| 1267 | struct bpf_map_priv *priv; | 1253 | const char *name = bpf_map__name(map); |
| 1254 | struct bpf_map_priv *priv = bpf_map__priv(map); | ||
| 1268 | 1255 | ||
| 1269 | name = bpf_map__get_name(map); | 1256 | if (IS_ERR(priv)) { |
| 1270 | |||
| 1271 | err = bpf_map__get_private(map, (void **)&priv); | ||
| 1272 | if (err) { | ||
| 1273 | pr_debug("ERROR: failed to get private from map %s\n", name); | 1257 | pr_debug("ERROR: failed to get private from map %s\n", name); |
| 1274 | return -BPF_LOADER_ERRNO__INTERNAL; | 1258 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 1275 | } | 1259 | } |
| @@ -1278,29 +1262,29 @@ bpf_map_config_foreach_key(struct bpf_map *map, | |||
| 1278 | return 0; | 1262 | return 0; |
| 1279 | } | 1263 | } |
| 1280 | 1264 | ||
| 1281 | err = bpf_map__get_def(map, &def); | 1265 | def = bpf_map__def(map); |
| 1282 | if (err) { | 1266 | if (IS_ERR(def)) { |
| 1283 | pr_debug("ERROR: failed to get definition from map %s\n", name); | 1267 | pr_debug("ERROR: failed to get definition from map %s\n", name); |
| 1284 | return -BPF_LOADER_ERRNO__INTERNAL; | 1268 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 1285 | } | 1269 | } |
| 1286 | map_fd = bpf_map__get_fd(map); | 1270 | map_fd = bpf_map__fd(map); |
| 1287 | if (map_fd < 0) { | 1271 | if (map_fd < 0) { |
| 1288 | pr_debug("ERROR: failed to get fd from map %s\n", name); | 1272 | pr_debug("ERROR: failed to get fd from map %s\n", name); |
| 1289 | return map_fd; | 1273 | return map_fd; |
| 1290 | } | 1274 | } |
| 1291 | 1275 | ||
| 1292 | list_for_each_entry(op, &priv->ops_list, list) { | 1276 | list_for_each_entry(op, &priv->ops_list, list) { |
| 1293 | switch (def.type) { | 1277 | switch (def->type) { |
| 1294 | case BPF_MAP_TYPE_ARRAY: | 1278 | case BPF_MAP_TYPE_ARRAY: |
| 1295 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: | 1279 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: |
| 1296 | switch (op->key_type) { | 1280 | switch (op->key_type) { |
| 1297 | case BPF_MAP_KEY_ALL: | 1281 | case BPF_MAP_KEY_ALL: |
| 1298 | err = foreach_key_array_all(func, arg, name, | 1282 | err = foreach_key_array_all(func, arg, name, |
| 1299 | map_fd, &def, op); | 1283 | map_fd, def, op); |
| 1300 | break; | 1284 | break; |
| 1301 | case BPF_MAP_KEY_RANGES: | 1285 | case BPF_MAP_KEY_RANGES: |
| 1302 | err = foreach_key_array_ranges(func, arg, name, | 1286 | err = foreach_key_array_ranges(func, arg, name, |
| 1303 | map_fd, &def, | 1287 | map_fd, def, |
| 1304 | op); | 1288 | op); |
| 1305 | break; | 1289 | break; |
| 1306 | default: | 1290 | default: |
| @@ -1410,7 +1394,7 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey, | |||
| 1410 | 1394 | ||
| 1411 | static int | 1395 | static int |
| 1412 | apply_obj_config_map_for_key(const char *name, int map_fd, | 1396 | apply_obj_config_map_for_key(const char *name, int map_fd, |
| 1413 | struct bpf_map_def *pdef __maybe_unused, | 1397 | const struct bpf_map_def *pdef, |
| 1414 | struct bpf_map_op *op, | 1398 | struct bpf_map_op *op, |
| 1415 | void *pkey, void *arg __maybe_unused) | 1399 | void *pkey, void *arg __maybe_unused) |
| 1416 | { | 1400 | { |
| @@ -1475,9 +1459,9 @@ int bpf__apply_obj_config(void) | |||
| 1475 | 1459 | ||
| 1476 | #define bpf__for_each_stdout_map(pos, obj, objtmp) \ | 1460 | #define bpf__for_each_stdout_map(pos, obj, objtmp) \ |
| 1477 | bpf__for_each_map(pos, obj, objtmp) \ | 1461 | bpf__for_each_map(pos, obj, objtmp) \ |
| 1478 | if (bpf_map__get_name(pos) && \ | 1462 | if (bpf_map__name(pos) && \ |
| 1479 | (strcmp("__bpf_stdout__", \ | 1463 | (strcmp("__bpf_stdout__", \ |
| 1480 | bpf_map__get_name(pos)) == 0)) | 1464 | bpf_map__name(pos)) == 0)) |
| 1481 | 1465 | ||
| 1482 | int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) | 1466 | int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) |
| 1483 | { | 1467 | { |
| @@ -1489,10 +1473,9 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) | |||
| 1489 | bool need_init = false; | 1473 | bool need_init = false; |
| 1490 | 1474 | ||
| 1491 | bpf__for_each_stdout_map(map, obj, tmp) { | 1475 | bpf__for_each_stdout_map(map, obj, tmp) { |
| 1492 | struct bpf_map_priv *priv; | 1476 | struct bpf_map_priv *priv = bpf_map__priv(map); |
| 1493 | 1477 | ||
| 1494 | err = bpf_map__get_private(map, (void **)&priv); | 1478 | if (IS_ERR(priv)) |
| 1495 | if (err) | ||
| 1496 | return -BPF_LOADER_ERRNO__INTERNAL; | 1479 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 1497 | 1480 | ||
| 1498 | /* | 1481 | /* |
| @@ -1520,10 +1503,9 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) | |||
| 1520 | } | 1503 | } |
| 1521 | 1504 | ||
| 1522 | bpf__for_each_stdout_map(map, obj, tmp) { | 1505 | bpf__for_each_stdout_map(map, obj, tmp) { |
| 1523 | struct bpf_map_priv *priv; | 1506 | struct bpf_map_priv *priv = bpf_map__priv(map); |
| 1524 | 1507 | ||
| 1525 | err = bpf_map__get_private(map, (void **)&priv); | 1508 | if (IS_ERR(priv)) |
| 1526 | if (err) | ||
| 1527 | return -BPF_LOADER_ERRNO__INTERNAL; | 1509 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 1528 | if (priv) | 1510 | if (priv) |
| 1529 | continue; | 1511 | continue; |
| @@ -1533,7 +1515,7 @@ int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused) | |||
| 1533 | if (!priv) | 1515 | if (!priv) |
| 1534 | return -ENOMEM; | 1516 | return -ENOMEM; |
| 1535 | 1517 | ||
| 1536 | err = bpf_map__set_private(map, priv, bpf_map_priv__clear); | 1518 | err = bpf_map__set_priv(map, priv, bpf_map_priv__clear); |
| 1537 | if (err) { | 1519 | if (err) { |
| 1538 | bpf_map_priv__clear(map, priv); | 1520 | bpf_map_priv__clear(map, priv); |
| 1539 | return err; | 1521 | return err; |
| @@ -1677,7 +1659,7 @@ int bpf__strerror_load(struct bpf_object *obj, | |||
| 1677 | { | 1659 | { |
| 1678 | bpf__strerror_head(err, buf, size); | 1660 | bpf__strerror_head(err, buf, size); |
| 1679 | case LIBBPF_ERRNO__KVER: { | 1661 | case LIBBPF_ERRNO__KVER: { |
| 1680 | unsigned int obj_kver = bpf_object__get_kversion(obj); | 1662 | unsigned int obj_kver = bpf_object__kversion(obj); |
| 1681 | unsigned int real_kver; | 1663 | unsigned int real_kver; |
| 1682 | 1664 | ||
| 1683 | if (fetch_kernel_version(&real_kver, NULL, 0)) { | 1665 | if (fetch_kernel_version(&real_kver, NULL, 0)) { |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 67f986c8c378..20aef90bf194 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
| @@ -147,20 +147,17 @@ static int asnprintf(char **strp, size_t size, const char *fmt, ...) | |||
| 147 | char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, | 147 | char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, |
| 148 | size_t size) | 148 | size_t size) |
| 149 | { | 149 | { |
| 150 | bool is_alloc = !!bf; | ||
| 151 | bool retry_old = true; | 150 | bool retry_old = true; |
| 152 | 151 | ||
| 153 | asnprintf(&bf, size, "%s/%s/%s/kallsyms", | 152 | snprintf(bf, size, "%s/%s/%s/kallsyms", |
| 154 | buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); | 153 | buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); |
| 155 | retry: | 154 | retry: |
| 156 | if (!access(bf, F_OK)) | 155 | if (!access(bf, F_OK)) |
| 157 | return bf; | 156 | return bf; |
| 158 | if (is_alloc) | ||
| 159 | free(bf); | ||
| 160 | if (retry_old) { | 157 | if (retry_old) { |
| 161 | /* Try old style kallsyms cache */ | 158 | /* Try old style kallsyms cache */ |
| 162 | asnprintf(&bf, size, "%s/%s/%s", | 159 | snprintf(bf, size, "%s/%s/%s", |
| 163 | buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); | 160 | buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); |
| 164 | retry_old = false; | 161 | retry_old = false; |
| 165 | goto retry; | 162 | goto retry; |
| 166 | } | 163 | } |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index c73f1c4d1ca9..8749eca3055f 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
| @@ -643,17 +643,64 @@ static int collect_config(const char *var, const char *value, | |||
| 643 | 643 | ||
| 644 | out_free: | 644 | out_free: |
| 645 | free(key); | 645 | free(key); |
| 646 | perf_config_set__delete(set); | ||
| 647 | return -1; | 646 | return -1; |
| 648 | } | 647 | } |
| 649 | 648 | ||
| 649 | static int perf_config_set__init(struct perf_config_set *set) | ||
| 650 | { | ||
| 651 | int ret = -1; | ||
| 652 | const char *home = NULL; | ||
| 653 | |||
| 654 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ | ||
| 655 | if (config_exclusive_filename) | ||
| 656 | return perf_config_from_file(collect_config, config_exclusive_filename, set); | ||
| 657 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { | ||
| 658 | if (perf_config_from_file(collect_config, perf_etc_perfconfig(), set) < 0) | ||
| 659 | goto out; | ||
| 660 | } | ||
| 661 | |||
| 662 | home = getenv("HOME"); | ||
| 663 | if (perf_config_global() && home) { | ||
| 664 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); | ||
| 665 | struct stat st; | ||
| 666 | |||
| 667 | if (user_config == NULL) { | ||
| 668 | warning("Not enough memory to process %s/.perfconfig, " | ||
| 669 | "ignoring it.", home); | ||
| 670 | goto out; | ||
| 671 | } | ||
| 672 | |||
| 673 | if (stat(user_config, &st) < 0) | ||
| 674 | goto out_free; | ||
| 675 | |||
| 676 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
| 677 | warning("File %s not owned by current user or root, " | ||
| 678 | "ignoring it.", user_config); | ||
| 679 | goto out_free; | ||
| 680 | } | ||
| 681 | |||
| 682 | if (!st.st_size) | ||
| 683 | goto out_free; | ||
| 684 | |||
| 685 | ret = perf_config_from_file(collect_config, user_config, set); | ||
| 686 | |||
| 687 | out_free: | ||
| 688 | free(user_config); | ||
| 689 | } | ||
| 690 | out: | ||
| 691 | return ret; | ||
| 692 | } | ||
| 693 | |||
| 650 | struct perf_config_set *perf_config_set__new(void) | 694 | struct perf_config_set *perf_config_set__new(void) |
| 651 | { | 695 | { |
| 652 | struct perf_config_set *set = zalloc(sizeof(*set)); | 696 | struct perf_config_set *set = zalloc(sizeof(*set)); |
| 653 | 697 | ||
| 654 | if (set) { | 698 | if (set) { |
| 655 | INIT_LIST_HEAD(&set->sections); | 699 | INIT_LIST_HEAD(&set->sections); |
| 656 | perf_config(collect_config, set); | 700 | if (perf_config_set__init(set) < 0) { |
| 701 | perf_config_set__delete(set); | ||
| 702 | set = NULL; | ||
| 703 | } | ||
| 657 | } | 704 | } |
| 658 | 705 | ||
| 659 | return set; | 706 | return set; |
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c new file mode 100644 index 000000000000..4fb5395669f8 --- /dev/null +++ b/tools/perf/util/libunwind/arm64.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * This file setups defines to compile arch specific binary from the | ||
| 3 | * generic one. | ||
| 4 | * | ||
| 5 | * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch | ||
| 6 | * name and the defination of this function is included directly from | ||
| 7 | * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function | ||
| 8 | * is defined no matter what arch the host is. | ||
| 9 | * | ||
| 10 | * Finally, the arch specific unwind methods are exported which will | ||
| 11 | * be assigned to each arm64 thread. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #define REMOTE_UNWIND_LIBUNWIND | ||
| 15 | |||
| 16 | #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum) | ||
| 17 | |||
| 18 | #include "unwind.h" | ||
| 19 | #include "debug.h" | ||
| 20 | #include "libunwind-aarch64.h" | ||
| 21 | #include <../../../../arch/arm64/include/uapi/asm/perf_regs.h> | ||
| 22 | #include "../../arch/arm64/util/unwind-libunwind.c" | ||
| 23 | |||
| 24 | /* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind, | ||
| 25 | * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64 | ||
| 26 | * unwind methods. | ||
| 27 | */ | ||
| 28 | #undef NO_LIBUNWIND_DEBUG_FRAME | ||
| 29 | #ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64 | ||
| 30 | #define NO_LIBUNWIND_DEBUG_FRAME | ||
| 31 | #endif | ||
| 32 | #include "util/unwind-libunwind-local.c" | ||
| 33 | |||
| 34 | struct unwind_libunwind_ops * | ||
| 35 | 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 index 000000000000..d98c17e19a2b --- /dev/null +++ b/tools/perf/util/libunwind/x86_32.c | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * This file setups defines to compile arch specific binary from the | ||
| 3 | * generic one. | ||
| 4 | * | ||
| 5 | * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch | ||
| 6 | * name and the defination of this function is included directly from | ||
| 7 | * 'arch/x86/util/unwind-libunwind.c', to make sure that this function | ||
| 8 | * is defined no matter what arch the host is. | ||
| 9 | * | ||
| 10 | * Finally, the arch specific unwind methods are exported which will | ||
| 11 | * be assigned to each x86 thread. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #define REMOTE_UNWIND_LIBUNWIND | ||
| 15 | #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum) | ||
| 16 | |||
| 17 | #include "unwind.h" | ||
| 18 | #include "debug.h" | ||
| 19 | #include "libunwind-x86.h" | ||
| 20 | #include <../../../../arch/x86/include/uapi/asm/perf_regs.h> | ||
| 21 | |||
| 22 | /* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c' | ||
| 23 | * for x86_32, we undef it to compile code for x86_32 only. | ||
| 24 | */ | ||
| 25 | #undef HAVE_ARCH_X86_64_SUPPORT | ||
| 26 | #include "../../arch/x86/util/unwind-libunwind.c" | ||
| 27 | |||
| 28 | /* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no | ||
| 29 | * dwarf_find_debug_frame() function. | ||
| 30 | */ | ||
| 31 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 32 | #define NO_LIBUNWIND_DEBUG_FRAME | ||
| 33 | #endif | ||
| 34 | #include "util/unwind-libunwind-local.c" | ||
| 35 | |||
| 36 | struct unwind_libunwind_ops * | ||
| 37 | x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops; | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b1772180c820..a0c186acb1f3 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -1353,11 +1353,16 @@ int machine__process_mmap2_event(struct machine *machine, | |||
| 1353 | if (map == NULL) | 1353 | if (map == NULL) |
| 1354 | goto out_problem_map; | 1354 | goto out_problem_map; |
| 1355 | 1355 | ||
| 1356 | thread__insert_map(thread, map); | 1356 | ret = thread__insert_map(thread, map); |
| 1357 | if (ret) | ||
| 1358 | goto out_problem_insert; | ||
| 1359 | |||
| 1357 | thread__put(thread); | 1360 | thread__put(thread); |
| 1358 | map__put(map); | 1361 | map__put(map); |
| 1359 | return 0; | 1362 | return 0; |
| 1360 | 1363 | ||
| 1364 | out_problem_insert: | ||
| 1365 | map__put(map); | ||
| 1361 | out_problem_map: | 1366 | out_problem_map: |
| 1362 | thread__put(thread); | 1367 | thread__put(thread); |
| 1363 | out_problem: | 1368 | out_problem: |
| @@ -1403,11 +1408,16 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
| 1403 | if (map == NULL) | 1408 | if (map == NULL) |
| 1404 | goto out_problem_map; | 1409 | goto out_problem_map; |
| 1405 | 1410 | ||
| 1406 | thread__insert_map(thread, map); | 1411 | ret = thread__insert_map(thread, map); |
| 1412 | if (ret) | ||
| 1413 | goto out_problem_insert; | ||
| 1414 | |||
| 1407 | thread__put(thread); | 1415 | thread__put(thread); |
| 1408 | map__put(map); | 1416 | map__put(map); |
| 1409 | return 0; | 1417 | return 0; |
| 1410 | 1418 | ||
| 1419 | out_problem_insert: | ||
| 1420 | map__put(map); | ||
| 1411 | out_problem_map: | 1421 | out_problem_map: |
| 1412 | thread__put(thread); | 1422 | thread__put(thread); |
| 1413 | out_problem: | 1423 | out_problem: |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index ada58e6070bf..f30f9566fddc 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) | |||
| 43 | thread->cpu = -1; | 43 | thread->cpu = -1; |
| 44 | INIT_LIST_HEAD(&thread->comm_list); | 44 | INIT_LIST_HEAD(&thread->comm_list); |
| 45 | 45 | ||
| 46 | if (unwind__prepare_access(thread) < 0) | ||
| 47 | goto err_thread; | ||
| 48 | |||
| 49 | comm_str = malloc(32); | 46 | comm_str = malloc(32); |
| 50 | if (!comm_str) | 47 | if (!comm_str) |
| 51 | goto err_thread; | 48 | goto err_thread; |
| @@ -201,10 +198,18 @@ size_t thread__fprintf(struct thread *thread, FILE *fp) | |||
| 201 | map_groups__fprintf(thread->mg, fp); | 198 | map_groups__fprintf(thread->mg, fp); |
| 202 | } | 199 | } |
| 203 | 200 | ||
| 204 | void thread__insert_map(struct thread *thread, struct map *map) | 201 | int thread__insert_map(struct thread *thread, struct map *map) |
| 205 | { | 202 | { |
| 203 | int ret; | ||
| 204 | |||
| 205 | ret = unwind__prepare_access(thread, map); | ||
| 206 | if (ret) | ||
| 207 | return ret; | ||
| 208 | |||
| 206 | map_groups__fixup_overlappings(thread->mg, map, stderr); | 209 | map_groups__fixup_overlappings(thread->mg, map, stderr); |
| 207 | map_groups__insert(thread->mg, map); | 210 | map_groups__insert(thread->mg, map); |
| 211 | |||
| 212 | return 0; | ||
| 208 | } | 213 | } |
| 209 | 214 | ||
| 210 | static int thread__clone_map_groups(struct thread *thread, | 215 | static int thread__clone_map_groups(struct thread *thread, |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 08fcb14cf637..99263cb6e6b6 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -9,11 +9,9 @@ | |||
| 9 | #include "symbol.h" | 9 | #include "symbol.h" |
| 10 | #include <strlist.h> | 10 | #include <strlist.h> |
| 11 | #include <intlist.h> | 11 | #include <intlist.h> |
| 12 | #ifdef HAVE_LIBUNWIND_SUPPORT | ||
| 13 | #include <libunwind.h> | ||
| 14 | #endif | ||
| 15 | 12 | ||
| 16 | struct thread_stack; | 13 | struct thread_stack; |
| 14 | struct unwind_libunwind_ops; | ||
| 17 | 15 | ||
| 18 | struct thread { | 16 | struct thread { |
| 19 | union { | 17 | union { |
| @@ -36,7 +34,8 @@ struct thread { | |||
| 36 | void *priv; | 34 | void *priv; |
| 37 | struct thread_stack *ts; | 35 | struct thread_stack *ts; |
| 38 | #ifdef HAVE_LIBUNWIND_SUPPORT | 36 | #ifdef HAVE_LIBUNWIND_SUPPORT |
| 39 | unw_addr_space_t addr_space; | 37 | void *addr_space; |
| 38 | struct unwind_libunwind_ops *unwind_libunwind_ops; | ||
| 40 | #endif | 39 | #endif |
| 41 | }; | 40 | }; |
| 42 | 41 | ||
| @@ -77,7 +76,7 @@ int thread__comm_len(struct thread *thread); | |||
| 77 | struct comm *thread__comm(const struct thread *thread); | 76 | struct comm *thread__comm(const struct thread *thread); |
| 78 | struct comm *thread__exec_comm(const struct thread *thread); | 77 | struct comm *thread__exec_comm(const struct thread *thread); |
| 79 | const char *thread__comm_str(const struct thread *thread); | 78 | const char *thread__comm_str(const struct thread *thread); |
| 80 | void thread__insert_map(struct thread *thread, struct map *map); | 79 | int thread__insert_map(struct thread *thread, struct map *map); |
| 81 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 80 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); |
| 82 | size_t thread__fprintf(struct thread *thread, FILE *fp); | 81 | size_t thread__fprintf(struct thread *thread, FILE *fp); |
| 83 | 82 | ||
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c new file mode 100644 index 000000000000..01c2e86977f4 --- /dev/null +++ b/tools/perf/util/unwind-libunwind-local.c | |||
| @@ -0,0 +1,697 @@ | |||
| 1 | /* | ||
| 2 | * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. | ||
| 3 | * | ||
| 4 | * Lots of this code have been borrowed or heavily inspired from parts of | ||
| 5 | * the libunwind 0.99 code which are (amongst other contributors I may have | ||
| 6 | * forgotten): | ||
| 7 | * | ||
| 8 | * Copyright (C) 2002-2007 Hewlett-Packard Co | ||
| 9 | * Contributed by David Mosberger-Tang <davidm@hpl.hp.com> | ||
| 10 | * | ||
| 11 | * And the bugs have been added by: | ||
| 12 | * | ||
| 13 | * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com> | ||
| 14 | * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com> | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <elf.h> | ||
| 19 | #include <gelf.h> | ||
| 20 | #include <fcntl.h> | ||
| 21 | #include <string.h> | ||
| 22 | #include <unistd.h> | ||
| 23 | #include <sys/mman.h> | ||
| 24 | #include <linux/list.h> | ||
| 25 | #ifndef REMOTE_UNWIND_LIBUNWIND | ||
| 26 | #include <libunwind.h> | ||
| 27 | #include <libunwind-ptrace.h> | ||
| 28 | #endif | ||
| 29 | #include "callchain.h" | ||
| 30 | #include "thread.h" | ||
| 31 | #include "session.h" | ||
| 32 | #include "perf_regs.h" | ||
| 33 | #include "unwind.h" | ||
| 34 | #include "symbol.h" | ||
| 35 | #include "util.h" | ||
| 36 | #include "debug.h" | ||
| 37 | #include "asm/bug.h" | ||
| 38 | |||
| 39 | extern int | ||
| 40 | UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | ||
| 41 | unw_word_t ip, | ||
| 42 | unw_dyn_info_t *di, | ||
| 43 | unw_proc_info_t *pi, | ||
| 44 | int need_unwind_info, void *arg); | ||
| 45 | |||
| 46 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | ||
| 47 | |||
| 48 | extern int | ||
| 49 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
| 50 | unw_word_t ip, | ||
| 51 | unw_word_t segbase, | ||
| 52 | const char *obj_name, unw_word_t start, | ||
| 53 | unw_word_t end); | ||
| 54 | |||
| 55 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
| 56 | |||
| 57 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | ||
| 58 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ | ||
| 59 | |||
| 60 | /* Pointer-encoding formats: */ | ||
| 61 | #define DW_EH_PE_omit 0xff | ||
| 62 | #define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ | ||
| 63 | #define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ | ||
| 64 | #define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ | ||
| 65 | #define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ | ||
| 66 | #define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ | ||
| 67 | |||
| 68 | /* Pointer-encoding application: */ | ||
| 69 | #define DW_EH_PE_absptr 0x00 /* absolute value */ | ||
| 70 | #define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ | ||
| 71 | |||
| 72 | /* | ||
| 73 | * The following are not documented by LSB v1.3, yet they are used by | ||
| 74 | * GCC, presumably they aren't documented by LSB since they aren't | ||
| 75 | * used on Linux: | ||
| 76 | */ | ||
| 77 | #define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ | ||
| 78 | #define DW_EH_PE_aligned 0x50 /* aligned pointer */ | ||
| 79 | |||
| 80 | /* Flags intentionaly not handled, since they're not needed: | ||
| 81 | * #define DW_EH_PE_indirect 0x80 | ||
| 82 | * #define DW_EH_PE_uleb128 0x01 | ||
| 83 | * #define DW_EH_PE_udata2 0x02 | ||
| 84 | * #define DW_EH_PE_sleb128 0x09 | ||
| 85 | * #define DW_EH_PE_sdata2 0x0a | ||
| 86 | * #define DW_EH_PE_textrel 0x20 | ||
| 87 | * #define DW_EH_PE_datarel 0x30 | ||
| 88 | */ | ||
| 89 | |||
| 90 | struct unwind_info { | ||
| 91 | struct perf_sample *sample; | ||
| 92 | struct machine *machine; | ||
| 93 | struct thread *thread; | ||
| 94 | }; | ||
| 95 | |||
| 96 | #define dw_read(ptr, type, end) ({ \ | ||
| 97 | type *__p = (type *) ptr; \ | ||
| 98 | type __v; \ | ||
| 99 | if ((__p + 1) > (type *) end) \ | ||
| 100 | return -EINVAL; \ | ||
| 101 | __v = *__p++; \ | ||
| 102 | ptr = (typeof(ptr)) __p; \ | ||
| 103 | __v; \ | ||
| 104 | }) | ||
| 105 | |||
| 106 | static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, | ||
| 107 | u8 encoding) | ||
| 108 | { | ||
| 109 | u8 *cur = *p; | ||
| 110 | *val = 0; | ||
| 111 | |||
| 112 | switch (encoding) { | ||
| 113 | case DW_EH_PE_omit: | ||
| 114 | *val = 0; | ||
| 115 | goto out; | ||
| 116 | case DW_EH_PE_ptr: | ||
| 117 | *val = dw_read(cur, unsigned long, end); | ||
| 118 | goto out; | ||
| 119 | default: | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | |||
| 123 | switch (encoding & DW_EH_PE_APPL_MASK) { | ||
| 124 | case DW_EH_PE_absptr: | ||
| 125 | break; | ||
| 126 | case DW_EH_PE_pcrel: | ||
| 127 | *val = (unsigned long) cur; | ||
| 128 | break; | ||
| 129 | default: | ||
| 130 | return -EINVAL; | ||
| 131 | } | ||
| 132 | |||
| 133 | if ((encoding & 0x07) == 0x00) | ||
| 134 | encoding |= DW_EH_PE_udata4; | ||
| 135 | |||
| 136 | switch (encoding & DW_EH_PE_FORMAT_MASK) { | ||
| 137 | case DW_EH_PE_sdata4: | ||
| 138 | *val += dw_read(cur, s32, end); | ||
| 139 | break; | ||
| 140 | case DW_EH_PE_udata4: | ||
| 141 | *val += dw_read(cur, u32, end); | ||
| 142 | break; | ||
| 143 | case DW_EH_PE_sdata8: | ||
| 144 | *val += dw_read(cur, s64, end); | ||
| 145 | break; | ||
| 146 | case DW_EH_PE_udata8: | ||
| 147 | *val += dw_read(cur, u64, end); | ||
| 148 | break; | ||
| 149 | default: | ||
| 150 | return -EINVAL; | ||
| 151 | } | ||
| 152 | |||
| 153 | out: | ||
| 154 | *p = cur; | ||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | #define dw_read_encoded_value(ptr, end, enc) ({ \ | ||
| 159 | u64 __v; \ | ||
| 160 | if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ | ||
| 161 | return -EINVAL; \ | ||
| 162 | } \ | ||
| 163 | __v; \ | ||
| 164 | }) | ||
| 165 | |||
| 166 | static u64 elf_section_offset(int fd, const char *name) | ||
| 167 | { | ||
| 168 | Elf *elf; | ||
| 169 | GElf_Ehdr ehdr; | ||
| 170 | GElf_Shdr shdr; | ||
| 171 | u64 offset = 0; | ||
| 172 | |||
| 173 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
| 174 | if (elf == NULL) | ||
| 175 | return 0; | ||
| 176 | |||
| 177 | do { | ||
| 178 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 179 | break; | ||
| 180 | |||
| 181 | if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) | ||
| 182 | break; | ||
| 183 | |||
| 184 | offset = shdr.sh_offset; | ||
| 185 | } while (0); | ||
| 186 | |||
| 187 | elf_end(elf); | ||
| 188 | return offset; | ||
| 189 | } | ||
| 190 | |||
| 191 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 192 | static int elf_is_exec(int fd, const char *name) | ||
| 193 | { | ||
| 194 | Elf *elf; | ||
| 195 | GElf_Ehdr ehdr; | ||
| 196 | int retval = 0; | ||
| 197 | |||
| 198 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
| 199 | if (elf == NULL) | ||
| 200 | return 0; | ||
| 201 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 202 | goto out; | ||
| 203 | |||
| 204 | retval = (ehdr.e_type == ET_EXEC); | ||
| 205 | |||
| 206 | out: | ||
| 207 | elf_end(elf); | ||
| 208 | pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval); | ||
| 209 | return retval; | ||
| 210 | } | ||
| 211 | #endif | ||
| 212 | |||
| 213 | struct table_entry { | ||
| 214 | u32 start_ip_offset; | ||
| 215 | u32 fde_offset; | ||
| 216 | }; | ||
| 217 | |||
| 218 | struct eh_frame_hdr { | ||
| 219 | unsigned char version; | ||
| 220 | unsigned char eh_frame_ptr_enc; | ||
| 221 | unsigned char fde_count_enc; | ||
| 222 | unsigned char table_enc; | ||
| 223 | |||
| 224 | /* | ||
| 225 | * The rest of the header is variable-length and consists of the | ||
| 226 | * following members: | ||
| 227 | * | ||
| 228 | * encoded_t eh_frame_ptr; | ||
| 229 | * encoded_t fde_count; | ||
| 230 | */ | ||
| 231 | |||
| 232 | /* A single encoded pointer should not be more than 8 bytes. */ | ||
| 233 | u64 enc[2]; | ||
| 234 | |||
| 235 | /* | ||
| 236 | * struct { | ||
| 237 | * encoded_t start_ip; | ||
| 238 | * encoded_t fde_addr; | ||
| 239 | * } binary_search_table[fde_count]; | ||
| 240 | */ | ||
| 241 | char data[0]; | ||
| 242 | } __packed; | ||
| 243 | |||
| 244 | static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | ||
| 245 | u64 offset, u64 *table_data, u64 *segbase, | ||
| 246 | u64 *fde_count) | ||
| 247 | { | ||
| 248 | struct eh_frame_hdr hdr; | ||
| 249 | u8 *enc = (u8 *) &hdr.enc; | ||
| 250 | u8 *end = (u8 *) &hdr.data; | ||
| 251 | ssize_t r; | ||
| 252 | |||
| 253 | r = dso__data_read_offset(dso, machine, offset, | ||
| 254 | (u8 *) &hdr, sizeof(hdr)); | ||
| 255 | if (r != sizeof(hdr)) | ||
| 256 | return -EINVAL; | ||
| 257 | |||
| 258 | /* We dont need eh_frame_ptr, just skip it. */ | ||
| 259 | dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); | ||
| 260 | |||
| 261 | *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); | ||
| 262 | *segbase = offset; | ||
| 263 | *table_data = (enc - (u8 *) &hdr) + offset; | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | ||
| 268 | u64 *table_data, u64 *segbase, | ||
| 269 | u64 *fde_count) | ||
| 270 | { | ||
| 271 | int ret = -EINVAL, fd; | ||
| 272 | u64 offset = dso->data.eh_frame_hdr_offset; | ||
| 273 | |||
| 274 | if (offset == 0) { | ||
| 275 | fd = dso__data_get_fd(dso, machine); | ||
| 276 | if (fd < 0) | ||
| 277 | return -EINVAL; | ||
| 278 | |||
| 279 | /* Check the .eh_frame section for unwinding info */ | ||
| 280 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | ||
| 281 | dso->data.eh_frame_hdr_offset = offset; | ||
| 282 | dso__data_put_fd(dso); | ||
| 283 | } | ||
| 284 | |||
| 285 | if (offset) | ||
| 286 | ret = unwind_spec_ehframe(dso, machine, offset, | ||
| 287 | table_data, segbase, | ||
| 288 | fde_count); | ||
| 289 | |||
| 290 | return ret; | ||
| 291 | } | ||
| 292 | |||
| 293 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 294 | static int read_unwind_spec_debug_frame(struct dso *dso, | ||
| 295 | struct machine *machine, u64 *offset) | ||
| 296 | { | ||
| 297 | int fd; | ||
| 298 | u64 ofs = dso->data.debug_frame_offset; | ||
| 299 | |||
| 300 | if (ofs == 0) { | ||
| 301 | fd = dso__data_get_fd(dso, machine); | ||
| 302 | if (fd < 0) | ||
| 303 | return -EINVAL; | ||
| 304 | |||
| 305 | /* Check the .debug_frame section for unwinding info */ | ||
| 306 | ofs = elf_section_offset(fd, ".debug_frame"); | ||
| 307 | dso->data.debug_frame_offset = ofs; | ||
| 308 | dso__data_put_fd(dso); | ||
| 309 | } | ||
| 310 | |||
| 311 | *offset = ofs; | ||
| 312 | if (*offset) | ||
| 313 | return 0; | ||
| 314 | |||
| 315 | return -EINVAL; | ||
| 316 | } | ||
| 317 | #endif | ||
| 318 | |||
| 319 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | ||
| 320 | { | ||
| 321 | struct addr_location al; | ||
| 322 | |||
| 323 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, | ||
| 324 | MAP__FUNCTION, ip, &al); | ||
| 325 | if (!al.map) { | ||
| 326 | /* | ||
| 327 | * We've seen cases (softice) where DWARF unwinder went | ||
| 328 | * through non executable mmaps, which we need to lookup | ||
| 329 | * in MAP__VARIABLE tree. | ||
| 330 | */ | ||
| 331 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, | ||
| 332 | MAP__VARIABLE, ip, &al); | ||
| 333 | } | ||
| 334 | return al.map; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int | ||
| 338 | find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | ||
| 339 | int need_unwind_info, void *arg) | ||
| 340 | { | ||
| 341 | struct unwind_info *ui = arg; | ||
| 342 | struct map *map; | ||
| 343 | unw_dyn_info_t di; | ||
| 344 | u64 table_data, segbase, fde_count; | ||
| 345 | int ret = -EINVAL; | ||
| 346 | |||
| 347 | map = find_map(ip, ui); | ||
| 348 | if (!map || !map->dso) | ||
| 349 | return -EINVAL; | ||
| 350 | |||
| 351 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | ||
| 352 | |||
| 353 | /* Check the .eh_frame section for unwinding info */ | ||
| 354 | if (!read_unwind_spec_eh_frame(map->dso, ui->machine, | ||
| 355 | &table_data, &segbase, &fde_count)) { | ||
| 356 | memset(&di, 0, sizeof(di)); | ||
| 357 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
| 358 | di.start_ip = map->start; | ||
| 359 | di.end_ip = map->end; | ||
| 360 | di.u.rti.segbase = map->start + segbase; | ||
| 361 | di.u.rti.table_data = map->start + table_data; | ||
| 362 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
| 363 | / sizeof(unw_word_t); | ||
| 364 | ret = dwarf_search_unwind_table(as, ip, &di, pi, | ||
| 365 | need_unwind_info, arg); | ||
| 366 | } | ||
| 367 | |||
| 368 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 369 | /* Check the .debug_frame section for unwinding info */ | ||
| 370 | if (ret < 0 && | ||
| 371 | !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { | ||
| 372 | int fd = dso__data_get_fd(map->dso, ui->machine); | ||
| 373 | int is_exec = elf_is_exec(fd, map->dso->name); | ||
| 374 | unw_word_t base = is_exec ? 0 : map->start; | ||
| 375 | const char *symfile; | ||
| 376 | |||
| 377 | if (fd >= 0) | ||
| 378 | dso__data_put_fd(map->dso); | ||
| 379 | |||
| 380 | symfile = map->dso->symsrc_filename ?: map->dso->name; | ||
| 381 | |||
| 382 | memset(&di, 0, sizeof(di)); | ||
| 383 | if (dwarf_find_debug_frame(0, &di, ip, base, symfile, | ||
| 384 | map->start, map->end)) | ||
| 385 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
| 386 | need_unwind_info, arg); | ||
| 387 | } | ||
| 388 | #endif | ||
| 389 | |||
| 390 | return ret; | ||
| 391 | } | ||
| 392 | |||
| 393 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | ||
| 394 | unw_regnum_t __maybe_unused num, | ||
| 395 | unw_fpreg_t __maybe_unused *val, | ||
| 396 | int __maybe_unused __write, | ||
| 397 | void __maybe_unused *arg) | ||
| 398 | { | ||
| 399 | pr_err("unwind: access_fpreg unsupported\n"); | ||
| 400 | return -UNW_EINVAL; | ||
| 401 | } | ||
| 402 | |||
| 403 | static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, | ||
| 404 | unw_word_t __maybe_unused *dil_addr, | ||
| 405 | void __maybe_unused *arg) | ||
| 406 | { | ||
| 407 | return -UNW_ENOINFO; | ||
| 408 | } | ||
| 409 | |||
| 410 | static int resume(unw_addr_space_t __maybe_unused as, | ||
| 411 | unw_cursor_t __maybe_unused *cu, | ||
| 412 | void __maybe_unused *arg) | ||
| 413 | { | ||
| 414 | pr_err("unwind: resume unsupported\n"); | ||
| 415 | return -UNW_EINVAL; | ||
| 416 | } | ||
| 417 | |||
| 418 | static int | ||
| 419 | get_proc_name(unw_addr_space_t __maybe_unused as, | ||
| 420 | unw_word_t __maybe_unused addr, | ||
| 421 | char __maybe_unused *bufp, size_t __maybe_unused buf_len, | ||
| 422 | unw_word_t __maybe_unused *offp, void __maybe_unused *arg) | ||
| 423 | { | ||
| 424 | pr_err("unwind: get_proc_name unsupported\n"); | ||
| 425 | return -UNW_EINVAL; | ||
| 426 | } | ||
| 427 | |||
| 428 | static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, | ||
| 429 | unw_word_t *data) | ||
| 430 | { | ||
| 431 | struct map *map; | ||
| 432 | ssize_t size; | ||
| 433 | |||
| 434 | map = find_map(addr, ui); | ||
| 435 | if (!map) { | ||
| 436 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | ||
| 437 | return -1; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (!map->dso) | ||
| 441 | return -1; | ||
| 442 | |||
| 443 | size = dso__data_read_addr(map->dso, map, ui->machine, | ||
| 444 | addr, (u8 *) data, sizeof(*data)); | ||
| 445 | |||
| 446 | return !(size == sizeof(*data)); | ||
| 447 | } | ||
| 448 | |||
| 449 | static int access_mem(unw_addr_space_t __maybe_unused as, | ||
| 450 | unw_word_t addr, unw_word_t *valp, | ||
| 451 | int __write, void *arg) | ||
| 452 | { | ||
| 453 | struct unwind_info *ui = arg; | ||
| 454 | struct stack_dump *stack = &ui->sample->user_stack; | ||
| 455 | u64 start, end; | ||
| 456 | int offset; | ||
| 457 | int ret; | ||
| 458 | |||
| 459 | /* Don't support write, probably not needed. */ | ||
| 460 | if (__write || !stack || !ui->sample->user_regs.regs) { | ||
| 461 | *valp = 0; | ||
| 462 | return 0; | ||
| 463 | } | ||
| 464 | |||
| 465 | ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); | ||
| 466 | if (ret) | ||
| 467 | return ret; | ||
| 468 | |||
| 469 | end = start + stack->size; | ||
| 470 | |||
| 471 | /* Check overflow. */ | ||
| 472 | if (addr + sizeof(unw_word_t) < addr) | ||
| 473 | return -EINVAL; | ||
| 474 | |||
| 475 | if (addr < start || addr + sizeof(unw_word_t) >= end) { | ||
| 476 | ret = access_dso_mem(ui, addr, valp); | ||
| 477 | if (ret) { | ||
| 478 | pr_debug("unwind: access_mem %p not inside range" | ||
| 479 | " 0x%" PRIx64 "-0x%" PRIx64 "\n", | ||
| 480 | (void *) (uintptr_t) addr, start, end); | ||
| 481 | *valp = 0; | ||
| 482 | return ret; | ||
| 483 | } | ||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | offset = addr - start; | ||
| 488 | *valp = *(unw_word_t *)&stack->data[offset]; | ||
| 489 | pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", | ||
| 490 | (void *) (uintptr_t) addr, (unsigned long)*valp, offset); | ||
| 491 | return 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | static int access_reg(unw_addr_space_t __maybe_unused as, | ||
| 495 | unw_regnum_t regnum, unw_word_t *valp, | ||
| 496 | int __write, void *arg) | ||
| 497 | { | ||
| 498 | struct unwind_info *ui = arg; | ||
| 499 | int id, ret; | ||
| 500 | u64 val; | ||
| 501 | |||
| 502 | /* Don't support write, I suspect we don't need it. */ | ||
| 503 | if (__write) { | ||
| 504 | pr_err("unwind: access_reg w %d\n", regnum); | ||
| 505 | return 0; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (!ui->sample->user_regs.regs) { | ||
| 509 | *valp = 0; | ||
| 510 | return 0; | ||
| 511 | } | ||
| 512 | |||
| 513 | id = LIBUNWIND__ARCH_REG_ID(regnum); | ||
| 514 | if (id < 0) | ||
| 515 | return -EINVAL; | ||
| 516 | |||
| 517 | ret = perf_reg_value(&val, &ui->sample->user_regs, id); | ||
| 518 | if (ret) { | ||
| 519 | pr_err("unwind: can't read reg %d\n", regnum); | ||
| 520 | return ret; | ||
| 521 | } | ||
| 522 | |||
| 523 | *valp = (unw_word_t) val; | ||
| 524 | pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); | ||
| 525 | return 0; | ||
| 526 | } | ||
| 527 | |||
| 528 | static void put_unwind_info(unw_addr_space_t __maybe_unused as, | ||
| 529 | unw_proc_info_t *pi __maybe_unused, | ||
| 530 | void *arg __maybe_unused) | ||
| 531 | { | ||
| 532 | pr_debug("unwind: put_unwind_info called\n"); | ||
| 533 | } | ||
| 534 | |||
| 535 | static int entry(u64 ip, struct thread *thread, | ||
| 536 | unwind_entry_cb_t cb, void *arg) | ||
| 537 | { | ||
| 538 | struct unwind_entry e; | ||
| 539 | struct addr_location al; | ||
| 540 | |||
| 541 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, | ||
| 542 | MAP__FUNCTION, ip, &al); | ||
| 543 | |||
| 544 | e.ip = ip; | ||
| 545 | e.map = al.map; | ||
| 546 | e.sym = al.sym; | ||
| 547 | |||
| 548 | pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", | ||
| 549 | al.sym ? al.sym->name : "''", | ||
| 550 | ip, | ||
| 551 | al.map ? al.map->map_ip(al.map, ip) : (u64) 0); | ||
| 552 | |||
| 553 | return cb(&e, arg); | ||
| 554 | } | ||
| 555 | |||
| 556 | static void display_error(int err) | ||
| 557 | { | ||
| 558 | switch (err) { | ||
| 559 | case UNW_EINVAL: | ||
| 560 | pr_err("unwind: Only supports local.\n"); | ||
| 561 | break; | ||
| 562 | case UNW_EUNSPEC: | ||
| 563 | pr_err("unwind: Unspecified error.\n"); | ||
| 564 | break; | ||
| 565 | case UNW_EBADREG: | ||
| 566 | pr_err("unwind: Register unavailable.\n"); | ||
| 567 | break; | ||
| 568 | default: | ||
| 569 | break; | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | static unw_accessors_t accessors = { | ||
| 574 | .find_proc_info = find_proc_info, | ||
| 575 | .put_unwind_info = put_unwind_info, | ||
| 576 | .get_dyn_info_list_addr = get_dyn_info_list_addr, | ||
| 577 | .access_mem = access_mem, | ||
| 578 | .access_reg = access_reg, | ||
| 579 | .access_fpreg = access_fpreg, | ||
| 580 | .resume = resume, | ||
| 581 | .get_proc_name = get_proc_name, | ||
| 582 | }; | ||
| 583 | |||
| 584 | static int _unwind__prepare_access(struct thread *thread) | ||
| 585 | { | ||
| 586 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 587 | return 0; | ||
| 588 | |||
| 589 | thread->addr_space = unw_create_addr_space(&accessors, 0); | ||
| 590 | if (!thread->addr_space) { | ||
| 591 | pr_err("unwind: Can't create unwind address space.\n"); | ||
| 592 | return -ENOMEM; | ||
| 593 | } | ||
| 594 | |||
| 595 | unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL); | ||
| 596 | return 0; | ||
| 597 | } | ||
| 598 | |||
| 599 | static void _unwind__flush_access(struct thread *thread) | ||
| 600 | { | ||
| 601 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 602 | return; | ||
| 603 | |||
| 604 | unw_flush_cache(thread->addr_space, 0, 0); | ||
| 605 | } | ||
| 606 | |||
| 607 | static void _unwind__finish_access(struct thread *thread) | ||
| 608 | { | ||
| 609 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 610 | return; | ||
| 611 | |||
| 612 | unw_destroy_addr_space(thread->addr_space); | ||
| 613 | } | ||
| 614 | |||
| 615 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | ||
| 616 | void *arg, int max_stack) | ||
| 617 | { | ||
| 618 | u64 val; | ||
| 619 | unw_word_t ips[max_stack]; | ||
| 620 | unw_addr_space_t addr_space; | ||
| 621 | unw_cursor_t c; | ||
| 622 | int ret, i = 0; | ||
| 623 | |||
| 624 | ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); | ||
| 625 | if (ret) | ||
| 626 | return ret; | ||
| 627 | |||
| 628 | ips[i++] = (unw_word_t) val; | ||
| 629 | |||
| 630 | /* | ||
| 631 | * If we need more than one entry, do the DWARF | ||
| 632 | * unwind itself. | ||
| 633 | */ | ||
| 634 | if (max_stack - 1 > 0) { | ||
| 635 | WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); | ||
| 636 | addr_space = ui->thread->addr_space; | ||
| 637 | |||
| 638 | if (addr_space == NULL) | ||
| 639 | return -1; | ||
| 640 | |||
| 641 | ret = unw_init_remote(&c, addr_space, ui); | ||
| 642 | if (ret) | ||
| 643 | display_error(ret); | ||
| 644 | |||
| 645 | while (!ret && (unw_step(&c) > 0) && i < max_stack) { | ||
| 646 | unw_get_reg(&c, UNW_REG_IP, &ips[i]); | ||
| 647 | ++i; | ||
| 648 | } | ||
| 649 | |||
| 650 | max_stack = i; | ||
| 651 | } | ||
| 652 | |||
| 653 | /* | ||
| 654 | * Display what we got based on the order setup. | ||
| 655 | */ | ||
| 656 | for (i = 0; i < max_stack && !ret; i++) { | ||
| 657 | int j = i; | ||
| 658 | |||
| 659 | if (callchain_param.order == ORDER_CALLER) | ||
| 660 | j = max_stack - i - 1; | ||
| 661 | ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; | ||
| 662 | } | ||
| 663 | |||
| 664 | return ret; | ||
| 665 | } | ||
| 666 | |||
| 667 | static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, | ||
| 668 | struct thread *thread, | ||
| 669 | struct perf_sample *data, int max_stack) | ||
| 670 | { | ||
| 671 | struct unwind_info ui = { | ||
| 672 | .sample = data, | ||
| 673 | .thread = thread, | ||
| 674 | .machine = thread->mg->machine, | ||
| 675 | }; | ||
| 676 | |||
| 677 | if (!data->user_regs.regs) | ||
| 678 | return -EINVAL; | ||
| 679 | |||
| 680 | if (max_stack <= 0) | ||
| 681 | return -EINVAL; | ||
| 682 | |||
| 683 | return get_entries(&ui, cb, arg, max_stack); | ||
| 684 | } | ||
| 685 | |||
| 686 | static struct unwind_libunwind_ops | ||
| 687 | _unwind_libunwind_ops = { | ||
| 688 | .prepare_access = _unwind__prepare_access, | ||
| 689 | .flush_access = _unwind__flush_access, | ||
| 690 | .finish_access = _unwind__finish_access, | ||
| 691 | .get_entries = _unwind__get_entries, | ||
| 692 | }; | ||
| 693 | |||
| 694 | #ifndef REMOTE_UNWIND_LIBUNWIND | ||
| 695 | struct unwind_libunwind_ops * | ||
| 696 | local_unwind_libunwind_ops = &_unwind_libunwind_ops; | ||
| 697 | #endif | ||
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 63687d3a344e..854711966cad 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
| @@ -1,682 +1,76 @@ | |||
| 1 | /* | 1 | #include "unwind.h" |
| 2 | * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. | ||
| 3 | * | ||
| 4 | * Lots of this code have been borrowed or heavily inspired from parts of | ||
| 5 | * the libunwind 0.99 code which are (amongst other contributors I may have | ||
| 6 | * forgotten): | ||
| 7 | * | ||
| 8 | * Copyright (C) 2002-2007 Hewlett-Packard Co | ||
| 9 | * Contributed by David Mosberger-Tang <davidm@hpl.hp.com> | ||
| 10 | * | ||
| 11 | * And the bugs have been added by: | ||
| 12 | * | ||
| 13 | * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com> | ||
| 14 | * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com> | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <elf.h> | ||
| 19 | #include <gelf.h> | ||
| 20 | #include <fcntl.h> | ||
| 21 | #include <string.h> | ||
| 22 | #include <unistd.h> | ||
| 23 | #include <sys/mman.h> | ||
| 24 | #include <linux/list.h> | ||
| 25 | #include <libunwind.h> | ||
| 26 | #include <libunwind-ptrace.h> | ||
| 27 | #include "callchain.h" | ||
| 28 | #include "thread.h" | 2 | #include "thread.h" |
| 29 | #include "session.h" | 3 | #include "session.h" |
| 30 | #include "perf_regs.h" | ||
| 31 | #include "unwind.h" | ||
| 32 | #include "symbol.h" | ||
| 33 | #include "util.h" | ||
| 34 | #include "debug.h" | 4 | #include "debug.h" |
| 35 | #include "asm/bug.h" | 5 | #include "arch/common.h" |
| 36 | |||
| 37 | extern int | ||
| 38 | UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | ||
| 39 | unw_word_t ip, | ||
| 40 | unw_dyn_info_t *di, | ||
| 41 | unw_proc_info_t *pi, | ||
| 42 | int need_unwind_info, void *arg); | ||
| 43 | |||
| 44 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | ||
| 45 | |||
| 46 | extern int | ||
| 47 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
| 48 | unw_word_t ip, | ||
| 49 | unw_word_t segbase, | ||
| 50 | const char *obj_name, unw_word_t start, | ||
| 51 | unw_word_t end); | ||
| 52 | |||
| 53 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
| 54 | |||
| 55 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | ||
| 56 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ | ||
| 57 | |||
| 58 | /* Pointer-encoding formats: */ | ||
| 59 | #define DW_EH_PE_omit 0xff | ||
| 60 | #define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ | ||
| 61 | #define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ | ||
| 62 | #define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ | ||
| 63 | #define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ | ||
| 64 | #define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ | ||
| 65 | |||
| 66 | /* Pointer-encoding application: */ | ||
| 67 | #define DW_EH_PE_absptr 0x00 /* absolute value */ | ||
| 68 | #define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ | ||
| 69 | |||
| 70 | /* | ||
| 71 | * The following are not documented by LSB v1.3, yet they are used by | ||
| 72 | * GCC, presumably they aren't documented by LSB since they aren't | ||
| 73 | * used on Linux: | ||
| 74 | */ | ||
| 75 | #define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ | ||
| 76 | #define DW_EH_PE_aligned 0x50 /* aligned pointer */ | ||
| 77 | 6 | ||
| 78 | /* Flags intentionaly not handled, since they're not needed: | 7 | struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops; |
| 79 | * #define DW_EH_PE_indirect 0x80 | 8 | struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops; |
| 80 | * #define DW_EH_PE_uleb128 0x01 | 9 | struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops; |
| 81 | * #define DW_EH_PE_udata2 0x02 | ||
| 82 | * #define DW_EH_PE_sleb128 0x09 | ||
| 83 | * #define DW_EH_PE_sdata2 0x0a | ||
| 84 | * #define DW_EH_PE_textrel 0x20 | ||
| 85 | * #define DW_EH_PE_datarel 0x30 | ||
| 86 | */ | ||
| 87 | 10 | ||
| 88 | struct unwind_info { | 11 | static void unwind__register_ops(struct thread *thread, |
| 89 | struct perf_sample *sample; | 12 | struct unwind_libunwind_ops *ops) |
| 90 | struct machine *machine; | ||
| 91 | struct thread *thread; | ||
| 92 | }; | ||
| 93 | |||
| 94 | #define dw_read(ptr, type, end) ({ \ | ||
| 95 | type *__p = (type *) ptr; \ | ||
| 96 | type __v; \ | ||
| 97 | if ((__p + 1) > (type *) end) \ | ||
| 98 | return -EINVAL; \ | ||
| 99 | __v = *__p++; \ | ||
| 100 | ptr = (typeof(ptr)) __p; \ | ||
| 101 | __v; \ | ||
| 102 | }) | ||
| 103 | |||
| 104 | static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, | ||
| 105 | u8 encoding) | ||
| 106 | { | 13 | { |
| 107 | u8 *cur = *p; | 14 | thread->unwind_libunwind_ops = ops; |
| 108 | *val = 0; | ||
| 109 | |||
| 110 | switch (encoding) { | ||
| 111 | case DW_EH_PE_omit: | ||
| 112 | *val = 0; | ||
| 113 | goto out; | ||
| 114 | case DW_EH_PE_ptr: | ||
| 115 | *val = dw_read(cur, unsigned long, end); | ||
| 116 | goto out; | ||
| 117 | default: | ||
| 118 | break; | ||
| 119 | } | ||
| 120 | |||
| 121 | switch (encoding & DW_EH_PE_APPL_MASK) { | ||
| 122 | case DW_EH_PE_absptr: | ||
| 123 | break; | ||
| 124 | case DW_EH_PE_pcrel: | ||
| 125 | *val = (unsigned long) cur; | ||
| 126 | break; | ||
| 127 | default: | ||
| 128 | return -EINVAL; | ||
| 129 | } | ||
| 130 | |||
| 131 | if ((encoding & 0x07) == 0x00) | ||
| 132 | encoding |= DW_EH_PE_udata4; | ||
| 133 | |||
| 134 | switch (encoding & DW_EH_PE_FORMAT_MASK) { | ||
| 135 | case DW_EH_PE_sdata4: | ||
| 136 | *val += dw_read(cur, s32, end); | ||
| 137 | break; | ||
| 138 | case DW_EH_PE_udata4: | ||
| 139 | *val += dw_read(cur, u32, end); | ||
| 140 | break; | ||
| 141 | case DW_EH_PE_sdata8: | ||
| 142 | *val += dw_read(cur, s64, end); | ||
| 143 | break; | ||
| 144 | case DW_EH_PE_udata8: | ||
| 145 | *val += dw_read(cur, u64, end); | ||
| 146 | break; | ||
| 147 | default: | ||
| 148 | return -EINVAL; | ||
| 149 | } | ||
| 150 | |||
| 151 | out: | ||
| 152 | *p = cur; | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | #define dw_read_encoded_value(ptr, end, enc) ({ \ | ||
| 157 | u64 __v; \ | ||
| 158 | if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ | ||
| 159 | return -EINVAL; \ | ||
| 160 | } \ | ||
| 161 | __v; \ | ||
| 162 | }) | ||
| 163 | |||
| 164 | static u64 elf_section_offset(int fd, const char *name) | ||
| 165 | { | ||
| 166 | Elf *elf; | ||
| 167 | GElf_Ehdr ehdr; | ||
| 168 | GElf_Shdr shdr; | ||
| 169 | u64 offset = 0; | ||
| 170 | |||
| 171 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
| 172 | if (elf == NULL) | ||
| 173 | return 0; | ||
| 174 | |||
| 175 | do { | ||
| 176 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 177 | break; | ||
| 178 | |||
| 179 | if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) | ||
| 180 | break; | ||
| 181 | |||
| 182 | offset = shdr.sh_offset; | ||
| 183 | } while (0); | ||
| 184 | |||
| 185 | elf_end(elf); | ||
| 186 | return offset; | ||
| 187 | } | 15 | } |
| 188 | 16 | ||
| 189 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | 17 | int unwind__prepare_access(struct thread *thread, struct map *map) |
| 190 | static int elf_is_exec(int fd, const char *name) | ||
| 191 | { | 18 | { |
| 192 | Elf *elf; | 19 | const char *arch; |
| 193 | GElf_Ehdr ehdr; | 20 | enum dso_type dso_type; |
| 194 | int retval = 0; | 21 | struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops; |
| 195 | 22 | ||
| 196 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 23 | if (thread->addr_space) { |
| 197 | if (elf == NULL) | 24 | pr_debug("unwind: thread map already set, dso=%s\n", |
| 25 | map->dso->name); | ||
| 198 | return 0; | 26 | return 0; |
| 199 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 200 | goto out; | ||
| 201 | |||
| 202 | retval = (ehdr.e_type == ET_EXEC); | ||
| 203 | |||
| 204 | out: | ||
| 205 | elf_end(elf); | ||
| 206 | pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval); | ||
| 207 | return retval; | ||
| 208 | } | ||
| 209 | #endif | ||
| 210 | |||
| 211 | struct table_entry { | ||
| 212 | u32 start_ip_offset; | ||
| 213 | u32 fde_offset; | ||
| 214 | }; | ||
| 215 | |||
| 216 | struct eh_frame_hdr { | ||
| 217 | unsigned char version; | ||
| 218 | unsigned char eh_frame_ptr_enc; | ||
| 219 | unsigned char fde_count_enc; | ||
| 220 | unsigned char table_enc; | ||
| 221 | |||
| 222 | /* | ||
| 223 | * The rest of the header is variable-length and consists of the | ||
| 224 | * following members: | ||
| 225 | * | ||
| 226 | * encoded_t eh_frame_ptr; | ||
| 227 | * encoded_t fde_count; | ||
| 228 | */ | ||
| 229 | |||
| 230 | /* A single encoded pointer should not be more than 8 bytes. */ | ||
| 231 | u64 enc[2]; | ||
| 232 | |||
| 233 | /* | ||
| 234 | * struct { | ||
| 235 | * encoded_t start_ip; | ||
| 236 | * encoded_t fde_addr; | ||
| 237 | * } binary_search_table[fde_count]; | ||
| 238 | */ | ||
| 239 | char data[0]; | ||
| 240 | } __packed; | ||
| 241 | |||
| 242 | static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | ||
| 243 | u64 offset, u64 *table_data, u64 *segbase, | ||
| 244 | u64 *fde_count) | ||
| 245 | { | ||
| 246 | struct eh_frame_hdr hdr; | ||
| 247 | u8 *enc = (u8 *) &hdr.enc; | ||
| 248 | u8 *end = (u8 *) &hdr.data; | ||
| 249 | ssize_t r; | ||
| 250 | |||
| 251 | r = dso__data_read_offset(dso, machine, offset, | ||
| 252 | (u8 *) &hdr, sizeof(hdr)); | ||
| 253 | if (r != sizeof(hdr)) | ||
| 254 | return -EINVAL; | ||
| 255 | |||
| 256 | /* We dont need eh_frame_ptr, just skip it. */ | ||
| 257 | dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); | ||
| 258 | |||
| 259 | *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); | ||
| 260 | *segbase = offset; | ||
| 261 | *table_data = (enc - (u8 *) &hdr) + offset; | ||
| 262 | return 0; | ||
| 263 | } | ||
| 264 | |||
| 265 | static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | ||
| 266 | u64 *table_data, u64 *segbase, | ||
| 267 | u64 *fde_count) | ||
| 268 | { | ||
| 269 | int ret = -EINVAL, fd; | ||
| 270 | u64 offset = dso->data.eh_frame_hdr_offset; | ||
| 271 | |||
| 272 | if (offset == 0) { | ||
| 273 | fd = dso__data_get_fd(dso, machine); | ||
| 274 | if (fd < 0) | ||
| 275 | return -EINVAL; | ||
| 276 | |||
| 277 | /* Check the .eh_frame section for unwinding info */ | ||
| 278 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | ||
| 279 | dso->data.eh_frame_hdr_offset = offset; | ||
| 280 | dso__data_put_fd(dso); | ||
| 281 | } | 27 | } |
| 282 | 28 | ||
| 283 | if (offset) | 29 | /* env->arch is NULL for live-mode (i.e. perf top) */ |
| 284 | ret = unwind_spec_ehframe(dso, machine, offset, | 30 | if (!thread->mg->machine->env || !thread->mg->machine->env->arch) |
| 285 | table_data, segbase, | 31 | goto out_register; |
| 286 | fde_count); | ||
| 287 | 32 | ||
| 288 | return ret; | 33 | dso_type = dso__type(map->dso, thread->mg->machine); |
| 289 | } | 34 | if (dso_type == DSO__TYPE_UNKNOWN) |
| 290 | |||
| 291 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 292 | static int read_unwind_spec_debug_frame(struct dso *dso, | ||
| 293 | struct machine *machine, u64 *offset) | ||
| 294 | { | ||
| 295 | int fd; | ||
| 296 | u64 ofs = dso->data.debug_frame_offset; | ||
| 297 | |||
| 298 | if (ofs == 0) { | ||
| 299 | fd = dso__data_get_fd(dso, machine); | ||
| 300 | if (fd < 0) | ||
| 301 | return -EINVAL; | ||
| 302 | |||
| 303 | /* Check the .debug_frame section for unwinding info */ | ||
| 304 | ofs = elf_section_offset(fd, ".debug_frame"); | ||
| 305 | dso->data.debug_frame_offset = ofs; | ||
| 306 | dso__data_put_fd(dso); | ||
| 307 | } | ||
| 308 | |||
| 309 | *offset = ofs; | ||
| 310 | if (*offset) | ||
| 311 | return 0; | 35 | return 0; |
| 312 | 36 | ||
| 313 | return -EINVAL; | 37 | arch = normalize_arch(thread->mg->machine->env->arch); |
| 314 | } | ||
| 315 | #endif | ||
| 316 | |||
| 317 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | ||
| 318 | { | ||
| 319 | struct addr_location al; | ||
| 320 | |||
| 321 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, | ||
| 322 | MAP__FUNCTION, ip, &al); | ||
| 323 | if (!al.map) { | ||
| 324 | /* | ||
| 325 | * We've seen cases (softice) where DWARF unwinder went | ||
| 326 | * through non executable mmaps, which we need to lookup | ||
| 327 | * in MAP__VARIABLE tree. | ||
| 328 | */ | ||
| 329 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, | ||
| 330 | MAP__VARIABLE, ip, &al); | ||
| 331 | } | ||
| 332 | return al.map; | ||
| 333 | } | ||
| 334 | |||
| 335 | static int | ||
| 336 | find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | ||
| 337 | int need_unwind_info, void *arg) | ||
| 338 | { | ||
| 339 | struct unwind_info *ui = arg; | ||
| 340 | struct map *map; | ||
| 341 | unw_dyn_info_t di; | ||
| 342 | u64 table_data, segbase, fde_count; | ||
| 343 | int ret = -EINVAL; | ||
| 344 | |||
| 345 | map = find_map(ip, ui); | ||
| 346 | if (!map || !map->dso) | ||
| 347 | return -EINVAL; | ||
| 348 | |||
| 349 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | ||
| 350 | |||
| 351 | /* Check the .eh_frame section for unwinding info */ | ||
| 352 | if (!read_unwind_spec_eh_frame(map->dso, ui->machine, | ||
| 353 | &table_data, &segbase, &fde_count)) { | ||
| 354 | memset(&di, 0, sizeof(di)); | ||
| 355 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
| 356 | di.start_ip = map->start; | ||
| 357 | di.end_ip = map->end; | ||
| 358 | di.u.rti.segbase = map->start + segbase; | ||
| 359 | di.u.rti.table_data = map->start + table_data; | ||
| 360 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
| 361 | / sizeof(unw_word_t); | ||
| 362 | ret = dwarf_search_unwind_table(as, ip, &di, pi, | ||
| 363 | need_unwind_info, arg); | ||
| 364 | } | ||
| 365 | |||
| 366 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
| 367 | /* Check the .debug_frame section for unwinding info */ | ||
| 368 | if (ret < 0 && | ||
| 369 | !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { | ||
| 370 | int fd = dso__data_get_fd(map->dso, ui->machine); | ||
| 371 | int is_exec = elf_is_exec(fd, map->dso->name); | ||
| 372 | unw_word_t base = is_exec ? 0 : map->start; | ||
| 373 | const char *symfile; | ||
| 374 | |||
| 375 | if (fd >= 0) | ||
| 376 | dso__data_put_fd(map->dso); | ||
| 377 | |||
| 378 | symfile = map->dso->symsrc_filename ?: map->dso->name; | ||
| 379 | |||
| 380 | memset(&di, 0, sizeof(di)); | ||
| 381 | if (dwarf_find_debug_frame(0, &di, ip, base, symfile, | ||
| 382 | map->start, map->end)) | ||
| 383 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
| 384 | need_unwind_info, arg); | ||
| 385 | } | ||
| 386 | #endif | ||
| 387 | |||
| 388 | return ret; | ||
| 389 | } | ||
| 390 | |||
| 391 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | ||
| 392 | unw_regnum_t __maybe_unused num, | ||
| 393 | unw_fpreg_t __maybe_unused *val, | ||
| 394 | int __maybe_unused __write, | ||
| 395 | void __maybe_unused *arg) | ||
| 396 | { | ||
| 397 | pr_err("unwind: access_fpreg unsupported\n"); | ||
| 398 | return -UNW_EINVAL; | ||
| 399 | } | ||
| 400 | |||
| 401 | static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, | ||
| 402 | unw_word_t __maybe_unused *dil_addr, | ||
| 403 | void __maybe_unused *arg) | ||
| 404 | { | ||
| 405 | return -UNW_ENOINFO; | ||
| 406 | } | ||
| 407 | |||
| 408 | static int resume(unw_addr_space_t __maybe_unused as, | ||
| 409 | unw_cursor_t __maybe_unused *cu, | ||
| 410 | void __maybe_unused *arg) | ||
| 411 | { | ||
| 412 | pr_err("unwind: resume unsupported\n"); | ||
| 413 | return -UNW_EINVAL; | ||
| 414 | } | ||
| 415 | 38 | ||
| 416 | static int | 39 | if (!strcmp(arch, "x86")) { |
| 417 | get_proc_name(unw_addr_space_t __maybe_unused as, | 40 | if (dso_type != DSO__TYPE_64BIT) |
| 418 | unw_word_t __maybe_unused addr, | 41 | ops = x86_32_unwind_libunwind_ops; |
| 419 | char __maybe_unused *bufp, size_t __maybe_unused buf_len, | 42 | } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) { |
| 420 | unw_word_t __maybe_unused *offp, void __maybe_unused *arg) | 43 | if (dso_type == DSO__TYPE_64BIT) |
| 421 | { | 44 | ops = arm64_unwind_libunwind_ops; |
| 422 | pr_err("unwind: get_proc_name unsupported\n"); | ||
| 423 | return -UNW_EINVAL; | ||
| 424 | } | ||
| 425 | |||
| 426 | static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, | ||
| 427 | unw_word_t *data) | ||
| 428 | { | ||
| 429 | struct map *map; | ||
| 430 | ssize_t size; | ||
| 431 | |||
| 432 | map = find_map(addr, ui); | ||
| 433 | if (!map) { | ||
| 434 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | ||
| 435 | return -1; | ||
| 436 | } | 45 | } |
| 437 | 46 | ||
| 438 | if (!map->dso) | 47 | if (!ops) { |
| 48 | pr_err("unwind: target platform=%s is not supported\n", arch); | ||
| 439 | return -1; | 49 | return -1; |
| 440 | |||
| 441 | size = dso__data_read_addr(map->dso, map, ui->machine, | ||
| 442 | addr, (u8 *) data, sizeof(*data)); | ||
| 443 | |||
| 444 | return !(size == sizeof(*data)); | ||
| 445 | } | ||
| 446 | |||
| 447 | static int access_mem(unw_addr_space_t __maybe_unused as, | ||
| 448 | unw_word_t addr, unw_word_t *valp, | ||
| 449 | int __write, void *arg) | ||
| 450 | { | ||
| 451 | struct unwind_info *ui = arg; | ||
| 452 | struct stack_dump *stack = &ui->sample->user_stack; | ||
| 453 | u64 start, end; | ||
| 454 | int offset; | ||
| 455 | int ret; | ||
| 456 | |||
| 457 | /* Don't support write, probably not needed. */ | ||
| 458 | if (__write || !stack || !ui->sample->user_regs.regs) { | ||
| 459 | *valp = 0; | ||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); | ||
| 464 | if (ret) | ||
| 465 | return ret; | ||
| 466 | |||
| 467 | end = start + stack->size; | ||
| 468 | |||
| 469 | /* Check overflow. */ | ||
| 470 | if (addr + sizeof(unw_word_t) < addr) | ||
| 471 | return -EINVAL; | ||
| 472 | |||
| 473 | if (addr < start || addr + sizeof(unw_word_t) >= end) { | ||
| 474 | ret = access_dso_mem(ui, addr, valp); | ||
| 475 | if (ret) { | ||
| 476 | pr_debug("unwind: access_mem %p not inside range" | ||
| 477 | " 0x%" PRIx64 "-0x%" PRIx64 "\n", | ||
| 478 | (void *) (uintptr_t) addr, start, end); | ||
| 479 | *valp = 0; | ||
| 480 | return ret; | ||
| 481 | } | ||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | offset = addr - start; | ||
| 486 | *valp = *(unw_word_t *)&stack->data[offset]; | ||
| 487 | pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", | ||
| 488 | (void *) (uintptr_t) addr, (unsigned long)*valp, offset); | ||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | |||
| 492 | static int access_reg(unw_addr_space_t __maybe_unused as, | ||
| 493 | unw_regnum_t regnum, unw_word_t *valp, | ||
| 494 | int __write, void *arg) | ||
| 495 | { | ||
| 496 | struct unwind_info *ui = arg; | ||
| 497 | int id, ret; | ||
| 498 | u64 val; | ||
| 499 | |||
| 500 | /* Don't support write, I suspect we don't need it. */ | ||
| 501 | if (__write) { | ||
| 502 | pr_err("unwind: access_reg w %d\n", regnum); | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | if (!ui->sample->user_regs.regs) { | ||
| 507 | *valp = 0; | ||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | id = libunwind__arch_reg_id(regnum); | ||
| 512 | if (id < 0) | ||
| 513 | return -EINVAL; | ||
| 514 | |||
| 515 | ret = perf_reg_value(&val, &ui->sample->user_regs, id); | ||
| 516 | if (ret) { | ||
| 517 | pr_err("unwind: can't read reg %d\n", regnum); | ||
| 518 | return ret; | ||
| 519 | } | ||
| 520 | |||
| 521 | *valp = (unw_word_t) val; | ||
| 522 | pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); | ||
| 523 | return 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | static void put_unwind_info(unw_addr_space_t __maybe_unused as, | ||
| 527 | unw_proc_info_t *pi __maybe_unused, | ||
| 528 | void *arg __maybe_unused) | ||
| 529 | { | ||
| 530 | pr_debug("unwind: put_unwind_info called\n"); | ||
| 531 | } | ||
| 532 | |||
| 533 | static int entry(u64 ip, struct thread *thread, | ||
| 534 | unwind_entry_cb_t cb, void *arg) | ||
| 535 | { | ||
| 536 | struct unwind_entry e; | ||
| 537 | struct addr_location al; | ||
| 538 | |||
| 539 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, | ||
| 540 | MAP__FUNCTION, ip, &al); | ||
| 541 | |||
| 542 | e.ip = ip; | ||
| 543 | e.map = al.map; | ||
| 544 | e.sym = al.sym; | ||
| 545 | |||
| 546 | pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", | ||
| 547 | al.sym ? al.sym->name : "''", | ||
| 548 | ip, | ||
| 549 | al.map ? al.map->map_ip(al.map, ip) : (u64) 0); | ||
| 550 | |||
| 551 | return cb(&e, arg); | ||
| 552 | } | ||
| 553 | |||
| 554 | static void display_error(int err) | ||
| 555 | { | ||
| 556 | switch (err) { | ||
| 557 | case UNW_EINVAL: | ||
| 558 | pr_err("unwind: Only supports local.\n"); | ||
| 559 | break; | ||
| 560 | case UNW_EUNSPEC: | ||
| 561 | pr_err("unwind: Unspecified error.\n"); | ||
| 562 | break; | ||
| 563 | case UNW_EBADREG: | ||
| 564 | pr_err("unwind: Register unavailable.\n"); | ||
| 565 | break; | ||
| 566 | default: | ||
| 567 | break; | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | static unw_accessors_t accessors = { | ||
| 572 | .find_proc_info = find_proc_info, | ||
| 573 | .put_unwind_info = put_unwind_info, | ||
| 574 | .get_dyn_info_list_addr = get_dyn_info_list_addr, | ||
| 575 | .access_mem = access_mem, | ||
| 576 | .access_reg = access_reg, | ||
| 577 | .access_fpreg = access_fpreg, | ||
| 578 | .resume = resume, | ||
| 579 | .get_proc_name = get_proc_name, | ||
| 580 | }; | ||
| 581 | |||
| 582 | int unwind__prepare_access(struct thread *thread) | ||
| 583 | { | ||
| 584 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 585 | return 0; | ||
| 586 | |||
| 587 | thread->addr_space = unw_create_addr_space(&accessors, 0); | ||
| 588 | if (!thread->addr_space) { | ||
| 589 | pr_err("unwind: Can't create unwind address space.\n"); | ||
| 590 | return -ENOMEM; | ||
| 591 | } | 50 | } |
| 51 | out_register: | ||
| 52 | unwind__register_ops(thread, ops); | ||
| 592 | 53 | ||
| 593 | unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL); | 54 | return thread->unwind_libunwind_ops->prepare_access(thread); |
| 594 | return 0; | ||
| 595 | } | 55 | } |
| 596 | 56 | ||
| 597 | void unwind__flush_access(struct thread *thread) | 57 | void unwind__flush_access(struct thread *thread) |
| 598 | { | 58 | { |
| 599 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | 59 | if (thread->unwind_libunwind_ops) |
| 600 | return; | 60 | thread->unwind_libunwind_ops->flush_access(thread); |
| 601 | |||
| 602 | unw_flush_cache(thread->addr_space, 0, 0); | ||
| 603 | } | 61 | } |
| 604 | 62 | ||
| 605 | void unwind__finish_access(struct thread *thread) | 63 | void unwind__finish_access(struct thread *thread) |
| 606 | { | 64 | { |
| 607 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | 65 | if (thread->unwind_libunwind_ops) |
| 608 | return; | 66 | thread->unwind_libunwind_ops->finish_access(thread); |
| 609 | |||
| 610 | unw_destroy_addr_space(thread->addr_space); | ||
| 611 | } | ||
| 612 | |||
| 613 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | ||
| 614 | void *arg, int max_stack) | ||
| 615 | { | ||
| 616 | u64 val; | ||
| 617 | unw_word_t ips[max_stack]; | ||
| 618 | unw_addr_space_t addr_space; | ||
| 619 | unw_cursor_t c; | ||
| 620 | int ret, i = 0; | ||
| 621 | |||
| 622 | ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); | ||
| 623 | if (ret) | ||
| 624 | return ret; | ||
| 625 | |||
| 626 | ips[i++] = (unw_word_t) val; | ||
| 627 | |||
| 628 | /* | ||
| 629 | * If we need more than one entry, do the DWARF | ||
| 630 | * unwind itself. | ||
| 631 | */ | ||
| 632 | if (max_stack - 1 > 0) { | ||
| 633 | WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); | ||
| 634 | addr_space = ui->thread->addr_space; | ||
| 635 | |||
| 636 | if (addr_space == NULL) | ||
| 637 | return -1; | ||
| 638 | |||
| 639 | ret = unw_init_remote(&c, addr_space, ui); | ||
| 640 | if (ret) | ||
| 641 | display_error(ret); | ||
| 642 | |||
| 643 | while (!ret && (unw_step(&c) > 0) && i < max_stack) { | ||
| 644 | unw_get_reg(&c, UNW_REG_IP, &ips[i]); | ||
| 645 | ++i; | ||
| 646 | } | ||
| 647 | |||
| 648 | max_stack = i; | ||
| 649 | } | ||
| 650 | |||
| 651 | /* | ||
| 652 | * Display what we got based on the order setup. | ||
| 653 | */ | ||
| 654 | for (i = 0; i < max_stack && !ret; i++) { | ||
| 655 | int j = i; | ||
| 656 | |||
| 657 | if (callchain_param.order == ORDER_CALLER) | ||
| 658 | j = max_stack - i - 1; | ||
| 659 | ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; | ||
| 660 | } | ||
| 661 | |||
| 662 | return ret; | ||
| 663 | } | 67 | } |
| 664 | 68 | ||
| 665 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 69 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
| 666 | struct thread *thread, | 70 | struct thread *thread, |
| 667 | struct perf_sample *data, int max_stack) | 71 | struct perf_sample *data, int max_stack) |
| 668 | { | 72 | { |
| 669 | struct unwind_info ui = { | 73 | if (thread->unwind_libunwind_ops) |
| 670 | .sample = data, | 74 | return thread->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack); |
| 671 | .thread = thread, | 75 | return 0; |
| 672 | .machine = thread->mg->machine, | ||
| 673 | }; | ||
| 674 | |||
| 675 | if (!data->user_regs.regs) | ||
| 676 | return -EINVAL; | ||
| 677 | |||
| 678 | if (max_stack <= 0) | ||
| 679 | return -EINVAL; | ||
| 680 | |||
| 681 | return get_entries(&ui, cb, arg, max_stack); | ||
| 682 | } | 76 | } |
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 12790cf94618..b07466240346 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
| @@ -14,18 +14,31 @@ struct unwind_entry { | |||
| 14 | 14 | ||
| 15 | typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); | 15 | typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); |
| 16 | 16 | ||
| 17 | struct unwind_libunwind_ops { | ||
| 18 | int (*prepare_access)(struct thread *thread); | ||
| 19 | void (*flush_access)(struct thread *thread); | ||
| 20 | void (*finish_access)(struct thread *thread); | ||
| 21 | int (*get_entries)(unwind_entry_cb_t cb, void *arg, | ||
| 22 | struct thread *thread, | ||
| 23 | struct perf_sample *data, int max_stack); | ||
| 24 | }; | ||
| 25 | |||
| 17 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 26 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
| 18 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 27 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
| 19 | struct thread *thread, | 28 | struct thread *thread, |
| 20 | struct perf_sample *data, int max_stack); | 29 | struct perf_sample *data, int max_stack); |
| 21 | /* libunwind specific */ | 30 | /* libunwind specific */ |
| 22 | #ifdef HAVE_LIBUNWIND_SUPPORT | 31 | #ifdef HAVE_LIBUNWIND_SUPPORT |
| 23 | int libunwind__arch_reg_id(int regnum); | 32 | #ifndef LIBUNWIND__ARCH_REG_ID |
| 24 | int unwind__prepare_access(struct thread *thread); | 33 | #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) |
| 34 | #endif | ||
| 35 | int LIBUNWIND__ARCH_REG_ID(int regnum); | ||
| 36 | int unwind__prepare_access(struct thread *thread, struct map *map); | ||
| 25 | void unwind__flush_access(struct thread *thread); | 37 | void unwind__flush_access(struct thread *thread); |
| 26 | void unwind__finish_access(struct thread *thread); | 38 | void unwind__finish_access(struct thread *thread); |
| 27 | #else | 39 | #else |
| 28 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | 40 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused, |
| 41 | struct map *map __maybe_unused) | ||
| 29 | { | 42 | { |
| 30 | return 0; | 43 | return 0; |
| 31 | } | 44 | } |
| @@ -44,7 +57,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | |||
| 44 | return 0; | 57 | return 0; |
| 45 | } | 58 | } |
| 46 | 59 | ||
| 47 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | 60 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused, |
| 61 | struct map *map __maybe_unused) | ||
| 48 | { | 62 | { |
| 49 | return 0; | 63 | return 0; |
| 50 | } | 64 | } |
