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:
parent
db46c0d0cb
commit
05985a7f80
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) */
|
||||
|
@ -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;
|
||||
|
@ -34,7 +34,6 @@
|
||||
|
||||
struct unwind_state {
|
||||
uintptr_t fp;
|
||||
uintptr_t sp;
|
||||
uintptr_t pc;
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user