diff options
author | ian <ian@FreeBSD.org> | 2013-05-04 19:50:50 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2013-05-04 19:50:50 +0000 |
commit | 34a388c771c058e2b8f236142c0e7c9607f7b00f (patch) | |
tree | 0ec13cf5f29ca10a932d33c489e3a57d9575d1d9 /sys/arm | |
parent | d9d71436d975a7ec598297c564219c530aa41138 (diff) | |
download | FreeBSD-src-34a388c771c058e2b8f236142c0e7c9607f7b00f.zip FreeBSD-src-34a388c771c058e2b8f236142c0e7c9607f7b00f.tar.gz |
EABI unwinder enhancements... When it's time to stop unwinding, don't
exit the loop until after printing info about the current frame. Also,
if executing the unwind function for a frame doesn't change the values of
any registers, log that and exit the loop rather than looping endlessly.
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/arm/db_trace.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c index 90ac0b1..2ade6c53 100644 --- a/sys/arm/arm/db_trace.c +++ b/sys/arm/arm/db_trace.c @@ -342,8 +342,11 @@ db_stack_trace_cmd(struct unwind_state *state) c_db_sym_t sym; u_int reg, i; char *sep; + uint16_t upd_mask; + bool finished; - while (1) { + finished = false; + while (!finished) { /* Reset the mask of updated registers */ state->update_mask = 0; @@ -353,28 +356,20 @@ 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) { - db_printf("Unable to unwind\n"); - break; - } else if (index->insn & (1 << 31)) { - /* The data is within the instruction */ - state->insn = &index->insn; - } else { - /* We have a prel31 offset to the unwind table */ - uint32_t prel31_tbl = db_expand_prel31(index->insn); - - state->insn = (uint32_t *)((uintptr_t)&index->insn + - prel31_tbl); + if (index->insn != EXIDX_CANTUNWIND) { + if (index->insn & (1 << 31)) { + /* The data is within the instruction */ + state->insn = &index->insn; + } else { + /* A prel31 offset to the unwind table */ + state->insn = (uint32_t *) + ((uintptr_t)&index->insn + + db_expand_prel31(index->insn)); + } + /* Run the unwind function */ + finished = db_unwind_tab(state); } - /* Run the unwind function */ - if (db_unwind_tab(state) != 0) - break; - - /* This is not a kernel address, stop processing */ - if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) - break; - /* Print the frame details */ sym = db_search_symbol(state->start_pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { @@ -393,12 +388,11 @@ db_stack_trace_cmd(struct unwind_state *state) state->registers[SP], state->registers[FP]); /* Don't print the registers we have already printed */ - state->update_mask &= ~((1 << SP) | (1 << FP) | (1 << LR) | - (1 << PC)); + upd_mask = state->update_mask & + ~((1 << SP) | (1 << FP) | (1 << LR) | (1 << PC)); sep = "\n\t"; - for (i = 0, reg = 0; state->update_mask != 0; - state->update_mask >>= 1, reg++) { - if ((state->update_mask & 1) != 0) { + for (i = 0, reg = 0; upd_mask != 0; upd_mask >>= 1, reg++) { + if ((upd_mask & 1) != 0) { db_printf("%s%sr%d = 0x%08x", sep, (reg < 10) ? " " : "", reg, state->registers[reg]); @@ -412,6 +406,25 @@ db_stack_trace_cmd(struct unwind_state *state) } } db_printf("\n"); + + /* 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 + * anything (to avoid getting stuck in this loop forever). + * If the latter happens, it's an indication that the unwind + * information is incorrect somehow for the function named in + * 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"); + finished = true; + } else if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) { + 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"); + finished = true; + } } } #endif |