]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
arm: perf: factor out callchain code
authorMark Rutland <mark.rutland@arm.com>
Mon, 29 Sep 2014 16:15:32 +0000 (17:15 +0100)
committerWill Deacon <will.deacon@arm.com>
Thu, 30 Oct 2014 12:16:58 +0000 (12:16 +0000)
The ARM callchain handling code is currently bundled with the ARM PMU
management code, despite the two having no dependency on each other.
This bundling has the unfortunate property of making callchain handling
depend on CONFIG_HW_PERF_EVENTS, even though the callchain handling
could be applied to software events in the absence of PMU hardware
support.

This patch separates the two, placing the callchain handling in
perf_callchain.c and making it depend on CONFIG_PERF_EVENTS rather than
CONFIG_HW_PERF_EVENTS, enabling callchain recording on kernels built
without hardware perf event support.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm/include/asm/perf_event.h
arch/arm/kernel/Makefile
arch/arm/kernel/perf_callchain.c [new file with mode: 0644]
arch/arm/kernel/perf_event.c

index c3a83691af8eda239f00df0a46b8839044c9e65d..d9cf138fd7d455624e0e823d9ed136fd329716a2 100644 (file)
@@ -12,7 +12,7 @@
 #ifndef __ARM_PERF_EVENT_H__
 #define __ARM_PERF_EVENT_H__
 
-#ifdef CONFIG_HW_PERF_EVENTS
+#ifdef CONFIG_PERF_EVENTS
 struct pt_regs;
 extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
 extern unsigned long perf_misc_flags(struct pt_regs *regs);
index 38ddd9f83d0e586289c56bdbf1e38a6a977ff1a7..8dcbed5016ac2c3d18f3b3bbc2c30030c5c0a87e 100644 (file)
@@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK)      += xscale-cp0.o
 obj-$(CONFIG_CPU_PJ4)          += pj4-cp0.o
 obj-$(CONFIG_CPU_PJ4B)         += pj4-cp0.o
 obj-$(CONFIG_IWMMXT)           += iwmmxt.o
-obj-$(CONFIG_PERF_EVENTS)      += perf_regs.o
+obj-$(CONFIG_PERF_EVENTS)      += perf_regs.o perf_callchain.o
 obj-$(CONFIG_HW_PERF_EVENTS)   += perf_event.o perf_event_cpu.o
 AFLAGS_iwmmxt.o                        := -Wa,-mcpu=iwmmxt
 obj-$(CONFIG_ARM_CPU_TOPOLOGY)  += topology.o
diff --git a/arch/arm/kernel/perf_callchain.c b/arch/arm/kernel/perf_callchain.c
new file mode 100644 (file)
index 0000000..4e02ae5
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * ARM callchain support
+ *
+ * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
+ * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
+ *
+ * This code is based on the ARM OProfile backtrace code.
+ */
+#include <linux/perf_event.h>
+#include <linux/uaccess.h>
+
+#include <asm/stacktrace.h>
+
+/*
+ * The registers we're interested in are at the end of the variable
+ * length saved register structure. The fp points at the end of this
+ * structure so the address of this struct is:
+ * (struct frame_tail *)(xxx->fp)-1
+ *
+ * This code has been adapted from the ARM OProfile support.
+ */
+struct frame_tail {
+       struct frame_tail __user *fp;
+       unsigned long sp;
+       unsigned long lr;
+} __attribute__((packed));
+
+/*
+ * Get the return address for a single stackframe and return a pointer to the
+ * next frame tail.
+ */
+static struct frame_tail __user *
+user_backtrace(struct frame_tail __user *tail,
+              struct perf_callchain_entry *entry)
+{
+       struct frame_tail buftail;
+       unsigned long err;
+
+       if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
+               return NULL;
+
+       pagefault_disable();
+       err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
+       pagefault_enable();
+
+       if (err)
+               return NULL;
+
+       perf_callchain_store(entry, buftail.lr);
+
+       /*
+        * Frame pointers should strictly progress back up the stack
+        * (towards higher addresses).
+        */
+       if (tail + 1 >= buftail.fp)
+               return NULL;
+
+       return buftail.fp - 1;
+}
+
+void
+perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
+{
+       struct frame_tail __user *tail;
+
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               /* We don't support guest os callchain now */
+               return;
+       }
+
+       perf_callchain_store(entry, regs->ARM_pc);
+
+       if (!current->mm)
+               return;
+
+       tail = (struct frame_tail __user *)regs->ARM_fp - 1;
+
+       while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
+              tail && !((unsigned long)tail & 0x3))
+               tail = user_backtrace(tail, entry);
+}
+
+/*
+ * Gets called by walk_stackframe() for every stackframe. This will be called
+ * whist unwinding the stackframe and is like a subroutine return so we use
+ * the PC.
+ */
+static int
+callchain_trace(struct stackframe *fr,
+               void *data)
+{
+       struct perf_callchain_entry *entry = data;
+       perf_callchain_store(entry, fr->pc);
+       return 0;
+}
+
+void
+perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
+{
+       struct stackframe fr;
+
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               /* We don't support guest os callchain now */
+               return;
+       }
+
+       arm_get_current_stackframe(regs, &fr);
+       walk_stackframe(&fr, callchain_trace, entry);
+}
+
+unsigned long perf_instruction_pointer(struct pt_regs *regs)
+{
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
+               return perf_guest_cbs->get_guest_ip();
+
+       return instruction_pointer(regs);
+}
+
+unsigned long perf_misc_flags(struct pt_regs *regs)
+{
+       int misc = 0;
+
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               if (perf_guest_cbs->is_user_mode())
+                       misc |= PERF_RECORD_MISC_GUEST_USER;
+               else
+                       misc |= PERF_RECORD_MISC_GUEST_KERNEL;
+       } else {
+               if (user_mode(regs))
+                       misc |= PERF_RECORD_MISC_USER;
+               else
+                       misc |= PERF_RECORD_MISC_KERNEL;
+       }
+
+       return misc;
+}
index 266cba46db3efe4f92c554320ac9668d098cdf7f..ae96b986d50f480a3d17615a9dc9d9c3cd86d6c8 100644 (file)
@@ -7,21 +7,18 @@
  * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
  *
  * This code is based on the sparc64 perf event code, which is in turn based
- * on the x86 code. Callchain code is based on the ARM OProfile backtrace
- * code.
+ * on the x86 code.
  */
 #define pr_fmt(fmt) "hw perfevents: " fmt
 
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/uaccess.h>
 #include <linux/irq.h>
 #include <linux/irqdesc.h>
 
 #include <asm/irq_regs.h>
 #include <asm/pmu.h>
-#include <asm/stacktrace.h>
 
 static int
 armpmu_map_cache_event(const unsigned (*cache_map)
@@ -533,130 +530,3 @@ int armpmu_register(struct arm_pmu *armpmu, int type)
        return perf_pmu_register(&armpmu->pmu, armpmu->name, type);
 }
 
-/*
- * Callchain handling code.
- */
-
-/*
- * The registers we're interested in are at the end of the variable
- * length saved register structure. The fp points at the end of this
- * structure so the address of this struct is:
- * (struct frame_tail *)(xxx->fp)-1
- *
- * This code has been adapted from the ARM OProfile support.
- */
-struct frame_tail {
-       struct frame_tail __user *fp;
-       unsigned long sp;
-       unsigned long lr;
-} __attribute__((packed));
-
-/*
- * Get the return address for a single stackframe and return a pointer to the
- * next frame tail.
- */
-static struct frame_tail __user *
-user_backtrace(struct frame_tail __user *tail,
-              struct perf_callchain_entry *entry)
-{
-       struct frame_tail buftail;
-       unsigned long err;
-
-       if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
-               return NULL;
-
-       pagefault_disable();
-       err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
-       pagefault_enable();
-
-       if (err)
-               return NULL;
-
-       perf_callchain_store(entry, buftail.lr);
-
-       /*
-        * Frame pointers should strictly progress back up the stack
-        * (towards higher addresses).
-        */
-       if (tail + 1 >= buftail.fp)
-               return NULL;
-
-       return buftail.fp - 1;
-}
-
-void
-perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
-{
-       struct frame_tail __user *tail;
-
-       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
-               /* We don't support guest os callchain now */
-               return;
-       }
-
-       perf_callchain_store(entry, regs->ARM_pc);
-
-       if (!current->mm)
-               return;
-
-       tail = (struct frame_tail __user *)regs->ARM_fp - 1;
-
-       while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
-              tail && !((unsigned long)tail & 0x3))
-               tail = user_backtrace(tail, entry);
-}
-
-/*
- * Gets called by walk_stackframe() for every stackframe. This will be called
- * whist unwinding the stackframe and is like a subroutine return so we use
- * the PC.
- */
-static int
-callchain_trace(struct stackframe *fr,
-               void *data)
-{
-       struct perf_callchain_entry *entry = data;
-       perf_callchain_store(entry, fr->pc);
-       return 0;
-}
-
-void
-perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
-{
-       struct stackframe fr;
-
-       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
-               /* We don't support guest os callchain now */
-               return;
-       }
-
-       arm_get_current_stackframe(regs, &fr);
-       walk_stackframe(&fr, callchain_trace, entry);
-}
-
-unsigned long perf_instruction_pointer(struct pt_regs *regs)
-{
-       if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
-               return perf_guest_cbs->get_guest_ip();
-
-       return instruction_pointer(regs);
-}
-
-unsigned long perf_misc_flags(struct pt_regs *regs)
-{
-       int misc = 0;
-
-       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
-               if (perf_guest_cbs->is_user_mode())
-                       misc |= PERF_RECORD_MISC_GUEST_USER;
-               else
-                       misc |= PERF_RECORD_MISC_GUEST_KERNEL;
-       } else {
-               if (user_mode(regs))
-                       misc |= PERF_RECORD_MISC_USER;
-               else
-                       misc |= PERF_RECORD_MISC_KERNEL;
-       }
-
-       return misc;
-}