How to get a call stack backtrace? (deeply embedded, no library support)

前端 未结 6 1412

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

6条回答
  •  攒了一身酷
    2021-01-31 11:07

    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.

提交回复
热议问题