aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/include/uapi/asm/Kbuild1
-rw-r--r--arch/arm/include/uapi/asm/perf_regs.h23
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/perf_event.c3
-rw-r--r--arch/arm/kernel/perf_regs.c30
-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/Makefile13
-rw-r--r--tools/perf/config/feature-tests.mak21
-rw-r--r--tools/perf/util/unwind.c75
12 files changed, 252 insertions, 22 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 22efc5d9c952..3917dd4f270c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -51,6 +51,8 @@ config ARM
51 select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND 51 select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND
52 select HAVE_OPROFILE if (HAVE_PERF_EVENTS) 52 select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
53 select HAVE_PERF_EVENTS 53 select HAVE_PERF_EVENTS
54 select HAVE_PERF_REGS
55 select HAVE_PERF_USER_STACK_DUMP
54 select HAVE_REGS_AND_STACK_ACCESS_API 56 select HAVE_REGS_AND_STACK_ACCESS_API
55 select HAVE_SYSCALL_TRACEPOINTS 57 select HAVE_SYSCALL_TRACEPOINTS
56 select HAVE_UID16 58 select HAVE_UID16
diff --git a/arch/arm/include/uapi/asm/Kbuild b/arch/arm/include/uapi/asm/Kbuild
index 18d76fd5a2af..70a1c9da30ca 100644
--- a/arch/arm/include/uapi/asm/Kbuild
+++ b/arch/arm/include/uapi/asm/Kbuild
@@ -7,6 +7,7 @@ header-y += hwcap.h
7header-y += ioctls.h 7header-y += ioctls.h
8header-y += kvm_para.h 8header-y += kvm_para.h
9header-y += mman.h 9header-y += mman.h
10header-y += perf_regs.h
10header-y += posix_types.h 11header-y += posix_types.h
11header-y += ptrace.h 12header-y += ptrace.h
12header-y += setup.h 13header-y += setup.h
diff --git a/arch/arm/include/uapi/asm/perf_regs.h b/arch/arm/include/uapi/asm/perf_regs.h
new file mode 100644
index 000000000000..ce59448458b2
--- /dev/null
+++ b/arch/arm/include/uapi/asm/perf_regs.h
@@ -0,0 +1,23 @@
1#ifndef _ASM_ARM_PERF_REGS_H
2#define _ASM_ARM_PERF_REGS_H
3
4enum perf_event_arm_regs {
5 PERF_REG_ARM_R0,
6 PERF_REG_ARM_R1,
7 PERF_REG_ARM_R2,
8 PERF_REG_ARM_R3,
9 PERF_REG_ARM_R4,
10 PERF_REG_ARM_R5,
11 PERF_REG_ARM_R6,
12 PERF_REG_ARM_R7,
13 PERF_REG_ARM_R8,
14 PERF_REG_ARM_R9,
15 PERF_REG_ARM_R10,
16 PERF_REG_ARM_FP,
17 PERF_REG_ARM_IP,
18 PERF_REG_ARM_SP,
19 PERF_REG_ARM_LR,
20 PERF_REG_ARM_PC,
21 PERF_REG_ARM_MAX,
22};
23#endif /* _ASM_ARM_PERF_REGS_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 5140df5f23aa..9b818ca3610b 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
78obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o 78obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
79obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o 79obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
80obj-$(CONFIG_IWMMXT) += iwmmxt.o 80obj-$(CONFIG_IWMMXT) += iwmmxt.o
81obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
81obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o 82obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
82AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt 83AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
83obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o 84obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index e186ee1e63f6..bc3f2efa0d86 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -256,12 +256,11 @@ validate_event(struct pmu_hw_events *hw_events,
256 struct perf_event *event) 256 struct perf_event *event)
257{ 257{
258 struct arm_pmu *armpmu = to_arm_pmu(event->pmu); 258 struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
259 struct pmu *leader_pmu = event->group_leader->pmu;
260 259
261 if (is_software_event(event)) 260 if (is_software_event(event))
262 return 1; 261 return 1;
263 262
264 if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF) 263 if (event->state < PERF_EVENT_STATE_OFF)
265 return 1; 264 return 1;
266 265
267 if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) 266 if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c
new file mode 100644
index 000000000000..6e4379c67cbc
--- /dev/null
+++ b/arch/arm/kernel/perf_regs.c
@@ -0,0 +1,30 @@
1
2#include <linux/errno.h>
3#include <linux/kernel.h>
4#include <linux/perf_event.h>
5#include <linux/bug.h>
6#include <asm/perf_regs.h>
7#include <asm/ptrace.h>
8
9u64 perf_reg_value(struct pt_regs *regs, int idx)
10{
11 if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM_MAX))
12 return 0;
13
14 return regs->uregs[idx];
15}
16
17#define REG_RESERVED (~((1ULL << PERF_REG_ARM_MAX) - 1))
18
19int perf_reg_validate(u64 mask)
20{
21 if (!mask || mask & REG_RESERVED)
22 return -EINVAL;
23
24 return 0;
25}
26
27u64 perf_reg_abi(struct task_struct *task)
28{
29 return PERF_SAMPLE_REGS_ABI_32;
30}
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 5f6f9b3271bb..75b93d7f7860 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -29,6 +29,10 @@ ifeq ($(ARCH),x86_64)
29 NO_PERF_REGS := 0 29 NO_PERF_REGS := 0
30 LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 30 LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
31endif 31endif
32ifeq ($(ARCH),arm)
33 NO_PERF_REGS := 0
34 LIBUNWIND_LIBS = -lunwind -lunwind-arm
35endif
32 36
33ifeq ($(NO_PERF_REGS),0) 37ifeq ($(NO_PERF_REGS),0)
34 CFLAGS += -DHAVE_PERF_REGS 38 CFLAGS += -DHAVE_PERF_REGS
@@ -208,8 +212,7 @@ ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y)
208endif # try-cc 212endif # try-cc
209endif # NO_LIBELF 213endif # NO_LIBELF
210 214
211# There's only x86 (both 32 and 64) support for CFI unwind so far 215ifeq ($(LIBUNWIND_LIBS),)
212ifneq ($(ARCH),x86)
213 NO_LIBUNWIND := 1 216 NO_LIBUNWIND := 1
214endif 217endif
215 218
@@ -223,9 +226,13 @@ endif
223 226
224FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) 227FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
225ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) 228ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y)
226 msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); 229 msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1);
227 NO_LIBUNWIND := 1 230 NO_LIBUNWIND := 1
228endif # Libunwind support 231endif # Libunwind support
232ifneq ($(call try-cc,$(SOURCE_LIBUNWIND_DEBUG_FRAME),$(FLAGS_UNWIND),libunwind debug_frame),y)
233 msg := $(warning No debug_frame support found in libunwind);
234CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
235endif # debug_frame support in libunwind
229endif # NO_LIBUNWIND 236endif # NO_LIBUNWIND
230 237
231ifndef NO_LIBUNWIND 238ifndef NO_LIBUNWIND
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak
index d5a8dd44945f..40b21c0bc10a 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -185,7 +185,6 @@ extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
185 unw_proc_info_t *pi, 185 unw_proc_info_t *pi,
186 int need_unwind_info, void *arg); 186 int need_unwind_info, void *arg);
187 187
188
189#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) 188#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
190 189
191int main(void) 190int main(void)
@@ -197,6 +196,26 @@ int main(void)
197 return 0; 196 return 0;
198} 197}
199endef 198endef
199
200define SOURCE_LIBUNWIND_DEBUG_FRAME
201#include <libunwind.h>
202#include <stdlib.h>
203
204extern int
205UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
206 unw_word_t ip, unw_word_t segbase,
207 const char *obj_name, unw_word_t start,
208 unw_word_t end);
209
210#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
211
212int main(void)
213{
214 dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
215 return 0;
216}
217endef
218
200endif 219endif
201 220
202ifndef NO_BACKTRACE 221ifndef NO_BACKTRACE
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,