mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
arm64: Add USER_STACKTRACE support
Currently, userstacktrace is unsupported for ftrace and uprobe tracers on arm64. This patch uses the perf_callchain_user() code as blueprint to implement the arch_stack_walk_user() which add userstacktrace support on arm64. Meanwhile, we can use arch_stack_walk_user() to simplify the implementation of perf_callchain_user(). This patch is tested pass with ftrace, uprobe and perf tracers profiling userstacktrace cases. Tested-by: chenqiwu <qiwu.chen@transsion.com> Signed-off-by: chenqiwu <qiwu.chen@transsion.com> Link: https://lore.kernel.org/r/20231219022229.10230-1-qiwu.chen@transsion.com Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
582c1aeee0
commit
410e471f87
3 changed files with 125 additions and 114 deletions
|
@ -258,6 +258,7 @@ config ARM64
|
||||||
select TRACE_IRQFLAGS_SUPPORT
|
select TRACE_IRQFLAGS_SUPPORT
|
||||||
select TRACE_IRQFLAGS_NMI_SUPPORT
|
select TRACE_IRQFLAGS_NMI_SUPPORT
|
||||||
select HAVE_SOFTIRQ_ON_OWN_STACK
|
select HAVE_SOFTIRQ_ON_OWN_STACK
|
||||||
|
select USER_STACKTRACE_SUPPORT
|
||||||
help
|
help
|
||||||
ARM 64-bit (AArch64) Linux support.
|
ARM 64-bit (AArch64) Linux support.
|
||||||
|
|
||||||
|
|
|
@ -10,95 +10,13 @@
|
||||||
|
|
||||||
#include <asm/pointer_auth.h>
|
#include <asm/pointer_auth.h>
|
||||||
|
|
||||||
struct frame_tail {
|
static bool callchain_trace(void *data, unsigned long pc)
|
||||||
struct frame_tail __user *fp;
|
|
||||||
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_ctx *entry)
|
|
||||||
{
|
{
|
||||||
struct frame_tail buftail;
|
struct perf_callchain_entry_ctx *entry = data;
|
||||||
unsigned long err;
|
|
||||||
unsigned long lr;
|
|
||||||
|
|
||||||
/* Also check accessibility of one struct frame_tail beyond */
|
return perf_callchain_store(entry, pc) == 0;
|
||||||
if (!access_ok(tail, sizeof(buftail)))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
pagefault_disable();
|
|
||||||
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
|
|
||||||
pagefault_enable();
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
lr = ptrauth_strip_user_insn_pac(buftail.lr);
|
|
||||||
|
|
||||||
perf_callchain_store(entry, lr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Frame pointers should strictly progress back up the stack
|
|
||||||
* (towards higher addresses).
|
|
||||||
*/
|
|
||||||
if (tail >= buftail.fp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return buftail.fp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
/*
|
|
||||||
* 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 compat_frame_tail *)(xxx->fp)-1
|
|
||||||
*
|
|
||||||
* This code has been adapted from the ARM OProfile support.
|
|
||||||
*/
|
|
||||||
struct compat_frame_tail {
|
|
||||||
compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
|
|
||||||
u32 sp;
|
|
||||||
u32 lr;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
static struct compat_frame_tail __user *
|
|
||||||
compat_user_backtrace(struct compat_frame_tail __user *tail,
|
|
||||||
struct perf_callchain_entry_ctx *entry)
|
|
||||||
{
|
|
||||||
struct compat_frame_tail buftail;
|
|
||||||
unsigned long err;
|
|
||||||
|
|
||||||
/* Also check accessibility of one struct frame_tail beyond */
|
|
||||||
if (!access_ok(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 >= (struct compat_frame_tail __user *)
|
|
||||||
compat_ptr(buftail.fp))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_COMPAT */
|
|
||||||
|
|
||||||
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
|
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
@ -107,35 +25,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
perf_callchain_store(entry, regs->pc);
|
arch_stack_walk_user(callchain_trace, entry, regs);
|
||||||
|
|
||||||
if (!compat_user_mode(regs)) {
|
|
||||||
/* AARCH64 mode */
|
|
||||||
struct frame_tail __user *tail;
|
|
||||||
|
|
||||||
tail = (struct frame_tail __user *)regs->regs[29];
|
|
||||||
|
|
||||||
while (entry->nr < entry->max_stack &&
|
|
||||||
tail && !((unsigned long)tail & 0x7))
|
|
||||||
tail = user_backtrace(tail, entry);
|
|
||||||
} else {
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
/* AARCH32 compat mode */
|
|
||||||
struct compat_frame_tail __user *tail;
|
|
||||||
|
|
||||||
tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
|
|
||||||
|
|
||||||
while ((entry->nr < entry->max_stack) &&
|
|
||||||
tail && !((unsigned long)tail & 0x3))
|
|
||||||
tail = compat_user_backtrace(tail, entry);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool callchain_trace(void *data, unsigned long pc)
|
|
||||||
{
|
|
||||||
struct perf_callchain_entry_ctx *entry = data;
|
|
||||||
return perf_callchain_store(entry, pc) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
|
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
|
||||||
|
|
|
@ -324,3 +324,123 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
|
||||||
dump_backtrace(NULL, tsk, loglvl);
|
dump_backtrace(NULL, tsk, loglvl);
|
||||||
barrier();
|
barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The struct defined for userspace stack frame in AARCH64 mode.
|
||||||
|
*/
|
||||||
|
struct frame_tail {
|
||||||
|
struct frame_tail __user *fp;
|
||||||
|
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 *
|
||||||
|
unwind_user_frame(struct frame_tail __user *tail, void *cookie,
|
||||||
|
stack_trace_consume_fn consume_entry)
|
||||||
|
{
|
||||||
|
struct frame_tail buftail;
|
||||||
|
unsigned long err;
|
||||||
|
unsigned long lr;
|
||||||
|
|
||||||
|
/* Also check accessibility of one struct frame_tail beyond */
|
||||||
|
if (!access_ok(tail, sizeof(buftail)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pagefault_disable();
|
||||||
|
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
|
||||||
|
pagefault_enable();
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
lr = ptrauth_strip_user_insn_pac(buftail.lr);
|
||||||
|
|
||||||
|
if (!consume_entry(cookie, lr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frame pointers should strictly progress back up the stack
|
||||||
|
* (towards higher addresses).
|
||||||
|
*/
|
||||||
|
if (tail >= buftail.fp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return buftail.fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
/*
|
||||||
|
* 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 compat_frame_tail *)(xxx->fp)-1
|
||||||
|
*
|
||||||
|
* This code has been adapted from the ARM OProfile support.
|
||||||
|
*/
|
||||||
|
struct compat_frame_tail {
|
||||||
|
compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
|
||||||
|
u32 sp;
|
||||||
|
u32 lr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static struct compat_frame_tail __user *
|
||||||
|
unwind_compat_user_frame(struct compat_frame_tail __user *tail, void *cookie,
|
||||||
|
stack_trace_consume_fn consume_entry)
|
||||||
|
{
|
||||||
|
struct compat_frame_tail buftail;
|
||||||
|
unsigned long err;
|
||||||
|
|
||||||
|
/* Also check accessibility of one struct frame_tail beyond */
|
||||||
|
if (!access_ok(tail, sizeof(buftail)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pagefault_disable();
|
||||||
|
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
|
||||||
|
pagefault_enable();
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!consume_entry(cookie, buftail.lr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frame pointers should strictly progress back up the stack
|
||||||
|
* (towards higher addresses).
|
||||||
|
*/
|
||||||
|
if (tail + 1 >= (struct compat_frame_tail __user *)
|
||||||
|
compat_ptr(buftail.fp))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
|
|
||||||
|
void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
|
||||||
|
const struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (!consume_entry(cookie, regs->pc))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!compat_user_mode(regs)) {
|
||||||
|
/* AARCH64 mode */
|
||||||
|
struct frame_tail __user *tail;
|
||||||
|
|
||||||
|
tail = (struct frame_tail __user *)regs->regs[29];
|
||||||
|
while (tail && !((unsigned long)tail & 0x7))
|
||||||
|
tail = unwind_user_frame(tail, cookie, consume_entry);
|
||||||
|
} else {
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
/* AARCH32 compat mode */
|
||||||
|
struct compat_frame_tail __user *tail;
|
||||||
|
|
||||||
|
tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
|
||||||
|
while (tail && !((unsigned long)tail & 0x3))
|
||||||
|
tail = unwind_compat_user_frame(tail, cookie, consume_entry);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue