Add routines for saving stack traces and printing saved traces
authorvisa <visa@openbsd.org>
Thu, 20 Apr 2017 12:41:43 +0000 (12:41 +0000)
committervisa <visa@openbsd.org>
Thu, 20 Apr 2017 12:41:43 +0000 (12:41 +0000)
on amd64 and i386.

With guenther@

sys/arch/amd64/amd64/db_trace.c
sys/arch/i386/i386/db_trace.c
sys/ddb/db_access.h
sys/ddb/db_output.c

index a24b8ba..9c6e275 100644 (file)
@@ -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)
 {
index 4ef17ce..5417988 100644 (file)
@@ -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)
 {
index b214af7..e4d3011 100644 (file)
@@ -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 *);
index 11e5c5a..7522978 100644 (file)
@@ -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");
+       }
+}