aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/arch/arm/Makefile3
-rw-r--r--tools/perf/arch/arm/include/perf_regs.h54
-rw-r--r--tools/perf/arch/arm/util/unwind.c48
-rw-r--r--tools/perf/config/Makefile14
-rw-r--r--tools/perf/config/feature-checks/Makefile1
-rw-r--r--tools/perf/config/feature-checks/test-all.c4
-rw-r--r--tools/perf/config/feature-checks/test-libunwind-debug-frame.c16
-rw-r--r--tools/perf/util/unwind.c75
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
2PERF_HAVE_DWARF_REGS := 1 2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o 3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif 4endif
5ifndef NO_LIBUNWIND
6LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
7endif
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
12static 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
7int 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
33endif 33endif
34ifeq ($(ARCH),arm)
35 NO_PERF_REGS := 0
36 LIBUNWIND_LIBS = -lunwind -lunwind-arm
37endif
34 38
35ifeq ($(NO_PERF_REGS),0) 39ifeq ($(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
306endif # NO_LIBELF 310endif # NO_LIBELF
307 311
308# There's only x86 (both 32 and 64) support for CFI unwind so far 312ifeq ($(LIBUNWIND_LIBS),)
309ifneq ($(ARCH),x86)
310 NO_LIBUNWIND := 1 313 NO_LIBUNWIND := 1
311endif 314endif
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
328endif 336endif
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
4extern int
5UNW_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
12int 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
42extern int
43UNW_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
248static int read_unwind_spec(struct dso *dso, struct machine *machine, 257static 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
281static 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
270static struct map *find_map(unw_word_t ip, struct unwind_info *ui) 300static 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
310static int access_fpreg(unw_addr_space_t __maybe_unused as, 353static int access_fpreg(unw_addr_space_t __maybe_unused as,