arm64: Improve DDB backtrace support

The existing implementation relies on each trap handler saving a normal
stack frame record, which is a waste of time and space when we're
already saving a trapframe to the stack. It's also wrong as it currently
saves LR not ELR.

Instead of patching it up, rewrite it based on the RISC-V implementation
with inspiration from the amd64 implementation for how to handle
vectored traps to provide an improved implementation. This includes
compressing the information down to one line like other architectures
rather than the highly-verbose old form that repeats itself by printing
LR and FP in one frame only to print them as PC and SP in the next. It
also includes printing out actually useful information about the traps
that occurred, though FAR is not saved in the trapframe so we cannot
print it (in general it can be clobbered between when the trap happened
and now), only ESR.

The AAPCS also allows the stack frame record to be located anywhere in
the frame, not just the top, so the caller's SP is not at a fixed offset
from the callee's FP like on almost all other architectures in
existence. This means there is no way to derive the caller's SP in the
unwinder, and so we have to drop that bit of (unused) state everywhere.

Reviewed by:	jhb, markj
Differential Revision:	https://reviews.freebsd.org/D28026
This commit is contained in:
Jessica Clarke 2021-02-01 14:15:57 +00:00
parent db46c0d0cb
commit 05985a7f80
7 changed files with 67 additions and 41 deletions

View File

@ -42,6 +42,13 @@ __FBSDID("$FreeBSD$");
#include <machine/armreg.h>
#include <machine/debug_monitor.h>
#include <machine/stack.h>
#include <machine/vmparam.h>
#define FRAME_NORMAL 0
#define FRAME_SYNC 1
#define FRAME_IRQ 2
#define FRAME_SERROR 3
#define FRAME_UNHANDLED 4
void
db_md_list_watchpoints()
@ -71,14 +78,10 @@ db_stack_trace_cmd(struct thread *td, struct unwind_state *frame)
const char *name;
db_expr_t value;
db_expr_t offset;
int frame_type;
while (1) {
uintptr_t pc = frame->pc;
if (!unwind_frame(td, frame))
break;
sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
sym = db_search_symbol(frame->pc, DB_STGY_ANY, &offset);
if (sym == C_DB_SYM_NULL) {
value = 0;
name = "(null)";
@ -89,12 +92,61 @@ db_stack_trace_cmd(struct thread *td, struct unwind_state *frame)
db_printsym(frame->pc, DB_STGY_PROC);
db_printf("\n");
db_printf("\t pc = 0x%016lx lr = 0x%016lx\n", pc,
frame->pc);
db_printf("\t sp = 0x%016lx fp = 0x%016lx\n", frame->sp,
frame->fp);
/* TODO: Show some more registers */
db_printf("\n");
if (strcmp(name, "handle_el0_sync") == 0 ||
strcmp(name, "handle_el1h_sync") == 0)
frame_type = FRAME_SYNC;
else if (strcmp(name, "handle_el0_irq") == 0 ||
strcmp(name, "handle_el1h_irq") == 0)
frame_type = FRAME_IRQ;
else if (strcmp(name, "handle_serror") == 0)
frame_type = FRAME_SERROR;
else if (strcmp(name, "handle_empty_exception") == 0)
frame_type = FRAME_UNHANDLED;
else
frame_type = FRAME_NORMAL;
if (frame_type != FRAME_NORMAL) {
struct trapframe *tf;
tf = (struct trapframe *)(uintptr_t)frame->fp - 1;
if (!kstack_contains(td, (vm_offset_t)tf,
sizeof(*tf))) {
db_printf("--- invalid trapframe %p\n", tf);
break;
}
switch (frame_type) {
case FRAME_SYNC:
db_printf("--- exception, esr %#x\n",
tf->tf_esr);
break;
case FRAME_IRQ:
db_printf("--- interrupt\n");
break;
case FRAME_SERROR:
db_printf("--- system error, esr %#x\n",
tf->tf_esr);
break;
case FRAME_UNHANDLED:
db_printf("--- unhandled exception, esr %#x\n",
tf->tf_esr);
break;
default:
__assert_unreachable();
break;
}
frame->fp = tf->tf_x[29];
frame->pc = tf->tf_elr;
if (!INKERNEL(frame->fp))
break;
} else {
if (strcmp(name, "fork_trampoline") == 0)
break;
if (!unwind_frame(td, frame))
break;
}
}
}
@ -107,7 +159,6 @@ db_trace_thread(struct thread *thr, int count)
if (thr != curthread) {
ctx = kdb_thr_ctx(thr);
frame.sp = (uintptr_t)ctx->pcb_sp;
frame.fp = (uintptr_t)ctx->pcb_x[29];
frame.pc = (uintptr_t)ctx->pcb_lr;
db_stack_trace_cmd(thr, &frame);
@ -120,11 +171,7 @@ void
db_trace_self(void)
{
struct unwind_state frame;
uintptr_t sp;
__asm __volatile("mov %0, sp" : "=&r" (sp));
frame.sp = sp;
frame.fp = (uintptr_t)__builtin_frame_address(0);
frame.pc = (uintptr_t)db_trace_self;
db_stack_trace_cmd(curthread, &frame);

View File

@ -38,8 +38,7 @@ __FBSDID("$FreeBSD$");
mov x18, sp
sub sp, sp, #128
.endif
sub sp, sp, #(TF_SIZE + 16)
stp x29, lr, [sp, #(TF_SIZE)]
sub sp, sp, #(TF_SIZE)
stp x28, x29, [sp, #(TF_X + 28 * 8)]
stp x26, x27, [sp, #(TF_X + 26 * 8)]
stp x24, x25, [sp, #(TF_X + 24 * 8)]
@ -132,7 +131,7 @@ __FBSDID("$FreeBSD$");
ldr x29, [sp, #(TF_X + 29 * 8)]
.endif
.if \el == 0
add sp, sp, #(TF_SIZE + 16)
add sp, sp, #(TF_SIZE)
.else
mov sp, x18
mrs x18, tpidr_el1

View File

@ -69,7 +69,6 @@ stack_save_td(struct stack *st, struct thread *td)
if (TD_IS_RUNNING(td))
return (EOPNOTSUPP);
frame.sp = td->td_pcb->pcb_sp;
frame.fp = td->td_pcb->pcb_x[29];
frame.pc = td->td_pcb->pcb_lr;
@ -81,11 +80,7 @@ void
stack_save(struct stack *st)
{
struct unwind_state frame;
uintptr_t sp;
__asm __volatile("mov %0, sp" : "=&r" (sp));
frame.sp = sp;
frame.fp = (uintptr_t)__builtin_frame_address(0);
frame.pc = (uintptr_t)stack_save;

View File

@ -45,7 +45,6 @@ unwind_frame(struct thread *td, struct unwind_state *frame)
if (!kstack_contains(td, fp, sizeof(uintptr_t) * 2))
return (false);
frame->sp = fp + sizeof(uintptr_t) * 2;
/* FP to previous frame (X29) */
frame->fp = ((uintptr_t *)fp)[0];
/* LR (X30) */

View File

@ -76,12 +76,8 @@ kcsan_md_unwind(void)
const char *symname;
#endif
struct unwind_state frame;
uintptr_t sp;
int nsym;
__asm __volatile("mov %0, sp" : "=&r" (sp));
frame.sp = sp;
frame.fp = (uintptr_t)__builtin_frame_address(0);
frame.pc = (uintptr_t)kcsan_md_unwind;
nsym = 0;

View File

@ -34,7 +34,6 @@
struct unwind_state {
uintptr_t fp;
uintptr_t sp;
uintptr_t pc;
};

View File

@ -70,7 +70,6 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
{
struct unwind_state state;
int scp_offset;
register_t sp;
int depth;
depth = 0;
@ -81,10 +80,7 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
aframes++;
__asm __volatile("mov %0, sp" : "=&r" (sp));
state.fp = (uintptr_t)__builtin_frame_address(0);
state.sp = sp;
state.pc = (uintptr_t)dtrace_getpcstack;
while (depth < pcstack_limit) {
@ -179,7 +175,7 @@ dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
{
proc_t *p = curproc;
struct trapframe *tf;
uintptr_t pc, sp, fp;
uintptr_t pc, fp;
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
int n;
@ -203,7 +199,6 @@ dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
return;
pc = tf->tf_elr;
sp = tf->tf_sp;
fp = tf->tf_x[29];
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
@ -267,17 +262,13 @@ dtrace_getstackdepth(int aframes)
{
struct unwind_state state;
int scp_offset;
register_t sp;
int depth;
bool done;
depth = 1;
done = false;
__asm __volatile("mov %0, sp" : "=&r" (sp));
state.fp = (uintptr_t)__builtin_frame_address(0);
state.sp = sp;
state.pc = (uintptr_t)dtrace_getstackdepth;
do {