From 790fb4edf2081e491d98165792dbd74ca18be603 Mon Sep 17 00:00:00 2001 From: Fabien Thomas Date: Wed, 13 Jun 2012 06:38:25 +0000 Subject: [PATCH] Add ARM callchain support for hwpmc. Sponsored by: NETASQ MFC after: 3 days --- sys/arm/include/pmc_mdep.h | 14 +++-- sys/dev/hwpmc/hwpmc_arm.c | 104 ++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/sys/arm/include/pmc_mdep.h b/sys/arm/include/pmc_mdep.h index ab08d366448..6153df0f7eb 100644 --- a/sys/arm/include/pmc_mdep.h +++ b/sys/arm/include/pmc_mdep.h @@ -50,9 +50,17 @@ union pmc_md_pmc { struct pmc_md_xscale_pmc pm_xscale; }; -#define PMC_TRAPFRAME_TO_PC(TF) ((TF)->tf_pc) -#define PMC_TRAPFRAME_TO_FP(TF) ((TF)->tf_usr_lr) -#define PMC_TRAPFRAME_TO_SP(TF) ((TF)->tf_usr_sp) +#define PMC_IN_KERNEL_STACK(S,START,END) \ + ((S) >= (START) && (S) < (END)) +#define PMC_IN_KERNEL(va) (((va) >= USRSTACK) && \ + ((va) < VM_MAX_KERNEL_ADDRESS)) + +#define PMC_IN_USERSPACE(va) ((va) <= VM_MAXUSER_ADDRESS) + +#define PMC_TRAPFRAME_TO_PC(TF) ((TF)->tf_pc) +#define PMC_TRAPFRAME_TO_FP(TF) ((TF)->tf_r11) +#define PMC_TRAPFRAME_TO_SVC_SP(TF) ((TF)->tf_svc_sp) +#define PMC_TRAPFRAME_TO_USR_SP(TF) ((TF)->tf_usr_sp) /* Build a fake kernel trapframe from current instruction pointer. */ #define PMC_FAKE_TRAPFRAME(TF) \ diff --git a/sys/dev/hwpmc/hwpmc_arm.c b/sys/dev/hwpmc/hwpmc_arm.c index fc761654218..c2c24c226b3 100644 --- a/sys/dev/hwpmc/hwpmc_arm.c +++ b/sys/dev/hwpmc/hwpmc_arm.c @@ -30,10 +30,16 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include -#include +#include #include +#include + +#include +#include +#include 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; - return pmc_save_callchain(cc, maxsamples, tf); + 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); + + 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; - return pmc_save_callchain(cc, maxsamples, tf); + 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 (count); }