From 4a761caec7754f1fc2754466099320c54bf45abb Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 3 Aug 2005 04:27:40 +0000 Subject: - Add support for saving stack traces and displaying them via printf(9) and KTR. Contributed by: Antoine Brodin Concept code from: Neal Fachan --- sys/alpha/alpha/db_trace.c | 41 ++++++++++ sys/arm/arm/db_trace.c | 19 +++++ sys/conf/files | 1 + sys/i386/i386/db_trace.c | 23 ++++++ sys/ia64/ia64/db_trace.c | 13 ++++ sys/kern/subr_stack.c | 165 +++++++++++++++++++++++++++++++++++++++++ sys/powerpc/powerpc/db_trace.c | 23 ++++++ sys/sparc64/sparc64/db_trace.c | 21 ++++++ sys/sys/stack.h | 62 ++++++++++++++++ 9 files changed, 368 insertions(+) create mode 100644 sys/kern/subr_stack.c create mode 100644 sys/sys/stack.h diff --git a/sys/alpha/alpha/db_trace.c b/sys/alpha/alpha/db_trace.c index fcee2f3..12b6af4 100644 --- a/sys/alpha/alpha/db_trace.c +++ b/sys/alpha/alpha/db_trace.c @@ -47,7 +47,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include @@ -348,6 +350,45 @@ db_trace_thread(struct thread *thr, int count) count)); } +void +stack_save(struct stack *st) +{ + struct prologue_info pi; + linker_symval_t symval; + c_linker_sym_t sym; + vm_offset_t callpc, frame; + long offset; + register_t pc, sp; + + stack_zero(st); + __asm __volatile( + " mov $30,%0 \n" + " lda %1,1f \n" + "1:\n" + : "=r" (sp), "=r" (pc)); + callpc = (vm_offset_t)pc; + frame = (vm_offset_t)sp; + while (1) { + /* + * search_symbol/symbol_values are slow + */ + if (linker_ddb_search_symbol((caddr_t)callpc, &sym, &offset) != 0) + break; + if (linker_ddb_symbol_values(sym, &symval) != 0) + break; + if (callpc < (vm_offset_t)symval.value) + break; + if (stack_put(st, callpc) == -1) + break; + if (decode_prologue(callpc, (db_addr_t)symval.value, &pi)) + break; + if ((pi.pi_regmask & (1 << 26)) == 0) + break; + callpc = *(vm_offset_t *)(frame + pi.pi_reg_offset[26]); + frame += pi.pi_frame_size; + } +} + int db_md_set_watchpoint(addr, size) db_expr_t addr; diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c index 869713a..31ebfb9 100644 --- a/sys/arm/arm/db_trace.c +++ b/sys/arm/arm/db_trace.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -219,3 +220,21 @@ db_trace_self(void) { db_trace_thread(curthread, -1); } + +void +stack_save(struct stack *st) +{ + vm_offset_t callpc; + u_int32_t *frame; + + stack_zero(st); + frame = (u_int32_t *)__builtin_frame_address(0); + while (1) { + if (!INKERNEL(frame)) + break; + callpc = frame[FR_SCP]; + if (stack_put(st, callpc) == -1) + break; + frame = (u_int32_t *)(frame[FR_RFP]); + } +} diff --git a/sys/conf/files b/sys/conf/files index b449cbc..acddda1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1259,6 +1259,7 @@ kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_sleepqueue.c standard kern/subr_smp.c standard +kern/subr_stack.c optional ddb kern/subr_taskqueue.c standard kern/subr_trap.c standard kern/subr_turnstile.c standard diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index c26ba36..3b44f561 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -500,6 +501,28 @@ db_trace_thread(struct thread *thr, int count) ctx->pcb_eip, count)); } +void +stack_save(struct stack *st) +{ + struct i386_frame *frame; + vm_offset_t callpc; + register_t ebp; + + stack_zero(st); + __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); + frame = (struct i386_frame *)ebp; + while (1) { + if (!INKERNEL(frame)) + break; + callpc = frame->f_retaddr; + if (!INKERNEL(callpc)) + break; + if (stack_put(st, callpc) == -1) + break; + frame = frame->f_frame; + } +} + int i386_set_watch(watchnum, watchaddr, size, access, d) int watchnum; diff --git a/sys/ia64/ia64/db_trace.c b/sys/ia64/ia64/db_trace.c index 1dae4da..b70c2d5 100644 --- a/sys/ia64/ia64/db_trace.c +++ b/sys/ia64/ia64/db_trace.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -148,6 +149,18 @@ db_trace_thread(struct thread *td, int count) return (db_backtrace(td, ctx, count)); } +void +stack_save(struct stack *st) +{ + + stack_zero(st); + /* + * Nothing for now. + * Is libuwx reentrant? + * Can unw_create* sleep? + */ +} + int db_md_set_watchpoint(addr, size) db_expr_t addr; diff --git a/sys/kern/subr_stack.c b/sys/kern/subr_stack.c new file mode 100644 index 0000000..9b2be7b --- /dev/null +++ b/sys/kern/subr_stack.c @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 2005 Antoine Brodin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#ifdef KTR +#include +#endif +#include +#include +#include +#include +#include + +MALLOC_DEFINE(M_STACK, "stack", "Stack Traces"); + +static void stack_symbol(vm_offset_t pc, const char **name, long *offset); + +struct stack * +stack_create(void) +{ + struct stack *st; + + st = malloc(sizeof *st, M_STACK, M_WAITOK | M_ZERO); + return (st); +} + +void +stack_destroy(struct stack *st) +{ + + free(st, M_STACK); +} + +int +stack_put(struct stack *st, vm_offset_t pc) +{ + + if (st->depth < STACK_MAX) { + st->pcs[st->depth++] = pc; + return (0); + } else + return (-1); +} + +void +stack_copy(struct stack *src, struct stack *dst) +{ + + *dst = *src; +} + +void +stack_zero(struct stack *st) +{ + + bzero(st, sizeof *st); +} + +void +stack_print(struct stack *st) +{ + const char *name; + long offset; + int i; + + KASSERT(st->depth <= STACK_MAX, ("bogous stack")); + for (i = 0; i < st->depth; i++) { + stack_symbol(st->pcs[i], &name, &offset); + printf("#%d %p at %s+%#lx\n", i, (void *)st->pcs[i], + name, offset); + } +} + +void +stack_sbuf_print(struct sbuf *sb, struct stack *st) +{ + const char *name; + long offset; + int i; + + KASSERT(st->depth <= STACK_MAX, ("bogous stack")); + for (i = 0; i < st->depth; i++) { + stack_symbol(st->pcs[i], &name, &offset); + sbuf_printf(sb, "#%d %p at %s+%#lx\n", i, (void *)st->pcs[i], + name, offset); + } +} + +#ifdef KTR +void +stack_ktr(u_int mask, const char *file, int line, struct stack *st, int cheap) +{ + const char *name; + long offset; + int i; + + KASSERT(st->depth <= STACK_MAX, ("bogous stack")); + if (cheap) { + ktr_tracepoint(mask, file, line, "#0 %p %p %p %p %p %p", + st->pcs[0], st->pcs[1], st->pcs[2], st->pcs[3], + st->pcs[4], st->pcs[5]); + if (st->depth <= 6) + return; + ktr_tracepoint(mask, file, line, "#1 %p %p %p %p %p %p", + st->pcs[6], st->pcs[7], st->pcs[8], st->pcs[9], + st->pcs[10], st->pcs[11]); + if (st->depth <= 12) + return; + ktr_tracepoint(mask, file, line, "#2 %p %p %p %p %p %p", + st->pcs[12], st->pcs[13], st->pcs[14], st->pcs[15], + st->pcs[16], st->pcs[17]); + } else + for (i = 0; i < st->depth; i++) { + stack_symbol(st->pcs[i], &name, &offset); + ktr_tracepoint(mask, file, line, "#%d %p at %s+%#lx", + i, st->pcs[i], (u_long)name, offset, 0, 0); + } +} +#endif + +static void +stack_symbol(vm_offset_t pc, const char **name, long *offset) +{ + linker_symval_t symval; + c_linker_sym_t sym; + + if (linker_ddb_search_symbol((caddr_t)pc, &sym, offset) != 0) + goto out; + if (linker_ddb_symbol_values(sym, &symval) != 0) + goto out; + if (symval.name != NULL) { + *name = symval.name; + return; + } +out: + *offset = 0; + *name = "Unknown func"; +} diff --git a/sys/powerpc/powerpc/db_trace.c b/sys/powerpc/powerpc/db_trace.c index a32625a..a30595c 100644 --- a/sys/powerpc/powerpc/db_trace.c +++ b/sys/powerpc/powerpc/db_trace.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -287,3 +288,25 @@ db_trace_thread(struct thread *td, int count) return (db_backtrace(td, (db_addr_t)ctx->pcb_sp, count)); } +void +stack_save(struct stack *st) +{ + vm_offset_t callpc; + db_addr_t stackframe; + + stack_zero(st); + stackframe = (db_addr_t)__builtin_frame_address(1); + if (stackframe < PAGE_SIZE) + return; + while (1) { + stackframe = *(db_addr_t *)stackframe; + if (stackframe < PAGE_SIZE) + break; + callpc = *(vm_offset_t *)(stackframe + 4) - 4; + if ((callpc & 3) || (callpc < 0x100)) + break; + if (stack_put(st, callpc) == -1) + break; + } +} + diff --git a/sys/sparc64/sparc64/db_trace.c b/sys/sparc64/sparc64/db_trace.c index dc6e4bb..66cc76b 100644 --- a/sys/sparc64/sparc64/db_trace.c +++ b/sys/sparc64/sparc64/db_trace.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -291,3 +292,23 @@ db_trace_thread(struct thread *td, int count) ctx = kdb_thr_ctx(td); return (db_backtrace(td, (struct frame*)(ctx->pcb_sp + SPOFF), count)); } + +void +stack_save(struct stack *st) +{ + struct frame *fp; + db_expr_t addr; + vm_offset_t callpc; + + stack_zero(st); + addr = (db_expr_t)__builtin_frame_address(1); + fp = (struct frame *)(addr + SPOFF); + while (1) { + callpc = fp->fr_pc; + if (!INKERNEL(callpc)) + break; + if (stack_put(st, callpc) == -1) + break; + fp = (struct frame *)(fp->fr_fp + SPOFF); + } +} diff --git a/sys/sys/stack.h b/sys/sys/stack.h new file mode 100644 index 0000000..768c275 --- /dev/null +++ b/sys/sys/stack.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2005 Antoine Brodin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_STACK_H_ +#define _SYS_STACK_H_ + +#define STACK_MAX 18 /* Don't change, stack_ktr relies on this. */ + +struct sbuf; + +struct stack { + int depth; + vm_offset_t pcs[STACK_MAX]; +}; + +/* MI Routines. */ +struct stack *stack_create(void); +void stack_destroy(struct stack *); +int stack_put(struct stack *, vm_offset_t); +void stack_copy(struct stack *, struct stack *); +void stack_zero(struct stack *); +void stack_print(struct stack *); +void stack_sbuf_print(struct sbuf *, struct stack *); +#ifdef KTR +void stack_ktr(u_int, const char *, int, struct stack *, int); +#define CTRSTACK(m, st, cheap) do { \ + if (KTR_COMPILE & (m)) \ + stack_ktr((m), __FILE__, __LINE__, st, cheap); \ + } while(0) +#else +#define CTRSTACK(m, st, cheap) +#endif + +/* MD Routine. */ +void stack_save(struct stack *); + +#endif -- cgit v1.1