Backtrace User Guide

[ English | 简体中文 ]

I. Overview

In daily development, we often need to check the stack information of a specified thread, common scenarios include:

  • Deadlock
  • High CPU usage
  • Busy Loop
  • System crash
  • Memory debugging

Typically, with the help of debugging tools such as JLink, this can be done in the form of 'gdb' and breakpoints (bps). However, after device packet publishing and peripheral debugging are turned off, these features are often unusable on real devices.

To solve this problem, openvela allows you to view the stack information of a specific thread in the runtime environment.

II. Configuration Description

1. General configuration description

openvela supports common configurations for ARM, RISC-V, and Xtensa architectures, as follows:

# If the backtrace function is enabled, the function name will not be displayed by default
CONFIG_SCHED_BACKTRACE=y
CONFIG_SYSTEM_DUMPSTACK=y

# If you need symbol name support, enable the following options;
# If the Flash space is insufficient, you can manually parse the address to the symbol by addr2line
CONFIG_ALLSYMS=y

# Enable schema support
CONFIG_ARCH_HAVE_BACKTRACE=y

For the ARM platform, due to code resource limitations and Thumb-2 feature requirements, the following three backtrace implementations are available:

# Backtracking implementation based on frame pointer
CONFIG_UNWINDER_FRAME_POINTER

# Backtracking implementation based on stack pointer
CONFIG_UNWINDER_STACK_POINTER

# Backtracking implementation of .exidx segments based on ARM architecture
CONFIG_UNWINDER_ARM

Currently, openvela supports the backtrace function of ARM, RISC-V, and Xtensa architectures, and the configuration and implementation of each architecture are as follows.

2. ARM

ARM Cortex-A/R

In the ARM Cortex-A and Cortex-R families, the backtrace implementation supports one of two ways:

# Method 1: Backtrace implementation based on fp register 
CONFIG_UNWINDER_FRAME_POINTER 

# Method 2: Backtrace implementation based on .exidx section 
CONFIG_UNWINDER_ARM 

``` #### ARM Cortex-M 

In the ARM Cortex-M series, there are three implementations of backtrace, choose one according to project needs:

 ```Makefile 
 # Method 1: Backtrace implementation based on fp (注意编译器限制) 
 CONFIG_UNWINDER_FRAME_POINTER=y 
 
 # Method 2: Backtrace implementation based on sp, suitable for low-resource devices 
 CONFIG_UNWINDER_STACK_POINTER=y 

 # Method 3: Backtrace implementation based on .exidx section 
 CONFIG_UNWINDER_ARM=y 
  • CONFIG_UNWINDER_FRAME_POINTER: There are related limitations on GCC compiler (for details refer to GCC issues).

  • CONFIG_UNWINDER_STACK_POINTER: Obtains pc address through bl/blx instructions, suitable for resource-constrained devices, but has a certain probability of misjudgment. If the project code is scattered across multiple regions, multiple code regions need to be configured through the following API:

    void up_backtrace_init_code_regions(FAR void **regions) 
    
  • CONFIG_UNWINDER_ARM: Generates .exidx section during compilation, increasing the code size by 5%-8%.

3. RISC-V

In the RISC-V architecture, backtrace is implemented based on the frame pointer. Just enable the following configuration:

CONFIG_FRAME_POINTER=y 
CONFIG_SCHED_BACKTRACE=y 

For more information, refer to: RISC-V Backtrace Implementation.

4. Xtensa

In the Xtensa architecture, backtrace supports stack unwinding by default, and there is no need to enable the -fno-omit-frame-pointer option additionally.

III. Usage To use the backtrace series of functions

In the code, the header file #include <execinfo.h> must be included. This chapter describes how to use backtrace to obtain function call stack information, print logs, and locate error lines.

1. Using backtrace in code

Using the backtrace series of functions can capture the current program state and print stack information. Below is a description of common functions.

For more details, please refer to the Linux Man Page: backtrace.

#include <execinfo.h> 

/* Store the current program state in the __array array. The maximum number of elements in array is __size. The return value is the actual number of elements stored */ 
extern int backtrace (void **__array, int __size) __nonnull ((1)); 

/* Function: Returns an array of strings that contains the information obtained from backtrace. Parameters: - __array: Pointer array returned by backtrace - __size: Return value of backtrace Note: This method will call `backtrace_malloc(FAR void *const *buffer, int size)` to allocate space, defined in nuttx/libs/libc/misc/lib_execinfo.c. */
 extern char **backtrace_symbols (void *const *__array, int __size) 
    __THROW __nonnull ((1)); 

/* Function: Similar to backtrace_symbols, but does not require additional space; it writes directly to the file descriptor __fd. 
*/ 
extern void backtrace_symbols_fd (void *const *__array, int __size, int __fd) __THROW __nonnull ((1));

2. dump_stack()

  1. In the openvela system, you can directly call the dump_stack() function to print stack information. For example:

    // Add test code in the examples/hello/hello_main.c file.
    37 int main(int argc, FAR char *argv[])
    38 {
    39   printf("Hello, World!!\n");
    +40   dump_stack();  // Print stack information  
    41   return 0;
    42 }
    

    Print output example:

    ap> hello
    Hello, World!!
    [07:06:21] [28] [  INFO] [BackTrace|28|0]:   0xc070a96  0xc063d7c  0xc0809bc  0xc063d38  0xc0587de
    
  2. Use the addr2line tool to resolve the printed backtrace addresses to specific code lines:

    addr2line -fe nuttx 0xc070a96  0xc063d7c  0xc0809bc  0xc063d38  0xc0587de  
    

    Example of analysis results:

    nuttx/arch/arm/src/armv8-m/arm_backtrace.c:446
    nuttx/libs/libc/sched/sched_dumpstack.c:60
    apps/examples/hello/hello_main.c:40
    nuttx/libs/libc/sched/task_startup.c:151
    nuttx/sched/task/task_start.c:130
    
  3. Use the addresses from dump_stack() to locate the point of error, or print function names directly for easier analysis by allsyms symbol table feature usage guide.

3. dumpstack Command

In the command line, you can use the dumpstack command to obtain the backtrace of a specified task. Here is how to use it:

Use dumpstack [pid] to get the task stack

  1. Obtain the stack of the process.

    # Check Process Status
    ap> ps
    ...
    26       100 RR       Task    --- Waiting  Semaphore 00000010 004056 000648  15.9%    0.0% Telnet daemon 0x3c4293e0
    
    # View the backtrace of PID 26
    ap> dumpstack 26
    [15:18:53] [29] [  INFO] [BackTrace|26|0]:   0xc06b1c2   0x2154aa   0x20f312  0xc2ca118  0xc2c9bc4  0xc2c9c34  0xc08d894  0xc063d38
    [15:18:53] [29] [  INFO] [BackTrace|26|1]:   0xc0587de
    
  2. Use addr2line to resolve the backtrace address.

    addr2line -e nuttx 0xc06b1c2   0x2154aa   0x20f312  0xc2ca118  0xc2c9bc4  0xc2c9c34  0xc08d894  0xc063d38
    
  3. Check the analysis results.

    An example of the analysis results is as follows:

    nuttx/arch/arm/src/armv8-m/arm_switchcontext.S:79
    nuttx/net/utils/net_lock.c:128
    nuttx/net/tcp/tcp_accept.c:281
    nuttx/net/inet/inet_sockif.c:889
    nuttx/net/socket/accept.c:141 (discriminator 4)
    nuttx/net/socket/accept.c:270
    apps/netutils/telnetd/telnetd_daemon.c:240 (discriminator 3)
    nuttx/libs/libc/sched/task_startup.c:151
    

Use dumpstack [pid_start] [pid_end] to view the stack of multiple processes

View the backtrace information for PIDs from 0 to 26:

ap> dumpstack 0 26  
[15:21:07] [30] [  INFO] [BackTrace|0|0]:  0xc054ba0  0xc3129f0  0xc000056  
[15:21:07] [30] [  INFO] [BackTrace|1|0]:  0xc06b1c2  0xc0572e2  0xc0595e6  0xc0587d4  
[15:21:07] [30] [  INFO] [BackTrace|26|0]:  0xc06b1c2  0xc2154aa  0xc0587d4  
...  

4. Call stack analysis in crash logs

[15:21:21] [25] [  INFO] [BackTrace|25|0]:   0xc070aa6  0xc063d7c  0xc070f58  0xc060948  0xc06b2a2  0xc054d1e  0xc070dc8  0xc06b21e
[15:21:21] [25] [  INFO] [BackTrace|25|1]:   0xc087556  0xc0847fa  0xc087864  0xc0889b4  0xc0837a8  0xc063d38  0xc0587de

crash thread 25:

$ addr2line -e nuttx 0xc070aa6  0xc063d7c  0xc070efc  0xc0569c8  0xc070fba  0xc060948  0xc06b2a2  0xc054d1e 0xc070dc8  0xc06b21e  0xc087556  0xc0847fa  0xc087864  0xc0889b4  0xc0837a8  0xc063d38
nuttx/arch/arm/src/armv8-m/arm_backtrace.c:446
nuttx/libs/libc/sched/sched_dumpstack.c:60
nuttx/arch/arm/src/armv8-m/arm_assert.c:167