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/blxinstructions, 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()
-
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 -
Use the
addr2linetool to resolve the printed backtrace addresses to specific code lines:addr2line -fe nuttx 0xc070a96 0xc063d7c 0xc0809bc 0xc063d38 0xc0587deExample 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 -
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
-
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 -
Use
addr2lineto resolve the backtrace address.addr2line -e nuttx 0xc06b1c2 0x2154aa 0x20f312 0xc2ca118 0xc2c9bc4 0xc2c9c34 0xc08d894 0xc063d38 -
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