image_pdfimage_print

Debugging embedded bootloaders can often feel like working in the dark. When execution crashes or behaves unexpectedly, the first question an engineer usually asks is, “How did I get here?”

Of course, one should always have a JTAG debugger ready to press into service, but so few boards provide a JTAG header.

To help, a recent series introduces a standardised Backtrace Library and a corresponding command to U-Boot. While currently implemented for the sandbox architecture, the design provides a generic API that paves the way for runtime stack traces across other architectures in the future.

The Power of Context

Previously, getting a call stack in U-Boot often meant attaching an external debugger (like GDB) or manually instrumenting code with print statements. This series adds runtime support to look at the backtrace directly from the console.

With the new backtrace command, developers can instantly see the call stack, complete with function names, source filenames, and line numbers.

How it Works

The implementation is split into a generic library and architecture-specific drivers:

  1. Generic API (include/backtrace.h): Provides functions like backtrace_init() to collect the stack and backtrace_show() to print it.
  2. Sandbox Implementation: For sandbox, we use the host’s libbacktrace (bundled with GCC). This allows U-Boot to read the DWARF debug information generated during the build to resolve addresses into human-readable symbols.

Cleaner Output

Raw backtraces can be messy, often printing absolute paths that clutter the terminal. A nice touch in this series (specifically Patch 9) is the automatic stripping of the source tree prefix.

Before:

run_command_list() at /home/user/workspace/u-boot/common/cli.c:168

After:

run_command_list() at common/cli.c:168

Example Usage

Once enabled with CONFIG_CMD_BACKTRACE, using the tool is straightforward:

=> backtrace
backtrace: 14 addresses
  backtrace_show() at lib/backtrace.c:18
  do_backtrace() at cmd/backtrace.c:17
  cmd_process() at common/command.c:637
  run_list_real() at common/cli_hush.c:1868
  parse_stream_outer() at common/cli_hush.c:3207
  parse_string_outer() at common/cli_hush.c:3257
  run_command_list() at common/cli.c:168
  sandbox_main_loop_init() at arch/sandbox/cpu/start.c:153
  board_init_r() at common/board_r.c:774
  ...

Looking Ahead

Currently this feature relies on libbacktrace and is available only for sandbox. However, the infrastructure is now in place. We are looking forward to seeing architecture maintainers implement the backing backtrace_init logic for ARM, RISC-V, and x86 to bring this powerful debugging capability to physical hardware.

Author

  • Simon Glass is a primary author of U-Boot, with around 10K commits. He is maintainer of driver model and various other subsystems in U-Boot.

One Comment

  1. This backtrace command is a great addition to U-Boot, especially for debugging embedded systems without JTAG support. Being able to instantly access the call stack from the console should save a lot of time and effort, and I’m excited to see how it expands to other architectures.

Leave a Reply

Your email address will not be published. Required fields are marked *