From bfc40b2341fe2b2ff729d325407c8c1dbf4182c0 Mon Sep 17 00:00:00 2001 From: visa Date: Thu, 20 Apr 2017 12:41:43 +0000 Subject: [PATCH] Add routines for saving stack traces and printing saved traces on amd64 and i386. With guenther@ --- sys/arch/amd64/amd64/db_trace.c | 108 +++++++++++++++++++++++++------- sys/arch/i386/i386/db_trace.c | 96 +++++++++++++++++++++------- sys/ddb/db_access.h | 12 +++- sys/ddb/db_output.c | 15 ++++- 4 files changed, 186 insertions(+), 45 deletions(-) diff --git a/sys/arch/amd64/amd64/db_trace.c b/sys/arch/amd64/amd64/db_trace.c index a24b8ba0371..9c6e2756436 100644 --- a/sys/arch/amd64/amd64/db_trace.c +++ b/sys/arch/amd64/amd64/db_trace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_trace.c,v 1.25 2017/02/06 09:13:41 mpi Exp $ */ +/* $OpenBSD: db_trace.c,v 1.26 2017/04/20 12:41:43 visa Exp $ */ /* $NetBSD: db_trace.c,v 1.1 2003/04/26 18:39:27 fvdl Exp $ */ /* @@ -153,6 +153,28 @@ db_nextframe(struct callframe **fp, db_addr_t *ip, long *argp, int is_trap, } } +static inline int +db_is_trap(const char *name) +{ + if (name != NULL) { + if (!strcmp(name, "trap")) + return TRAP; + if (!strcmp(name, "ast")) + return AST; + if (!strcmp(name, "syscall")) + return SYSCALL; + if (name[0] == 'X') { + if (!strncmp(name, "Xintr", 5) || + !strncmp(name, "Xresume", 7) || + !strncmp(name, "Xrecurse", 8) || + !strcmp(name, "Xdoreti") || + !strncmp(name, "Xsoft", 5)) + return INTERRUPT; + } + } + return NONE; +} + void db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif, int (*pr)(const char *, ...)) @@ -217,27 +239,9 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, offset = 0; } } - if (INKERNEL(callpc) && name) { - if (!strcmp(name, "trap")) { - is_trap = TRAP; - } else if (!strcmp(name, "ast")) { - is_trap = AST; - } else if (!strcmp(name, "syscall")) { - is_trap = SYSCALL; - } else if (name[0] == 'X') { - if (!strncmp(name, "Xintr", 5) || - !strncmp(name, "Xresume", 7) || - !strncmp(name, "Xrecurse", 8) || - !strcmp(name, "Xdoreti") || - !strncmp(name, "Xsoft", 5)) { - is_trap = INTERRUPT; - } else - goto normal; - } else - goto normal; + if (INKERNEL(callpc) && (is_trap = db_is_trap(name)) != NONE) narg = 0; - } else { - normal: + else { is_trap = NONE; narg = db_numargs(frame, name); } @@ -322,6 +326,68 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, } } +void +db_save_stack_trace(struct db_stack_trace *st) +{ + struct callframe *frame, *lastframe; + db_addr_t callpc; + unsigned int i; + + frame = __builtin_frame_address(0); + + callpc = db_get_value((db_addr_t)&frame->f_retaddr, 8, FALSE); + frame = frame->f_frame; + + lastframe = NULL; + for (i = 0; i < DB_STACK_TRACE_MAX && frame != NULL; i++) { + struct trapframe *tf; + char *name; + db_expr_t offset; + db_sym_t sym; + int is_trap; + + st->st_pc[st->st_count++] = callpc; + sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); + db_symbol_values(sym, &name, NULL); + + if (INKERNEL(callpc)) + is_trap = db_is_trap(name); + else + is_trap = NONE; + + if (is_trap == NONE) { + lastframe = frame; + callpc = frame->f_retaddr; + frame = frame->f_frame; + } else { + if (is_trap == INTERRUPT) { + /* + * Interrupt routines don't update %rbp, + * so it still points to the frame that + * was interrupted. Pull back to just + * above lastframe so we can find the + * trapframe as with syscalls and traps. + */ + if (lastframe == NULL) + break; + + frame = + (struct callframe *)&lastframe->f_retaddr; + } + lastframe = frame; + + tf = (struct trapframe *)&frame->f_arg0; + callpc = (db_addr_t)tf->tf_rip; + frame = (struct callframe *)tf->tf_rbp; + } + + if (!INKERNEL(frame)) + break; + if (frame <= lastframe) + break; + } +} + vaddr_t db_get_pc(struct trapframe *tf) { diff --git a/sys/arch/i386/i386/db_trace.c b/sys/arch/i386/i386/db_trace.c index 4ef17ce2333..541798882c4 100644 --- a/sys/arch/i386/i386/db_trace.c +++ b/sys/arch/i386/i386/db_trace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_trace.c,v 1.24 2017/02/06 09:13:41 mpi Exp $ */ +/* $OpenBSD: db_trace.c,v 1.25 2017/04/20 12:41:43 visa Exp $ */ /* $NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $ */ /* @@ -160,6 +160,30 @@ db_nextframe(struct callframe **fp, db_addr_t *ip, int *argp, int is_trap, } } +static inline int +db_is_trap(const char *name) +{ + if (name != NULL) { + if (!strcmp(name, "trap")) + return TRAP; + if (!strcmp(name, "ast")) + return AST; + if (!strcmp(name, "syscall")) + return SYSCALL; + if (name[0] == 'X') { + if (!strncmp(name, "Xintr", 5) || + !strncmp(name, "Xresume", 7) || + !strncmp(name, "Xstray", 6) || + !strncmp(name, "Xhold", 5) || + !strncmp(name, "Xrecurse", 8) || + !strcmp(name, "Xdoreti") || + !strncmp(name, "Xsoft", 5)) + return INTERRUPT; + } + } + return NONE; +} + void db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif, int (*pr)(const char *, ...)) @@ -231,26 +255,9 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, offset = 0; } } - if (INKERNEL(callpc) && name) { - if (!strcmp(name, "trap")) { - is_trap = TRAP; - } else if (!strcmp(name, "ast")) { - is_trap = AST; - } else if (!strcmp(name, "syscall")) { - is_trap = SYSCALL; - } else if (!strncmp(name, "Xintr", 5) || - !strncmp(name, "Xresume", 7) || - !strncmp(name, "Xstray", 6) || - !strncmp(name, "Xhold", 5) || - !strncmp(name, "Xrecurse", 8) || - !strcmp(name, "Xdoreti") || - !strncmp(name, "Xsoft", 5)) { - is_trap = INTERRUPT; - } else - goto normal; + if (INKERNEL(callpc) && (is_trap = db_is_trap(name)) != NONE) narg = 0; - } else { - normal: + else { is_trap = NONE; narg = db_numargs(frame, name); } @@ -293,13 +300,13 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, /* end of chain */ break; } - if (INKERNEL((int)frame)) { + if (INKERNEL(frame)) { /* staying in kernel */ if (frame <= lastframe) { (*pr)("Bad frame pointer: %p\n", frame); break; } - } else if (INKERNEL((int)lastframe)) { + } else if (INKERNEL(lastframe)) { /* switch from user to kernel */ if (kernel_only) break; /* kernel stack only */ @@ -320,6 +327,51 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, } } +void +db_save_stack_trace(struct db_stack_trace *st) +{ + struct callframe *frame, *lastframe; + db_addr_t callpc; + unsigned int i; + + frame = __builtin_frame_address(0); + callpc = db_get_value((int)&frame->f_retaddr, 4, FALSE); + + lastframe = NULL; + for (i = 0; i < DB_STACK_TRACE_MAX && frame != NULL; i++) { + char *name; + db_expr_t offset; + db_sym_t sym; + int is_trap = 0; + + st->st_pc[st->st_count++] = callpc; + sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); + db_symbol_values(sym, &name, NULL); + + if (INKERNEL(callpc)) + is_trap = db_is_trap(name); + else + is_trap = NONE; + + lastframe = frame; + if (is_trap == NONE) { + callpc = frame->f_retaddr; + frame = frame->f_frame; + } else { + struct trapframe *tf; + + tf = (struct trapframe *)&frame->f_arg0; + callpc = (db_addr_t)tf->tf_eip; + frame = (struct callframe *)tf->tf_ebp; + } + + if (!INKERNEL(frame)) + break; + if (frame <= lastframe) + break; + } +} + vaddr_t db_get_pc(struct trapframe *tf) { diff --git a/sys/ddb/db_access.h b/sys/ddb/db_access.h index b214af7c63b..e4d3011748a 100644 --- a/sys/ddb/db_access.h +++ b/sys/ddb/db_access.h @@ -1,4 +1,4 @@ -/* $OpenBSD: db_access.h,v 1.7 2016/04/19 10:24:42 mpi Exp $ */ +/* $OpenBSD: db_access.h,v 1.8 2017/04/20 12:41:43 visa Exp $ */ /* $NetBSD: db_access.h,v 1.6 1994/10/09 08:29:57 mycroft Exp $ */ /* @@ -38,3 +38,13 @@ void db_put_value(db_addr_t, size_t, db_expr_t); void db_read_bytes(db_addr_t, size_t, char *); void db_write_bytes(db_addr_t, size_t, char *); + +#define DB_STACK_TRACE_MAX 19 + +struct db_stack_trace { + unsigned int st_count; + db_addr_t st_pc[DB_STACK_TRACE_MAX]; +}; + +void db_print_stack_trace(struct db_stack_trace *); +void db_save_stack_trace(struct db_stack_trace *); diff --git a/sys/ddb/db_output.c b/sys/ddb/db_output.c index 11e5c5a0813..7522978b77e 100644 --- a/sys/ddb/db_output.c +++ b/sys/ddb/db_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_output.c,v 1.30 2016/09/03 21:43:46 jasper Exp $ */ +/* $OpenBSD: db_output.c,v 1.31 2017/04/20 12:41:43 visa Exp $ */ /* $NetBSD: db_output.c,v 1.13 1996/04/01 17:27:14 christos Exp $ */ /* @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -242,3 +243,15 @@ db_stack_dump(void) printf("End of stack trace.\n"); intrace = 0; } + +void +db_print_stack_trace(struct db_stack_trace *st) +{ + unsigned int i; + + for (i = 0; i < st->st_count; i++) { + printf("#%-2u ", i); + db_printsym(st->st_pc[i], DB_STGY_PROC, printf); + printf("\n"); + } +} -- 2.20.1