Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,36 @@ to the name of the function (if the GCC -mpoke-function-name option is used).

Backtrace provides the following API:

```C
typedef struct backtrace
{
void *function; /* Address of the current address */
void *address; /* Calling site address
void *address; /* Calling site address */
const char *name;
} backtrace_t;

/* Unwind the stack from the current address, filling in provided
backtrace bufffer, limited by size of the buffer, return a count
of the valid frames */
of the valid frames */
int backtrace_unwind(backtrace_t *backtrace, int size)

/* Unwind the stack from the current address, calling the specified callback
for each frame. Stack traversion will stop on stack end or when the callback
returns anything but UNWIND_CONTINUE. */
int backtrace_unwind_cb(unwind_trace_fn callback, void *context);

/* Same as backtrace_unwind but iterates over a user specified frame */
int backtrace_unwind_from_frame(backtrace_t *buffer, int size, backtrace_frame_t *frame);

/* Same as backtrace_unwind_cb but iterates over a user specified frame */
int backtrace_unwind_from_frame_cb(unwind_trace_fn callback, void *context, backtrace_frame_t *frame);

/* Return a pointer to the function name at the specified address or
"unknown" if the function name string is not present. The address
can be the entry point for the function or any address within the
function's scope. */
"unknown" if the function name string is not present. The address
can be the entry point for the function or any address within the
function's scope. */
const char *backtrace_function_name(uint32_t pc);
```C

ARM estimates the overhead for the unwind tables at 2.6% to 3.6%
(see /doc/IHI0038B_ehabi.pdf). Using the -mpoke-function-name option increases
Expand Down Expand Up @@ -61,16 +74,20 @@ Dependencies

Backtrace requires a compatible/working ARM cross compiler supporting the following built-ins:

```C
__builtin_return_address /* Get the return address for the current function */
__builtin_frame_address /* Get the current frame pointer */
```

and is known to work with recent version of gcc
(see https://launchpad.net/gcc-arm-embedded).

In addition your linker scripts should export the following symbols:

```C
__exidx_start /* Pointing to the start of the unwind index table */
__exidx_end /* Pointing to the end of the unwind index table */
```

Building
--------
Expand Down Expand Up @@ -150,6 +167,7 @@ Using The Unwinder
/* All good */
return 0;
}
```

To compile and link this sample using a recent GCC ARM Embedded compiler:

Expand Down
82 changes: 70 additions & 12 deletions backtrace/backtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,46 @@
#include <stdlib.h>
#include <string.h>

int _backtrace_unwind(unwind_trace_fn callback, void *context, backtrace_frame_t *frame);
unwind_code_t default_unwind_callback(backtrace_t *trace, void *context);

/* These symbols point to the unwind index and should be provide by the linker script */
extern const unwind_index_t __exidx_start[];
extern const unwind_index_t __exidx_end[];

/* Default unwind context */
typedef struct
{
backtrace_t *buffer;
backtrace_t *buffer_end;
} default_unwind_context_t;

/* Default unwind handler */
unwind_code_t default_unwind_callback(backtrace_t *trace, void *context)
{
default_unwind_context_t *ctx = (default_unwind_context_t*) context;

if(ctx->buffer == ctx->buffer_end)
return UNWIND_ABORT;

ctx->buffer->address = trace->address;
ctx->buffer->function = trace->function;
ctx->buffer->name = trace->name;
ctx->buffer++;

return ctx->buffer != ctx->buffer_end ? UNWIND_CONTINUE : UNWIND_DONE;
}

static unwind_code_t collect_backtrace(unwind_trace_fn callback, void *context, backtrace_t *trace, int *count)
{
unwind_code_t res = callback(trace, context);

if(res == UNWIND_CONTINUE || res == UNWIND_DONE)
(*count)++;

return res;
}

/* This prevents the linking of libgcc unwinder code */
void __aeabi_unwind_cpp_pr0(void);
void __aeabi_unwind_cpp_pr1(void);
Expand Down Expand Up @@ -340,24 +380,26 @@ static int unwind_frame(backtrace_frame_t *frame)
return 1;
}

int _backtrace_unwind(backtrace_t *buffer, int size, backtrace_frame_t *frame)
int _backtrace_unwind(unwind_trace_fn callback, void *context, backtrace_frame_t *frame)
{
int count = 0;

/* Initialize the backtrace frame buffer */
memset(buffer, 0, sizeof(backtrace_t) * size);
backtrace_t backtrace;

/* Unwind all frames */
do {
memset(&backtrace, 0, sizeof(backtrace_t));

if (frame->pc == 0) {
/* Reached __exidx_end. */
buffer[count++].name = "<reached end of unwind table>";
backtrace.name = "<reached end of unwind table>";
collect_backtrace(callback, context, &backtrace, &count);
break;
}

if (frame->pc == 0x00000001) {
/* Reached .cantunwind instruction. */
buffer[count++].name = "<reached .cantunwind>";
backtrace.name = "<reached .cantunwind>";
collect_backtrace(callback, context, &backtrace, &count);
break;
}

Expand All @@ -368,14 +410,14 @@ int _backtrace_unwind(backtrace_t *buffer, int size, backtrace_frame_t *frame)
frame->pc &= 0xfffffffeU;

/* Generate the backtrace information */
buffer[count].address = (void *)frame->pc;
buffer[count].function = (void *)prel31_to_addr(&index->addr_offset);
buffer[count].name = unwind_get_function_name(buffer[count].function);
backtrace.address = (void *)frame->pc;
backtrace.function = (void *)prel31_to_addr(&index->addr_offset);
backtrace.name = unwind_get_function_name(backtrace.function);

/* Next backtrace frame */
++count;
if(collect_backtrace(callback, context, &backtrace, &count) != UNWIND_CONTINUE)
break;

} while (unwind_frame(frame) && count < size);
} while (unwind_frame(frame));

/* All done */
return count;
Expand All @@ -389,3 +431,19 @@ const char *backtrace_function_name(uint32_t pc)

return unwind_get_function_name((void *)prel31_to_addr(&index->addr_offset));
}

int backtrace_unwind_from_frame(backtrace_t *buffer, int size, backtrace_frame_t *frame)
{
default_unwind_context_t context =
{
.buffer = buffer,
.buffer_end = buffer + size
};

return _backtrace_unwind(default_unwind_callback, &context, frame);
}

int backtrace_unwind_from_frame_cb(unwind_trace_fn callback, void *context, backtrace_frame_t *frame)
{
return _backtrace_unwind(callback, context, frame);
}
33 changes: 25 additions & 8 deletions include/backtrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,23 @@ typedef struct unwind_index
uint32_t insn;
} unwind_index_t;

/* These symbols point to the unwind index and should be provide by the linker script */
extern const unwind_index_t __exidx_start[];
extern const unwind_index_t __exidx_end[];
typedef enum
{
UNWIND_CONTINUE,
UNWIND_DONE,
UNWIND_ABORT
} unwind_code_t;

typedef unwind_code_t (*unwind_trace_fn)(backtrace_t *trace, void *context);

int _backtrace_unwind(backtrace_t *buffer, int size, backtrace_frame_t *frame);
const char *backtrace_function_name(uint32_t pc);
int backtrace_unwind_from_frame(backtrace_t *buffer, int size, backtrace_frame_t *frame);
int backtrace_unwind_from_frame_cb(unwind_trace_fn callback, void *context, backtrace_frame_t *frame);

static inline int __attribute__((always_inline)) backtrace_unwind(backtrace_t *buffer, int size)
static inline backtrace_frame_t __attribute__((always_inline)) construct_backtrace_frame()
{
/* Get the current pc */
register uint32_t pc;
uint32_t pc;
__asm__ volatile("mov %0, pc" : "=r"(pc));

/* Initialize the stack frame */
Expand All @@ -60,8 +66,19 @@ static inline int __attribute__((always_inline)) backtrace_unwind(backtrace_t *b
frame.lr = (uint32_t)__builtin_return_address(0);
frame.pc = pc;

/* Let it rip */
return _backtrace_unwind(buffer, size, &frame);
return frame;
}

static inline int __attribute__((always_inline)) backtrace_unwind(backtrace_t *buffer, int size)
{
backtrace_frame_t frame = construct_backtrace_frame();
return backtrace_unwind_from_frame(buffer, size, &frame);
}

static inline int __attribute__((always_inline)) backtrace_unwind_cb(unwind_trace_fn callback, void *context)
{
backtrace_frame_t frame = construct_backtrace_frame();
return backtrace_unwind_from_frame_cb(callback, context, &frame);
}

#endif /* BACKTRACE_H_ */