I want my exception handlers and debug functions to be able to print call stack backtraces, basically just like the backtrace() library function in glibc. Unfortunately, my C li
This is hacky, but I've found it works good enough considering the amount of code/RAM space required:
Assuming you're using ARM THUMB mode, compile with the following options:
-mtpcs-frame -mtpcs-leaf-frame -fno-omit-frame-pointer
The following function is used to retrieve the callstack. Refer to the comments for more info:
/*
* This should be compiled with:
* -mtpcs-frame -mtpcs-leaf-frame -fno-omit-frame-pointer
*
* With these options, the Stack pointer is automatically pushed to the stack
* at the beginning of each function.
*
* This function basically iterates through the current stack finding the following combination of values:
* -
* -
*
* This combination will occur for each function in the call stack
*/
static void backtrace(uint32_t *caller_list, const uint32_t *caller_list_end, const uint32_t *stack_pointer)
{
uint32_t previous_frame_address = (uint32_t)stack_pointer;
uint32_t stack_entry_counter = 0;
// be sure to clear the caller_list buffer
memset(caller_list, 0, caller_list_end-caller_list);
// loop until the buffer is full
while(caller_list < caller_list_end)
{
// Attempt to obtain next stack pointer
// The link address should come immediately after
const uint32_t possible_frame_address = *stack_pointer;
const uint32_t possible_link_address = *(stack_pointer+1);
// Have we searched past the allowable size of a given stack?
if(stack_entry_counter > PLATFORM_MAX_STACK_SIZE/4)
{
// yes, so just quite
break;
}
// Next check that the frame addresss (i.e. stack pointer for the function)
// and Link address are within an acceptable range
else if((possible_frame_address > previous_frame_address) &&
((possible_frame_address < previous_frame_address + PLATFORM_MAX_STACK_SIZE)) &&
((possible_link_address & 0x01) != 0) && // in THUMB mode the address will be odd
(possible_link_address > PLATFORM_CODE_SPACE_START_ADDRESS &&
possible_link_address < PLATFORM_CODE_SPACE_END_ADDRESS))
{
// We found two acceptable values
// Store the link address
*caller_list++ = possible_link_address;
// Update the book-keeping registers for the next search
previous_frame_address = possible_frame_address;
stack_pointer = (uint32_t*)(possible_frame_address + 4);
stack_entry_counter = 0;
}
else
{
// Keep iterating through the stack until be find an acceptable combination
++stack_pointer;
++stack_entry_counter;
}
}
}
You'll need to update #defines for your platform.
Then call the following to populate a buffer with the current call stack:
uint32_t callers[8];
uint32_t sp_reg;
__ASM volatile ("mov %0, sp" : "=r" (sp_reg) );
backtrace(callers, &callers[8], (uint32_t*)sp_reg);
Again, this is rather hacky, but I've found it to work quite well. The buffer will be populated with link addresses of each function call in the call stack.