summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-11-12 22:45:51 +0000
committerjhb <jhb@FreeBSD.org>2015-11-12 22:45:51 +0000
commit2285630285e9d969da6a43fe0dbb58bb71fff2f6 (patch)
tree4c6c11e1fed1ad4f8cca85f9135fadae35c74d91 /sys/amd64
parent5b5d335fed990b7c6249d5063886fa5990382ffa (diff)
downloadFreeBSD-src-2285630285e9d969da6a43fe0dbb58bb71fff2f6.zip
FreeBSD-src-2285630285e9d969da6a43fe0dbb58bb71fff2f6.tar.gz
MFC 285773,285775,285776:
Various fixes for stack unwinding in DDB on x86. 285773: Remove some dead code from DDB's amd64 stack unwinder. The amd64 port copied some code from i386 to fetch function arguments and display them in backtraces. However, it was commented out and can't easily be implemented since the function arguments are passed in registers rather than on the stack in amd64. Remove it in preparation for some bug fixes in this area. 285775: Improve stack unwinding on i386 and amd64 after an IP fault. If we can't find a symbol corresponding to the faulting instruction, assume that the previously-executed function is a call and attempt to find the calling function using the return address on the stack. Otherwise we end up associating the last stack frame with the current call, which is incorrect and causes the unwinder to skip printing of the calling function, resulting in a confusing backtrace. 285776: Let the unwinder handle faults during function prologues or epilogues. The i386 and amd64 DDB stack unwinders contain code to detect and handle the case where the first frame is not completely set up or torn down. This code was accidentally unused however, since db_backtrace() was never called with a non-NULL trap frame. This change fixes that. Also remove get_rsp() from the amd64 code. It appears to have come from i386, which needs to take into account whether the exception triggered a CPL switch, since SS:ESP is only pushed onto the stack if so. On amd64, SS:RSP is pushed regardless, so get_rsp() was doing the wrong thing for kernel-mode exceptions. As a result, we can also remove custom print functions for these registers.
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/amd64/db_trace.c161
1 files changed, 36 insertions, 125 deletions
diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c
index 39297d9..d29d23e 100644
--- a/sys/amd64/amd64/db_trace.c
+++ b/sys/amd64/amd64/db_trace.c
@@ -61,8 +61,6 @@ static db_varfcn_t db_dr5;
static db_varfcn_t db_dr6;
static db_varfcn_t db_dr7;
static db_varfcn_t db_frame;
-static db_varfcn_t db_rsp;
-static db_varfcn_t db_ss;
CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg));
@@ -76,12 +74,12 @@ struct db_variable db_regs[] = {
{ "es", DB_OFFSET(tf_es), db_frame },
{ "fs", DB_OFFSET(tf_fs), db_frame },
{ "gs", DB_OFFSET(tf_gs), db_frame },
- { "ss", NULL, db_ss },
+ { "ss", DB_OFFSET(tf_ss), db_frame },
{ "rax", DB_OFFSET(tf_rax), db_frame },
{ "rcx", DB_OFFSET(tf_rcx), db_frame },
{ "rdx", DB_OFFSET(tf_rdx), db_frame },
{ "rbx", DB_OFFSET(tf_rbx), db_frame },
- { "rsp", NULL, db_rsp },
+ { "rsp", DB_OFFSET(tf_rsp), db_frame },
{ "rbp", DB_OFFSET(tf_rbp), db_frame },
{ "rsi", DB_OFFSET(tf_rsi), db_frame },
{ "rdi", DB_OFFSET(tf_rdi), db_frame },
@@ -130,13 +128,6 @@ DB_DRX_FUNC(dr5)
DB_DRX_FUNC(dr6)
DB_DRX_FUNC(dr7)
-static __inline long
-get_rsp(struct trapframe *tf)
-{
- return ((ISPL(tf->tf_cs)) ? tf->tf_rsp :
- (db_expr_t)tf + offsetof(struct trapframe, tf_rsp));
-}
-
static int
db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
{
@@ -153,34 +144,6 @@ db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
return (1);
}
-static int
-db_rsp(struct db_variable *vp, db_expr_t *valuep, int op)
-{
-
- if (kdb_frame == NULL)
- return (0);
-
- if (op == DB_VAR_GET)
- *valuep = get_rsp(kdb_frame);
- else if (ISPL(kdb_frame->tf_cs))
- kdb_frame->tf_rsp = *valuep;
- return (1);
-}
-
-static int
-db_ss(struct db_variable *vp, db_expr_t *valuep, int op)
-{
-
- if (kdb_frame == NULL)
- return (0);
-
- if (op == DB_VAR_GET)
- *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss();
- else if (ISPL(kdb_frame->tf_cs))
- kdb_frame->tf_ss = *valuep;
- return (1);
-}
-
#define NORMAL 0
#define TRAP 1
#define INTERRUPT 2
@@ -188,9 +151,7 @@ db_ss(struct db_variable *vp, db_expr_t *valuep, int op)
#define TRAP_INTERRUPT 5
static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *);
-static int db_numargs(struct amd64_frame *);
-static void db_print_stack_entry(const char *, int, char **, long *, db_addr_t,
- void *);
+static void db_print_stack_entry(const char *, db_addr_t, void *);
static void decode_syscall(int, struct thread *);
static const char * watchtype_str(int type);
@@ -198,62 +159,11 @@ int amd64_set_watch(int watchnum, unsigned long watchaddr, int size,
int access, struct dbreg *d);
int amd64_clr_watch(int watchnum, struct dbreg *d);
-/*
- * Figure out how many arguments were passed into the frame at "fp".
- */
-static int
-db_numargs(fp)
- struct amd64_frame *fp;
-{
-#if 1
- return (0); /* regparm, needs dwarf2 info */
-#else
- long *argp;
- int inst;
- int args;
-
- argp = (long *)db_get_value((long)&fp->f_retaddr, 8, FALSE);
- /*
- * XXX etext is wrong for LKMs. We should attempt to interpret
- * the instruction at the return address in all cases. This
- * may require better fault handling.
- */
- if (argp < (long *)btext || argp >= (long *)etext) {
- args = 5;
- } else {
- inst = db_get_value((long)argp, 4, FALSE);
- if ((inst & 0xff) == 0x59) /* popl %ecx */
- args = 1;
- else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */
- args = ((inst >> 16) & 0xff) / 4;
- else
- args = 5;
- }
- return (args);
-#endif
-}
-
static void
-db_print_stack_entry(name, narg, argnp, argp, callpc, frame)
- const char *name;
- int narg;
- char **argnp;
- long *argp;
- db_addr_t callpc;
- void *frame;
+db_print_stack_entry(const char *name, db_addr_t callpc, void *frame)
{
- db_printf("%s(", name);
-#if 0
- while (narg) {
- if (argnp)
- db_printf("%s=", *argnp++);
- db_printf("%lr", (long)db_get_value((long)argp, 8, FALSE));
- argp++;
- if (--narg != 0)
- db_printf(",");
- }
-#endif
- db_printf(") at ");
+
+ db_printf("%s() at ", name != NULL ? name : "??");
db_printsym(callpc, DB_STGY_PROC);
if (frame != NULL)
db_printf("/frame 0x%lx", (register_t)frame);
@@ -348,7 +258,7 @@ db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td)
return;
}
- db_print_stack_entry(name, 0, 0, 0, rip, &(*fp)->f_frame);
+ db_print_stack_entry(name, rip, &(*fp)->f_frame);
/*
* Point to base of trapframe which is just above the
@@ -357,7 +267,7 @@ db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td)
tf = (struct trapframe *)((long)*fp + 16);
if (INKERNEL((long) tf)) {
- rsp = get_rsp(tf);
+ rsp = tf->tf_rsp;
rip = tf->tf_rip;
rbp = tf->tf_rbp;
switch (frame_type) {
@@ -384,17 +294,13 @@ db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td)
}
static int
-db_backtrace(struct thread *td, struct trapframe *tf,
- struct amd64_frame *frame, db_addr_t pc, int count)
+db_backtrace(struct thread *td, struct trapframe *tf, struct amd64_frame *frame,
+ db_addr_t pc, register_t sp, int count)
{
struct amd64_frame *actframe;
-#define MAXNARG 16
- char *argnames[MAXNARG], **argnp = NULL;
const char *name;
- long *argp;
db_expr_t offset;
c_db_sym_t sym;
- int narg;
boolean_t first;
if (count == -1)
@@ -418,48 +324,51 @@ db_backtrace(struct thread *td, struct trapframe *tf,
*/
actframe = frame;
if (first) {
- if (tf != NULL) {
+ first = FALSE;
+ if (sym == C_DB_SYM_NULL && sp != 0) {
+ /*
+ * If a symbol couldn't be found, we've probably
+ * jumped to a bogus location, so try and use
+ * the return address to find our caller.
+ */
+ db_print_stack_entry(name, pc, NULL);
+ pc = db_get_value(sp, 8, FALSE);
+ if (db_search_symbol(pc, DB_STGY_PROC,
+ &offset) == C_DB_SYM_NULL)
+ break;
+ continue;
+ } else if (tf != NULL) {
int instr;
instr = db_get_value(pc, 4, FALSE);
if ((instr & 0xffffffff) == 0xe5894855) {
/* pushq %rbp; movq %rsp, %rbp */
- actframe = (void *)(get_rsp(tf) - 8);
+ actframe = (void *)(tf->tf_rsp - 8);
} else if ((instr & 0xffffff) == 0xe58948) {
/* movq %rsp, %rbp */
- actframe = (void *)get_rsp(tf);
+ actframe = (void *)tf->tf_rsp;
if (tf->tf_rbp == 0) {
/* Fake frame better. */
frame = actframe;
}
} else if ((instr & 0xff) == 0xc3) {
/* ret */
- actframe = (void *)(get_rsp(tf) - 8);
+ actframe = (void *)(tf->tf_rsp - 8);
} else if (offset == 0) {
/* Probably an assembler symbol. */
- actframe = (void *)(get_rsp(tf) - 8);
+ actframe = (void *)(tf->tf_rsp - 8);
}
} else if (strcmp(name, "fork_trampoline") == 0) {
/*
* Don't try to walk back on a stack for a
* process that hasn't actually been run yet.
*/
- db_print_stack_entry(name, 0, 0, 0, pc,
- actframe);
+ db_print_stack_entry(name, pc, actframe);
break;
}
- first = FALSE;
}
- argp = &actframe->f_arg0;
- narg = MAXNARG;
- if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
- argnp = argnames;
- } else {
- narg = db_numargs(frame);
- }
-
- db_print_stack_entry(name, narg, argnp, argp, pc, actframe);
+ db_print_stack_entry(name, pc, actframe);
if (actframe != frame) {
/* `frame' belongs to caller. */
@@ -473,7 +382,7 @@ db_backtrace(struct thread *td, struct trapframe *tf,
if (INKERNEL((long)pc) && !INKERNEL((long)frame)) {
sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
db_symbol_values(sym, &name, NULL);
- db_print_stack_entry(name, 0, 0, 0, pc, frame);
+ db_print_stack_entry(name, pc, frame);
break;
}
if (!INKERNEL((long) frame)) {
@@ -495,17 +404,19 @@ db_trace_self(void)
frame = (struct amd64_frame *)rbp;
callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE);
frame = frame->f_frame;
- db_backtrace(curthread, NULL, frame, callpc, -1);
+ db_backtrace(curthread, NULL, frame, callpc, 0, -1);
}
int
db_trace_thread(struct thread *thr, int count)
{
struct pcb *ctx;
+ struct trapframe *tf;
ctx = kdb_thr_ctx(thr);
- return (db_backtrace(thr, NULL, (struct amd64_frame *)ctx->pcb_rbp,
- ctx->pcb_rip, count));
+ tf = thr == kdb_thread ? kdb_frame : NULL;
+ return (db_backtrace(thr, tf, (struct amd64_frame *)ctx->pcb_rbp,
+ ctx->pcb_rip, ctx->pcb_rsp, count));
}
int
OpenPOWER on IntegriCloud