Skip site navigation (1)Skip section navigation (2)
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>