summaryrefslogtreecommitdiffstats
path: root/sys/dev/hwpmc
diff options
context:
space:
mode:
authorfabient <fabient@FreeBSD.org>2012-06-13 06:38:25 +0000
committerfabient <fabient@FreeBSD.org>2012-06-13 06:38:25 +0000
commit028605e4f68c0dcc6e9f41e223bbabe022eeedb0 (patch)
treefba50100a48a53f0809457204bf864e89ceeabad /sys/dev/hwpmc
parent045b5b971c5ebb52880eb30cac10975ec2cfa974 (diff)
downloadFreeBSD-src-028605e4f68c0dcc6e9f41e223bbabe022eeedb0.zip
FreeBSD-src-028605e4f68c0dcc6e9f41e223bbabe022eeedb0.tar.gz
Add ARM callchain support for hwpmc.
Sponsored by: NETASQ MFC after: 3 days
Diffstat (limited to 'sys/dev/hwpmc')
-rw-r--r--sys/dev/hwpmc/hwpmc_arm.c104
1 files changed, 92 insertions, 12 deletions
diff --git a/sys/dev/hwpmc/hwpmc_arm.c b/sys/dev/hwpmc/hwpmc_arm.c
index fc76165..c2c24c2 100644
--- a/sys/dev/hwpmc/hwpmc_arm.c
+++ b/sys/dev/hwpmc/hwpmc_arm.c
@@ -30,10 +30,16 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
+#include <sys/proc.h>
#include <sys/systm.h>
-#include <machine/pmc_mdep.h>
+#include <machine/cpu.h>
#include <machine/md_var.h>
+#include <machine/pmc_mdep.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
struct pmc_mdep *
pmc_md_initialize()
@@ -58,27 +64,101 @@ pmc_md_finalize(struct pmc_mdep *md)
#endif
}
-static int
-pmc_save_callchain(uintptr_t *cc, int maxsamples,
- struct trapframe *tf)
-{
-
- *cc = PMC_TRAPFRAME_TO_PC(tf);
- return (1);
-}
-
int
pmc_save_kernel_callchain(uintptr_t *cc, int maxsamples,
struct trapframe *tf)
{
+ uintptr_t pc, r, stackstart, stackend, fp;
+ struct thread *td;
+ int count;
+
+ KASSERT(TRAPF_USERMODE(tf) == 0,("[arm,%d] not a kernel backtrace",
+ __LINE__));
+
+ pc = PMC_TRAPFRAME_TO_PC(tf);
+ *cc++ = pc;
+
+ if ((td = curthread) == NULL)
+ return (1);
+
+ if (maxsamples <= 1)
+ return (1);
+
+ stackstart = (uintptr_t) td->td_kstack;
+ stackend = (uintptr_t) td->td_kstack + td->td_kstack_pages * PAGE_SIZE;
+ fp = PMC_TRAPFRAME_TO_FP(tf);
- return pmc_save_callchain(cc, maxsamples, tf);
+ if (!PMC_IN_KERNEL(pc) ||
+ !PMC_IN_KERNEL_STACK(fp, stackstart, stackend))
+ return (1);
+
+ for (count = 1; count < maxsamples; count++) {
+ /* Use saved lr as pc. */
+ r = fp - sizeof(uintptr_t);
+ if (!PMC_IN_KERNEL_STACK(r, stackstart, stackend))
+ break;
+ pc = *(uintptr_t *)r;
+ if (!PMC_IN_KERNEL(pc))
+ break;
+
+ *cc++ = pc;
+
+ /* Switch to next frame up */
+ r = fp - 3 * sizeof(uintptr_t);
+ if (!PMC_IN_KERNEL_STACK(r, stackstart, stackend))
+ break;
+ fp = *(uintptr_t *)r;
+ if (!PMC_IN_KERNEL_STACK(fp, stackstart, stackend))
+ break;
+ }
+
+ return (count);
}
int
pmc_save_user_callchain(uintptr_t *cc, int maxsamples,
struct trapframe *tf)
{
+ uintptr_t pc, r, oldfp, fp;
+ struct thread *td;
+ int count;
+
+ KASSERT(TRAPF_USERMODE(tf), ("[x86,%d] Not a user trap frame tf=%p",
+ __LINE__, (void *) tf));
+
+ pc = PMC_TRAPFRAME_TO_PC(tf);
+ *cc++ = pc;
+
+ if ((td = curthread) == NULL)
+ return (1);
+
+ if (maxsamples <= 1)
+ return (1);
+
+ oldfp = fp = PMC_TRAPFRAME_TO_FP(tf);
+
+ if (!PMC_IN_USERSPACE(pc) ||
+ !PMC_IN_USERSPACE(fp))
+ return (1);
+
+ for (count = 1; count < maxsamples; count++) {
+ /* Use saved lr as pc. */
+ r = fp - sizeof(uintptr_t);
+ if (copyin((void *)r, &pc, sizeof(pc)) != 0)
+ break;
+ if (!PMC_IN_USERSPACE(pc))
+ break;
+
+ *cc++ = pc;
+
+ /* Switch to next frame up */
+ oldfp = fp;
+ r = fp - 3 * sizeof(uintptr_t);
+ if (copyin((void *)r, &fp, sizeof(fp)) != 0)
+ break;
+ if (fp < oldfp || !PMC_IN_USERSPACE(fp))
+ break;
+ }
- return pmc_save_callchain(cc, maxsamples, tf);
+ return (count);
}
OpenPOWER on IntegriCloud