summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2017-04-20 12:41:44 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2017-04-20 12:41:44 +0000
commiteeb5de6186b3574f4852fdf604c0d31f96448ee1 (patch)
tree90df45bd434fbbdd05be3443af9075a8af59718c
parentc3de74dc1edd1469fba32ab5324713687a11f099 (diff)
Add routines for saving stack traces and printing saved traces
on amd64 and i386. With guenther@
-rw-r--r--sys/arch/amd64/amd64/db_trace.c108
-rw-r--r--sys/arch/i386/i386/db_trace.c96
-rw-r--r--sys/ddb/db_access.h12
-rw-r--r--sys/ddb/db_output.c15
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 <ddb/db_command.h>
#include <ddb/db_output.h>
+#include <ddb/db_access.h>
#include <ddb/db_interface.h>
#include <ddb/db_sym.h>
#include <ddb/db_var.h>
@@ -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");
+ }
+}