Date: Sat, 7 Dec 2013 20:16:35 +0800 From: Howard Su <howard0su@gmail.com> To: freebsd-arm <freebsd-arm@freebsd.org> Subject: [PATCH] Add stack unwind support for the functions in .ko Message-ID: <CAAvnz_pWZqw-HEB-GTJ_CkErjXrz3v7ozabMXE__vr=SfkBVSg@mail.gmail.com>
next in thread | raw e-mail | index | archive | help
I need this function when working on dtrace/arm support. the basic idea is locate the ARM.EXIDX section by the symbol __exidx_start and __exidx_stop. To keep it simple, I always look through the link_file_list when unwind the stack. From the testing, the performance seems ok. Also, I implement the count parameter when unwind the stack which can make 'show threads' output more readable. I also disabled some print output by #if 0 which makes output more align with the other platform. Please review. Thanks, Howard Su diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c index 57119da..9a4728d 100644 --- a/sys/arm/arm/db_trace.c +++ b/sys/arm/arm/db_trace.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/kdb.h> #include <sys/stack.h> +#include <sys/linker.h> #include <machine/armreg.h> #include <machine/asm.h> #include <machine/cpufunc.h> @@ -79,12 +80,6 @@ __FBSDID("$FreeBSD$"); #define PC 15 /* - * These are set in the linker script. Their addresses will be - * either the start or end of the exception table or index. - */ -extern int extab_start, extab_end, exidx_start, exidx_end; - -/* * Entry types. * These are the only entry types that have been seen in the kernel. */ @@ -135,6 +130,28 @@ db_expand_prel31(uint32_t prel31) return ((int32_t)(prel31 & 0x7fffffffu) << 1) / 2; } +struct db_find_index_context_t +{ + int valid; + caddr_t addr; + caddr_t exidx_start, exidx_end; +}; + +static int +db_find_index_file_cb(linker_file_t lf, void* arg) +{ + struct db_find_index_context_t *context = (struct db_find_index_context_t*)arg; + if (context->addr >= lf->address && context->addr < lf->address + lf->size) + { + context->exidx_start = linker_file_lookup_symbol(lf, "__exidx_start", 0); + context->exidx_end = linker_file_lookup_symbol(lf, "__exidx_end", 0); + context->valid = 1; + return 1; + } + + return 0; +} + /* * Perform a binary search of the index table to find the function * with the largest address that doesn't exceed addr. @@ -148,10 +165,18 @@ db_find_index(uint32_t addr) int32_t prel31_addr; uint32_t func_addr; - start = (struct unwind_idx *)&exidx_start; + struct db_find_index_context_t context; + context.valid = 0; + context.addr = (caddr_t)addr; + + linker_file_foreach(db_find_index_file_cb, &context); + if (!context.valid) + return 0; + + start = (struct unwind_idx *)context.exidx_start; min = 0; - max = (&exidx_end - &exidx_start) / 2; + max = (context.exidx_end - context.exidx_start) / 2; while (min != max) { mid = min + (max - min + 1) / 2; @@ -269,7 +294,7 @@ db_unwind_exec_insn(struct unwind_state *state) /* Stop processing */ state->entries = 0; - } else if ((insn == INSN_POP_REGS)) { + } else if (insn == INSN_POP_REGS) { unsigned int mask, reg; mask = db_unwind_exec_read_byte(state); @@ -352,20 +377,22 @@ db_unwind_tab(struct unwind_state *state) } static void -db_stack_trace_cmd(struct unwind_state *state) +db_stack_trace_cmd(struct unwind_state *state, int count) { struct unwind_idx *index; const char *name; db_expr_t value; db_expr_t offset; c_db_sym_t sym; +#if 0 u_int reg, i; char *sep; uint16_t upd_mask; +#endif bool finished; finished = false; - while (!finished) { + while (!finished && count--) { /* Reset the mask of updated registers */ state->update_mask = 0; @@ -375,7 +402,7 @@ db_stack_trace_cmd(struct unwind_state *state) /* Find the item to run */ index = db_find_index(state->start_pc); - if (index->insn != EXIDX_CANTUNWIND) { + if (index && index->insn != EXIDX_CANTUNWIND) { if (index->insn & (1U << 31)) { /* The data is within the instruction */ state->insn = &index->insn; @@ -399,6 +426,7 @@ db_stack_trace_cmd(struct unwind_state *state) db_printf("%s() at ", name); db_printsym(state->start_pc, DB_STGY_PROC); db_printf("\n"); +#if 0 db_printf("\t pc = 0x%08x lr = 0x%08x (", state->start_pc, state->registers[LR]); db_printsym(state->registers[LR], DB_STGY_PROC); @@ -425,7 +453,7 @@ db_stack_trace_cmd(struct unwind_state *state) } } db_printf("\n"); - +#endif /* * Stop if directed to do so, or if we've unwound back to the * kernel entry point, or if the unwind function didn't change @@ -435,14 +463,17 @@ db_stack_trace_cmd(struct unwind_state *state) * the last frame printed before you see the unwind failure * message (maybe it needs a STOP_UNWINDING). */ - if (index->insn == EXIDX_CANTUNWIND) { - db_printf("Unable to unwind further\n"); + if (index && index->insn == EXIDX_CANTUNWIND) { + if (count) + db_printf("Unable to unwind further\n"); finished = true; } else if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) { - db_printf("Unable to unwind into user mode\n"); + if (count) + db_printf("Unable to unwind into user mode\n"); finished = true; } else if (state->update_mask == 0) { - db_printf("Unwind failure (no registers changed)\n"); + if (count) + db_printf("Unwind failure (no registers changed)\n"); finished = true; } } @@ -479,7 +510,7 @@ db_stack_trace_cmd(struct unwind_state *state) #ifndef __ARM_EABI__ /* The frame format is differend in AAPCS */ static void -db_stack_trace_cmd(db_expr_t addr, db_expr_t count, boolean_t kernel_only) +db_stack_trace_cmd(db_expr_t addr, int count, boolean_t kernel_only) { u_int32_t *frame, *lastframe; c_db_sym_t sym; @@ -608,9 +639,9 @@ db_trace_thread(struct thread *thr, int count) state.registers[LR] = ctx->un_32.pcb32_lr; state.registers[PC] = ctx->un_32.pcb32_pc; - db_stack_trace_cmd(&state); + db_stack_trace_cmd(&state, count); #else - db_stack_trace_cmd(ctx->un_32.pcb32_r11, -1, TRUE); + db_stack_trace_cmd(ctx->un_32.pcb32_r11, count, TRUE); #endif } else db_trace_self(); @@ -632,7 +663,7 @@ db_trace_self(void) state.registers[LR] = (uint32_t)__builtin_return_address(0); state.registers[PC] = (uint32_t)db_trace_self; - db_stack_trace_cmd(&state); + db_stack_trace_cmd(&state, -1); #else db_addr_t addr; diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index bd05878..d0a1f0f 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -133,6 +133,16 @@ CFLAGS+= -mlongcall -fno-omit-frame-pointer CFLAGS+= -G0 -fno-pic -mno-abicalls -mlong-calls .endif +.if ${MACHINE_CPUARCH} == arm +.if !defined(WITHOUT_ARM_EABI) +CFLAGS+= -funwind-tables +.if ${COMPILER_TYPE} == "clang" +# clang requires us to tell it to emit assembly with unwind information +CFLAGS+= -mllvm -arm-enable-ehabi +.endif +.endif +.endif + .if defined(DEBUG) || defined(DEBUG_FLAGS) CTFFLAGS+= -g .endif diff --git a/sys/conf/ldscript.arm b/sys/conf/ldscript.arm index 0d1c7ee..2482ce7 100644 --- a/sys/conf/ldscript.arm +++ b/sys/conf/ldscript.arm @@ -57,17 +57,11 @@ SECTIONS .plt : { *(.plt) } . = ALIGN(4); - _extab_start = .; - PROVIDE(extab_start = .); .ARM.extab : { *(.ARM.extab) } - _extab.end = .; - PROVIDE(extab_end = .); - _exidx_start = .; - PROVIDE(exidx_start = .); + __exidx_start = .; .ARM.exidx : { *(.ARM.exidx) } - _exidx_end = .; - PROVIDE(exidx_end = .); + __exidx_end = .; /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ -- -Howard
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAAvnz_pWZqw-HEB-GTJ_CkErjXrz3v7ozabMXE__vr=SfkBVSg>