diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/arch/arm/Makefile | 3 | ||||
-rw-r--r-- | tools/perf/arch/arm/include/perf_regs.h | 54 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/unwind.c | 48 | ||||
-rw-r--r-- | tools/perf/config/Makefile | 14 | ||||
-rw-r--r-- | tools/perf/config/feature-checks/Makefile | 1 | ||||
-rw-r--r-- | tools/perf/config/feature-checks/test-all.c | 4 | ||||
-rw-r--r-- | tools/perf/config/feature-checks/test-libunwind-debug-frame.c | 16 | ||||
-rw-r--r-- | tools/perf/util/unwind.c | 75 |
8 files changed, 196 insertions, 19 deletions
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 15130b50dfe3..fe9b61e322a5 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile | |||
@@ -2,3 +2,6 @@ ifndef NO_DWARF | |||
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | endif | 4 | endif |
5 | ifndef NO_LIBUNWIND | ||
6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o | ||
7 | endif | ||
diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h new file mode 100644 index 000000000000..2a1cfde66b69 --- /dev/null +++ b/tools/perf/arch/arm/include/perf_regs.h | |||
@@ -0,0 +1,54 @@ | |||
1 | #ifndef ARCH_PERF_REGS_H | ||
2 | #define ARCH_PERF_REGS_H | ||
3 | |||
4 | #include <stdlib.h> | ||
5 | #include "../../util/types.h" | ||
6 | #include <asm/perf_regs.h> | ||
7 | |||
8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1) | ||
9 | #define PERF_REG_IP PERF_REG_ARM_PC | ||
10 | #define PERF_REG_SP PERF_REG_ARM_SP | ||
11 | |||
12 | static inline const char *perf_reg_name(int id) | ||
13 | { | ||
14 | switch (id) { | ||
15 | case PERF_REG_ARM_R0: | ||
16 | return "r0"; | ||
17 | case PERF_REG_ARM_R1: | ||
18 | return "r1"; | ||
19 | case PERF_REG_ARM_R2: | ||
20 | return "r2"; | ||
21 | case PERF_REG_ARM_R3: | ||
22 | return "r3"; | ||
23 | case PERF_REG_ARM_R4: | ||
24 | return "r4"; | ||
25 | case PERF_REG_ARM_R5: | ||
26 | return "r5"; | ||
27 | case PERF_REG_ARM_R6: | ||
28 | return "r6"; | ||
29 | case PERF_REG_ARM_R7: | ||
30 | return "r7"; | ||
31 | case PERF_REG_ARM_R8: | ||
32 | return "r8"; | ||
33 | case PERF_REG_ARM_R9: | ||
34 | return "r9"; | ||
35 | case PERF_REG_ARM_R10: | ||
36 | return "r10"; | ||
37 | case PERF_REG_ARM_FP: | ||
38 | return "fp"; | ||
39 | case PERF_REG_ARM_IP: | ||
40 | return "ip"; | ||
41 | case PERF_REG_ARM_SP: | ||
42 | return "sp"; | ||
43 | case PERF_REG_ARM_LR: | ||
44 | return "lr"; | ||
45 | case PERF_REG_ARM_PC: | ||
46 | return "pc"; | ||
47 | default: | ||
48 | return NULL; | ||
49 | } | ||
50 | |||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | #endif /* ARCH_PERF_REGS_H */ | ||
diff --git a/tools/perf/arch/arm/util/unwind.c b/tools/perf/arch/arm/util/unwind.c new file mode 100644 index 000000000000..da3dc950550c --- /dev/null +++ b/tools/perf/arch/arm/util/unwind.c | |||
@@ -0,0 +1,48 @@ | |||
1 | |||
2 | #include <errno.h> | ||
3 | #include <libunwind.h> | ||
4 | #include "perf_regs.h" | ||
5 | #include "../../util/unwind.h" | ||
6 | |||
7 | int unwind__arch_reg_id(int regnum) | ||
8 | { | ||
9 | switch (regnum) { | ||
10 | case UNW_ARM_R0: | ||
11 | return PERF_REG_ARM_R0; | ||
12 | case UNW_ARM_R1: | ||
13 | return PERF_REG_ARM_R1; | ||
14 | case UNW_ARM_R2: | ||
15 | return PERF_REG_ARM_R2; | ||
16 | case UNW_ARM_R3: | ||
17 | return PERF_REG_ARM_R3; | ||
18 | case UNW_ARM_R4: | ||
19 | return PERF_REG_ARM_R4; | ||
20 | case UNW_ARM_R5: | ||
21 | return PERF_REG_ARM_R5; | ||
22 | case UNW_ARM_R6: | ||
23 | return PERF_REG_ARM_R6; | ||
24 | case UNW_ARM_R7: | ||
25 | return PERF_REG_ARM_R7; | ||
26 | case UNW_ARM_R8: | ||
27 | return PERF_REG_ARM_R8; | ||
28 | case UNW_ARM_R9: | ||
29 | return PERF_REG_ARM_R9; | ||
30 | case UNW_ARM_R10: | ||
31 | return PERF_REG_ARM_R10; | ||
32 | case UNW_ARM_R11: | ||
33 | return PERF_REG_ARM_FP; | ||
34 | case UNW_ARM_R12: | ||
35 | return PERF_REG_ARM_IP; | ||
36 | case UNW_ARM_R13: | ||
37 | return PERF_REG_ARM_SP; | ||
38 | case UNW_ARM_R14: | ||
39 | return PERF_REG_ARM_LR; | ||
40 | case UNW_ARM_R15: | ||
41 | return PERF_REG_ARM_PC; | ||
42 | default: | ||
43 | pr_err("unwind: invalid reg id %d\n", regnum); | ||
44 | return -EINVAL; | ||
45 | } | ||
46 | |||
47 | return -EINVAL; | ||
48 | } | ||
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 58b2d37ae23a..f5905f2b197d 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -31,6 +31,10 @@ ifeq ($(ARCH),x86_64) | |||
31 | endif | 31 | endif |
32 | NO_PERF_REGS := 0 | 32 | NO_PERF_REGS := 0 |
33 | endif | 33 | endif |
34 | ifeq ($(ARCH),arm) | ||
35 | NO_PERF_REGS := 0 | ||
36 | LIBUNWIND_LIBS = -lunwind -lunwind-arm | ||
37 | endif | ||
34 | 38 | ||
35 | ifeq ($(NO_PERF_REGS),0) | 39 | ifeq ($(NO_PERF_REGS),0) |
36 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT | 40 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT |
@@ -305,8 +309,7 @@ ifndef NO_LIBELF | |||
305 | endif # NO_DWARF | 309 | endif # NO_DWARF |
306 | endif # NO_LIBELF | 310 | endif # NO_LIBELF |
307 | 311 | ||
308 | # There's only x86 (both 32 and 64) support for CFI unwind so far | 312 | ifeq ($(LIBUNWIND_LIBS),) |
309 | ifneq ($(ARCH),x86) | ||
310 | NO_LIBUNWIND := 1 | 313 | NO_LIBUNWIND := 1 |
311 | endif | 314 | endif |
312 | 315 | ||
@@ -322,8 +325,13 @@ ifndef NO_LIBUNWIND | |||
322 | endif | 325 | endif |
323 | 326 | ||
324 | ifneq ($(feature-libunwind), 1) | 327 | ifneq ($(feature-libunwind), 1) |
325 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); | 328 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1); |
326 | NO_LIBUNWIND := 1 | 329 | NO_LIBUNWIND := 1 |
330 | else | ||
331 | ifneq ($(feature-libunwind-debug-frame), 1) | ||
332 | msg := $(warning No debug_frame support found in libunwind); | ||
333 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME | ||
334 | endif | ||
327 | endif | 335 | endif |
328 | endif | 336 | endif |
329 | 337 | ||
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index c803f17fb986..e8e195f49a4e 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
@@ -23,6 +23,7 @@ FILES= \ | |||
23 | test-libpython-version \ | 23 | test-libpython-version \ |
24 | test-libslang \ | 24 | test-libslang \ |
25 | test-libunwind \ | 25 | test-libunwind \ |
26 | test-libunwind-debug-frame \ | ||
26 | test-on-exit \ | 27 | test-on-exit \ |
27 | test-stackprotector-all \ | 28 | test-stackprotector-all \ |
28 | test-stackprotector \ | 29 | test-stackprotector \ |
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 59e7a705e146..799865b60772 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
@@ -49,6 +49,10 @@ | |||
49 | # include "test-libunwind.c" | 49 | # include "test-libunwind.c" |
50 | #undef main | 50 | #undef main |
51 | 51 | ||
52 | #define main main_test_libunwind_debug_frame | ||
53 | # include "test-libunwind-debug-frame.c" | ||
54 | #undef main | ||
55 | |||
52 | #define main main_test_libaudit | 56 | #define main main_test_libaudit |
53 | # include "test-libaudit.c" | 57 | # include "test-libaudit.c" |
54 | #undef main | 58 | #undef main |
diff --git a/tools/perf/config/feature-checks/test-libunwind-debug-frame.c b/tools/perf/config/feature-checks/test-libunwind-debug-frame.c new file mode 100644 index 000000000000..0ef8087a104a --- /dev/null +++ b/tools/perf/config/feature-checks/test-libunwind-debug-frame.c | |||
@@ -0,0 +1,16 @@ | |||
1 | #include <libunwind.h> | ||
2 | #include <stdlib.h> | ||
3 | |||
4 | extern int | ||
5 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
6 | unw_word_t ip, unw_word_t segbase, | ||
7 | const char *obj_name, unw_word_t start, | ||
8 | unw_word_t end); | ||
9 | |||
10 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
11 | |||
12 | int main(void) | ||
13 | { | ||
14 | dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); | ||
15 | return 0; | ||
16 | } | ||
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 2f891f7e70bf..5390d0b8862a 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c | |||
@@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | |||
39 | 39 | ||
40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | 40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) |
41 | 41 | ||
42 | extern int | ||
43 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
44 | unw_word_t ip, | ||
45 | unw_word_t segbase, | ||
46 | const char *obj_name, unw_word_t start, | ||
47 | unw_word_t end); | ||
48 | |||
49 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
50 | |||
42 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | 51 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ |
43 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ | 52 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ |
44 | 53 | ||
@@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | |||
245 | return 0; | 254 | return 0; |
246 | } | 255 | } |
247 | 256 | ||
248 | static int read_unwind_spec(struct dso *dso, struct machine *machine, | 257 | static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, |
249 | u64 *table_data, u64 *segbase, u64 *fde_count) | 258 | u64 *table_data, u64 *segbase, |
259 | u64 *fde_count) | ||
250 | { | 260 | { |
251 | int ret = -EINVAL, fd; | 261 | int ret = -EINVAL, fd; |
252 | u64 offset; | 262 | u64 offset; |
@@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
255 | if (fd < 0) | 265 | if (fd < 0) |
256 | return -EINVAL; | 266 | return -EINVAL; |
257 | 267 | ||
268 | /* Check the .eh_frame section for unwinding info */ | ||
258 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 269 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
259 | close(fd); | 270 | close(fd); |
260 | 271 | ||
@@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
263 | table_data, segbase, | 274 | table_data, segbase, |
264 | fde_count); | 275 | fde_count); |
265 | 276 | ||
266 | /* TODO .debug_frame check if eh_frame_hdr fails */ | ||
267 | return ret; | 277 | return ret; |
268 | } | 278 | } |
269 | 279 | ||
280 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
281 | static int read_unwind_spec_debug_frame(struct dso *dso, | ||
282 | struct machine *machine, u64 *offset) | ||
283 | { | ||
284 | int fd = dso__data_fd(dso, machine); | ||
285 | |||
286 | if (fd < 0) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* Check the .debug_frame section for unwinding info */ | ||
290 | *offset = elf_section_offset(fd, ".debug_frame"); | ||
291 | close(fd); | ||
292 | |||
293 | if (*offset) | ||
294 | return 0; | ||
295 | |||
296 | return -EINVAL; | ||
297 | } | ||
298 | #endif | ||
299 | |||
270 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | 300 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) |
271 | { | 301 | { |
272 | struct addr_location al; | 302 | struct addr_location al; |
@@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | |||
291 | 321 | ||
292 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | 322 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); |
293 | 323 | ||
294 | if (read_unwind_spec(map->dso, ui->machine, | 324 | /* Check the .eh_frame section for unwinding info */ |
295 | &table_data, &segbase, &fde_count)) | 325 | if (!read_unwind_spec_eh_frame(map->dso, ui->machine, |
296 | return -EINVAL; | 326 | &table_data, &segbase, &fde_count)) { |
327 | memset(&di, 0, sizeof(di)); | ||
328 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
329 | di.start_ip = map->start; | ||
330 | di.end_ip = map->end; | ||
331 | di.u.rti.segbase = map->start + segbase; | ||
332 | di.u.rti.table_data = map->start + table_data; | ||
333 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
334 | / sizeof(unw_word_t); | ||
335 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
336 | need_unwind_info, arg); | ||
337 | } | ||
338 | |||
339 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
340 | /* Check the .debug_frame section for unwinding info */ | ||
341 | if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { | ||
342 | memset(&di, 0, sizeof(di)); | ||
343 | dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, | ||
344 | map->start, map->end); | ||
345 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
346 | need_unwind_info, arg); | ||
347 | } | ||
348 | #endif | ||
297 | 349 | ||
298 | memset(&di, 0, sizeof(di)); | 350 | return -EINVAL; |
299 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
300 | di.start_ip = map->start; | ||
301 | di.end_ip = map->end; | ||
302 | di.u.rti.segbase = map->start + segbase; | ||
303 | di.u.rti.table_data = map->start + table_data; | ||
304 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
305 | / sizeof(unw_word_t); | ||
306 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
307 | need_unwind_info, arg); | ||
308 | } | 351 | } |
309 | 352 | ||
310 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | 353 | static int access_fpreg(unw_addr_space_t __maybe_unused as, |