summaryrefslogtreecommitdiffstats
path: root/sys/arm
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2013-05-04 19:50:50 +0000
committerian <ian@FreeBSD.org>2013-05-04 19:50:50 +0000
commit34a388c771c058e2b8f236142c0e7c9607f7b00f (patch)
tree0ec13cf5f29ca10a932d33c489e3a57d9575d1d9 /sys/arm
parentd9d71436d975a7ec598297c564219c530aa41138 (diff)
downloadFreeBSD-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.c65
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
OpenPOWER on IntegriCloud