[PATCH 00/10] backtrace: Add runtime support for looking at the backtrace
From: Simon Glass <simon.glass@canonical.com> In some cases the backtrace contains useful information, such as whether a particular function was called earlier in the stack. This series provides a very simple backtrace library, along with some sandbox-specific functions to allow it to work. It is designed such that another arch could implement it. A new 'backtrace' command provides access to the backtrace. Simon Glass (10): claude: Tell it about my crosfw script being silent backtrace: Add a library to access the backtrace CI: Disable backtrace for the trace test backtrace: sandbox: Add support for obtaining symbols backtrace: sandbox: Link with libbacktraces backtrace: sandbox: Add -rdynamic to export symbols backtrace: Add a test backtrace: Add a command backtrace: doc: Mention the feature backtrace: Strip the source tree prefix from filenames .gitlab-ci.yml | 2 +- CLAUDE.md | 1 + arch/sandbox/config.mk | 13 +++- arch/sandbox/cpu/Makefile | 7 +- arch/sandbox/cpu/backtrace.c | 122 +++++++++++++++++++++++++++++++++++ arch/sandbox/lib/Makefile | 1 + arch/sandbox/lib/backtrace.c | 71 ++++++++++++++++++++ cmd/Kconfig | 8 +++ cmd/Makefile | 1 + cmd/backtrace.c | 30 +++++++++ configs/sandbox_defconfig | 1 + doc/arch/sandbox/sandbox.rst | 17 +++++ doc/usage/cmd/backtrace.rst | 51 +++++++++++++++ doc/usage/index.rst | 1 + examples/rust/build.rs | 3 + examples/ulib/config.mk | 2 +- include/backtrace.h | 72 +++++++++++++++++++++ include/os.h | 34 ++++++++++ lib/Kconfig | 8 +++ lib/Makefile | 2 + lib/backtrace.c | 55 ++++++++++++++++ test/cmd/Makefile | 1 + test/cmd/backtrace.c | 27 ++++++++ test/lib/Makefile | 1 + test/lib/backtrace.c | 47 ++++++++++++++ 25 files changed, 574 insertions(+), 4 deletions(-) create mode 100644 arch/sandbox/cpu/backtrace.c create mode 100644 arch/sandbox/lib/backtrace.c create mode 100644 cmd/backtrace.c create mode 100644 doc/usage/cmd/backtrace.rst create mode 100644 include/backtrace.h create mode 100644 lib/backtrace.c create mode 100644 test/cmd/backtrace.c create mode 100644 test/lib/backtrace.c -- 2.43.0 base-commit: 6d3cb76fe2af7453ed5bfeaacbbaedb0be087e03 branch: mal
From: Simon Glass <simon.glass@canonical.com> Add a note about this. At some point we should create a proper tools for IDE use in U-Boot. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- CLAUDE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CLAUDE.md b/CLAUDE.md index 374f24ba1f9..a13dd995b68 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -59,6 +59,7 @@ pyt <test_name> - Always run `make mrproper` if you encounter build issues - The sandbox build creates a test environment for U-Boot that runs on the host system - When using `git diff`, add `--no-ext-diff` to avoid external diff tools that may not work in this environment +- crosfw shows now output if everything was ok! ## Coding Conventions -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Provide an API to access the backtrace, in an arch-neutral way. The backtrace can be retrieved, examined and printed. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- include/backtrace.h | 72 +++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig | 8 +++++ lib/Makefile | 1 + lib/backtrace.c | 40 +++++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 include/backtrace.h create mode 100644 lib/backtrace.c diff --git a/include/backtrace.h b/include/backtrace.h new file mode 100644 index 00000000000..eece61e4d9a --- /dev/null +++ b/include/backtrace.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Backtrace support + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#ifndef __BACKTRACE_H +#define __BACKTRACE_H + +#define BACKTRACE_MAX 100 +#define BACKTRACE_SYM_SIZE 128 +#define BACKTRACE_BUFSZ (BACKTRACE_MAX * BACKTRACE_SYM_SIZE) + +/** + * struct backtrace_ctx - context for backtrace operations + * + * @addrs: array of return addresses + * @syms: array of symbol strings (NULL until backtrace_get_syms() called) + * @count: number of entries in addrs/syms arrays + */ +struct backtrace_ctx { + void *addrs[BACKTRACE_MAX]; + char *syms[BACKTRACE_MAX]; + unsigned int count; +}; + +/** + * backtrace_init() - collect a backtrace + * + * Collect backtrace addresses into the context. Call backtrace_uninit() when + * done with the context. + * + * @ctx: context to fill + * @skip: number of stack frames to skip (0 to include backtrace_init itself) + * Return: number of addresses collected, or -ve on error (e.g. -ENOSYS) + */ +int backtrace_init(struct backtrace_ctx *ctx, unsigned int skip); + +/** + * backtrace_get_syms() - get symbol strings for a backtrace + * + * Convert the addresses in the context to symbol strings. The strings are + * stored in ctx->syms[]. The caller must provide a buffer of sufficient size. + * + * @ctx: context with addresses from backtrace_init() + * @buf: buffer to use for string storage + * @size: size of buffer in bytes + * Return: 0 if OK, -ENOSPC if buffer too small + */ +int backtrace_get_syms(struct backtrace_ctx *ctx, char *buf, int size); + +/** + * backtrace_uninit() - free backtrace resources + * + * Free any memory allocated in the context. + * + * @ctx: context to free + */ +void backtrace_uninit(struct backtrace_ctx *ctx); + +/** + * backtrace_show() - print a backtrace + * + * Print a backtrace of the current call stack. + * + * Return: 0 if OK, -ve on error + */ +int backtrace_show(void); + +#endif /* __BACKTRACE_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 662b1a44d45..d7f791f77f3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -28,6 +28,14 @@ config PHYSMEM Enable this to access this basic support, which only supports clearing the memory. +config BACKTRACE + bool "Enable backtrace support" + depends on SANDBOX + help + Enables support for printing a backtrace showing the current call + stack. This is currently only available on sandbox. The backtrace + command can be used to print the backtrace. + config BCH bool "Enable Software based BCH ECC" help diff --git a/lib/Makefile b/lib/Makefile index 5cbf3071f96..b696e81c496 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -147,6 +147,7 @@ obj-$(CONFIG_TRACE) += trace.o obj-$(CONFIG_LIB_UUID) += uuid.o obj-$(CONFIG_LIB_RAND) += rand.o obj-y += panic.o +obj-$(CONFIG_BACKTRACE) += backtrace.o ifeq ($(CONFIG_XPL_BUILD),y) # SPL U-Boot may use full-printf, tiny-printf or none at all diff --git a/lib/backtrace.c b/lib/backtrace.c new file mode 100644 index 00000000000..e3d93b80b8a --- /dev/null +++ b/lib/backtrace.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Stack-backtrace support + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <backtrace.h> +#include <stdio.h> + +int backtrace_show(void) +{ + char buf[BACKTRACE_BUFSZ]; + struct backtrace_ctx ctx; + uint i; + int ret; + + ret = backtrace_init(&ctx, 1); + if (ret < 0) + return ret; + + ret = backtrace_get_syms(&ctx, buf, sizeof(buf)); + if (ret) { + backtrace_uninit(&ctx); + return ret; + } + + printf("backtrace: %d addresses\n", ctx.count); + for (i = 0; i < ctx.count; i++) { + if (ctx.syms[i]) + printf(" %s\n", ctx.syms[i]); + else + printf(" %p\n", ctx.addrs[i]); + } + + backtrace_uninit(&ctx); + + return 0; +} -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> This test expects to only sese a subset of the symbols in the executable. Disable backtrace to avoid dealing with changes in this area. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 68f2534918c..0d63bd4c358 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -480,7 +480,7 @@ sandbox trace_test.py: TEST_PY_BD: "sandbox" BUILD_ENV: "FTRACE=1 NO_LTO=1" TEST_PY_TEST_SPEC: "trace" - OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000 -a CONFIG_TRACE_BUFFER_SIZE=0x02000000" + OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000 -a CONFIG_TRACE_BUFFER_SIZE=0x02000000 -a ~CONFIG_BACKTRACE" <<: *buildman_and_testpy_dfn evb-ast2500 test.py: -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add backtrace functions for sandbox, including: - os_backtrace() to collect addresses into a caller-supplied buffer - os_backtrace_symbols() to convert addresses to symbol strings - os_backtrace_symbols_free() to free the symbol array The libbacktrace library (bundled with GCC) reads DWARF debug information to provide detailed symbol resolution including function names (even for static functions), source file paths, and line numbers. The sandbox backtrace implementation wraps these OS functions to implement the generic backtrace API (backtrace_init, backtrace_get_syms, etc.). Enable it for just the 'sandbox' board. Add the library for the Rust example too. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/sandbox/cpu/Makefile | 7 +- arch/sandbox/cpu/backtrace.c | 122 +++++++++++++++++++++++++++++++++++ arch/sandbox/lib/Makefile | 1 + arch/sandbox/lib/backtrace.c | 71 ++++++++++++++++++++ configs/sandbox_defconfig | 1 + examples/rust/build.rs | 3 + examples/ulib/config.mk | 2 +- include/os.h | 34 ++++++++++ 8 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 arch/sandbox/cpu/backtrace.c create mode 100644 arch/sandbox/lib/backtrace.c diff --git a/arch/sandbox/cpu/Makefile b/arch/sandbox/cpu/Makefile index ef9a01c5d7c..212cf14fe37 100644 --- a/arch/sandbox/cpu/Makefile +++ b/arch/sandbox/cpu/Makefile @@ -15,9 +15,14 @@ extra-y := start.o extra-$(CONFIG_SANDBOX_SDL) += sdl.o obj-$(CONFIG_XPL_BUILD) += spl.o obj-$(CONFIG_ETH_SANDBOX_RAW) += eth-raw-os.o +obj-$(CONFIG_BACKTRACE) += backtrace.o # Compile these files with system headers -CFLAGS_USE_SYSHDRS := eth-raw-os.o fuzz.o main.o os.o sdl.o tty.o +CFLAGS_USE_SYSHDRS := backtrace.o eth-raw-os.o fuzz.o main.o os.o sdl.o tty.o + +# backtrace.c needs libbacktrace header from GCC +LIBBT_INC := $(dir $(shell $(CC) -print-file-name=include/backtrace.h)) +CFLAGS_backtrace.o += -isystem $(LIBBT_INC) # sdl.c fails to build with -fshort-wchar using musl cmd_cc_sdl.o = $(CC) $(filter-out -nostdinc -fshort-wchar, \ diff --git a/arch/sandbox/cpu/backtrace.c b/arch/sandbox/cpu/backtrace.c new file mode 100644 index 00000000000..1f5a14ed541 --- /dev/null +++ b/arch/sandbox/cpu/backtrace.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OS-level backtrace support for sandbox + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#define _GNU_SOURCE + +#include <backtrace.h> +#include <execinfo.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <os.h> + +/* libbacktrace state - created once and cached */ +static struct backtrace_state *bt_state; + +/* Context for collecting symbol info */ +struct bt_sym_ctx { + char *buf; + size_t size; + int found; +}; + +uint os_backtrace(void **buffer, uint size, uint skip) +{ + void *tmp[size + skip]; + uint count; + int nptrs; + + nptrs = backtrace(tmp, size + skip); + if ((int)skip >= nptrs) + return 0; + + count = nptrs - skip; + memcpy(buffer, tmp + skip, count * sizeof(*buffer)); + + return count; +} + +static void bt_error_callback(void *data, const char *msg, int errnum) +{ + /* Silently ignore errors - we'll fall back to addresses */ +} + +static struct backtrace_state *get_bt_state(void) +{ + if (!bt_state) + bt_state = backtrace_create_state(NULL, 0, bt_error_callback, + NULL); + + return bt_state; +} + +static int bt_full_callback(void *data, uintptr_t pc, const char *fname, + int lineno, const char *func) +{ + struct bt_sym_ctx *ctx = data; + + if (func) { + if (fname && lineno) + snprintf(ctx->buf, ctx->size, "%s() at %s:%d", func, + fname, lineno); + else if (fname) + snprintf(ctx->buf, ctx->size, "%s() at %s", func, + fname); + else + snprintf(ctx->buf, ctx->size, "%s()", func); + ctx->found = 1; + } + + return 0; /* continue to get innermost frame for inlined functions */ +} + +char **os_backtrace_symbols(void *const *buffer, uint count) +{ + struct backtrace_state *state; + char *str_storage; + char **strings; + uint i; + + state = get_bt_state(); + + /* Allocate array of string pointers plus space for strings */ + strings = malloc(count * sizeof(char *) + count * 256); + if (!strings) + return NULL; + + /* String storage starts after the pointer array */ + str_storage = (char *)(strings + count); + + for (i = 0; i < count; i++) { + struct bt_sym_ctx ctx; + + strings[i] = str_storage + i * 256; + ctx.buf = strings[i]; + ctx.size = 256; + ctx.found = 0; + + if (state) { + backtrace_pcinfo(state, (uintptr_t)buffer[i], + bt_full_callback, bt_error_callback, + &ctx); + } + + /* Fall back to address if no symbol found */ + if (!ctx.found) + snprintf(strings[i], 256, "%p", buffer[i]); + } + + return strings; +} + +void os_backtrace_symbols_free(char **strings) +{ + free(strings); +} diff --git a/arch/sandbox/lib/Makefile b/arch/sandbox/lib/Makefile index edb650c48da..563a5c33156 100644 --- a/arch/sandbox/lib/Makefile +++ b/arch/sandbox/lib/Makefile @@ -5,6 +5,7 @@ # (C) Copyright 2002-2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. +obj-$(CONFIG_BACKTRACE) += backtrace.o obj-y += fdt_fixup.o interrupts.o obj-$(CONFIG_PCI) += pci_io.o obj-$(CONFIG_BOOT) += bootm.o diff --git a/arch/sandbox/lib/backtrace.c b/arch/sandbox/lib/backtrace.c new file mode 100644 index 00000000000..073eb945622 --- /dev/null +++ b/arch/sandbox/lib/backtrace.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Backtrace support for sandbox + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <backtrace.h> +#include <errno.h> +#include <os.h> +#include <string.h> + +int backtrace_init(struct backtrace_ctx *ctx, uint skip) +{ + uint i; + + for (i = 0; i < BACKTRACE_MAX; i++) + ctx->syms[i] = NULL; + /* +1 to skip this function */ + ctx->count = os_backtrace(ctx->addrs, BACKTRACE_MAX, skip + 1); + + return ctx->count; +} + +int backtrace_get_syms(struct backtrace_ctx *ctx, char *buf, int size) +{ + char **raw_syms; + size_t total_len; + char *p; + uint i; + + raw_syms = os_backtrace_symbols(ctx->addrs, ctx->count); + if (!raw_syms) + return -ENOMEM; + + /* Calculate total buffer size needed */ + total_len = 0; + for (i = 0; i < ctx->count; i++) { + if (raw_syms[i]) + total_len += strlen(raw_syms[i]) + 1; + else + total_len += 1; /* empty string */ + } + + if ((size_t)size < total_len) { + os_backtrace_symbols_free(raw_syms); + return -ENOSPC; + } + + /* Copy strings into buffer */ + p = buf; + for (i = 0; i < ctx->count; i++) { + ctx->syms[i] = p; + if (raw_syms[i]) { + strcpy(p, raw_syms[i]); + p += strlen(raw_syms[i]) + 1; + } else { + *p++ = '\0'; + } + } + + os_backtrace_symbols_free(raw_syms); + + return 0; +} + +void backtrace_uninit(struct backtrace_ctx *ctx) +{ + /* Nothing to free - caller owns the buffer */ +} diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 006c6916af6..55f8ddcc952 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -368,6 +368,7 @@ CONFIG_TPM=y CONFIG_ERRNO_STR=y CONFIG_GETOPT=y CONFIG_ARGON2=y +CONFIG_BACKTRACE=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y diff --git a/examples/rust/build.rs b/examples/rust/build.rs index 9c713ba574c..6be6a21d714 100644 --- a/examples/rust/build.rs +++ b/examples/rust/build.rs @@ -65,6 +65,9 @@ fn main() { // Fallback to just SDL2 if sdl2-config is not available println!("cargo:rustc-link-arg=-lSDL2"); } + + // Link with libbacktrace for backtrace support on sandbox + println!("cargo:rustc-link-arg=-lbacktrace"); } // For dynamic linking, link required system libraries normally diff --git a/examples/ulib/config.mk b/examples/ulib/config.mk index 993f0923b4f..5e2ffa5a4fe 100644 --- a/examples/ulib/config.mk +++ b/examples/ulib/config.mk @@ -44,4 +44,4 @@ SHARED_LDFLAGS := -L$(UBOOT_BUILD) -lu-boot -Wl,-rpath,$(UBOOT_BUILD) STATIC_LDFLAGS := -Wl,-T,$(LIB_STATIC_LDS) \ -Wl,--whole-archive $(UBOOT_BUILD)/libu-boot.a \ -Wl,--no-whole-archive \ - -lpthread -ldl $(PLATFORM_LIBS) -Wl,-z,noexecstack + -lpthread -ldl -lbacktrace $(PLATFORM_LIBS) -Wl,-z,noexecstack diff --git a/include/os.h b/include/os.h index 1b2243d46d4..ab4710fc265 100644 --- a/include/os.h +++ b/include/os.h @@ -576,6 +576,40 @@ int os_setup_signal_handlers(void); */ void os_signal_action(int sig, unsigned long pc); +/** + * os_backtrace() - get backtrace addresses + * + * Collect backtrace addresses into a caller-supplied buffer. + * + * @buffer: array to fill with return addresses + * @size: maximum number of entries in buffer + * @skip: number of stack frames to skip (0 to include os_backtrace itself) + * Return: number of addresses collected + */ +uint os_backtrace(void **buffer, uint size, uint skip); + +/** + * os_backtrace_symbols() - convert addresses to symbol strings + * + * Convert backtrace addresses to human-readable symbol strings. The returned + * array and strings are allocated with malloc() and must be freed with + * os_backtrace_symbols_free(). + * + * @buffer: array of addresses from os_backtrace() + * @count: number of addresses in buffer + * Return: array of symbol strings, or NULL on error + */ +char **os_backtrace_symbols(void *const *buffer, uint count); + +/** + * os_backtrace_symbols_free() - free symbol strings + * + * Free the array returned by os_backtrace_symbols(). + * + * @strings: array to free (may be NULL) + */ +void os_backtrace_symbols_free(char **strings); + /** * os_get_time_offset() - get time offset * -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Link with libbacktrace when CONFIG_BACKTRACE is enabled. This library is bundled with GCC and provides DWARF-based symbol resolution for backtraces. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/sandbox/config.mk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index f80e2ef369f..7039b256d81 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -3,7 +3,13 @@ PLATFORM_CPPFLAGS += -D__SANDBOX__ -U_FORTIFY_SOURCE PLATFORM_CPPFLAGS += -fPIC -ffunction-sections -fdata-sections + +ifeq ($(CONFIG_BACKTRACE),y) +GCC_LIB_DIR := $(shell $(CC) -print-file-name=) +PLATFORM_LIBS += -L$(GCC_LIB_DIR) -lbacktrace +endif PLATFORM_LIBS += -lrt + SDL_CONFIG ?= sdl2-config # Define this to avoid linking with SDL, which requires SDL libraries -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add the -rdynamic linker flag so that backtrace_symbols() can resolve function names. This is needed to show meaningful symbol names instead of just addresses. Only enable this CONFIG_BACKTRACE and CONFIG_CMDLINE are enabled, since it causes build failures with the tools-only build and sandbox_nocmdline Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/sandbox/config.mk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 7039b256d81..b92c259d2e3 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -28,6 +28,11 @@ SANITIZERS += -fsanitize=fuzzer endif KBUILD_CFLAGS += $(SANITIZERS) +# Avoid defeating linker's garbage collection +ifeq ($(CONFIG_BACKTRACE)$(CONFIG_CMDLINE),yy) +RDYNAMIC += -rdynamic +endif + cmd_u-boot__ = $(CC) -o $@ -Wl,-T u-boot.lds $(u-boot-init) \ $(KBUILD_LDFLAGS:%=-Wl,%) \ $(SANITIZERS) \ @@ -36,7 +41,7 @@ cmd_u-boot__ = $(CC) -o $@ -Wl,-T u-boot.lds $(u-boot-init) \ $(u-boot-main) \ $(u-boot-keep-syms-lto) \ -Wl,--no-whole-archive \ - $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -Wl,--gc-sections + $(RDYNAMIC) $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -Wl,--gc-sections cmd_u-boot-spl = (cd $(obj) && $(CC) -o $(SPL_BIN) -Wl,-T u-boot-spl.lds \ $(KBUILD_LDFLAGS:%=-Wl,%) \ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a simple test for the backtrace library. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/lib/Makefile | 1 + test/lib/backtrace.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/lib/backtrace.c diff --git a/test/lib/Makefile b/test/lib/Makefile index 1d94d6604d5..8b322df3b45 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_$(PHASE_)UT_COMPRESSION) += compression.o ifeq ($(CONFIG_XPL_BUILD),) obj-y += abuf.o obj-y += alist.o +obj-$(CONFIG_BACKTRACE) += backtrace.o obj-$(CONFIG_EFI_LOADER) += efi_device_path.o obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o obj-$(CONFIG_EFI_LOG) += efi_log.o diff --git a/test/lib/backtrace.c b/test/lib/backtrace.c new file mode 100644 index 00000000000..d9c36bbd495 --- /dev/null +++ b/test/lib/backtrace.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for backtrace functions + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <backtrace.h> +#include <string.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test backtrace_init() and backtrace_get_syms() */ +static int lib_test_backtrace(struct unit_test_state *uts) +{ + char buf[BACKTRACE_BUFSZ]; + struct backtrace_ctx ctx; + bool found_self = false; + bool found_ut_run_list = false; + uint i; + + ut_assert(backtrace_init(&ctx, 0) > 2); + ut_assertok(backtrace_get_syms(&ctx, buf, sizeof(buf))); + + /* + * Check for known functions in the call stack. With libbacktrace + * we can find static functions too, so check for this test function. + */ + for (i = 0; i < ctx.count; i++) { + if (ctx.syms[i]) { + if (strstr(ctx.syms[i], "lib_test_backtrace")) + found_self = true; + if (strstr(ctx.syms[i], "ut_run_list")) + found_ut_run_list = true; + } + } + + ut_assert(found_self); + ut_assert(found_ut_run_list); + + backtrace_uninit(&ctx); + + return 0; +} +LIB_TEST(lib_test_backtrace, 0); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a new 'backtrace' command which prints the current call stack, which is useful for debugging. The command is enabled by CONFIG_CMD_BACKTRACE Add docs and a test. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- cmd/Kconfig | 8 ++++++ cmd/Makefile | 1 + cmd/backtrace.c | 30 ++++++++++++++++++++++ doc/usage/cmd/backtrace.rst | 51 +++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + test/cmd/Makefile | 1 + test/cmd/backtrace.c | 22 ++++++++++++++++ 7 files changed, 114 insertions(+) create mode 100644 cmd/backtrace.c create mode 100644 doc/usage/cmd/backtrace.rst create mode 100644 test/cmd/backtrace.c diff --git a/cmd/Kconfig b/cmd/Kconfig index a45df78c8fd..ff5f6f85144 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -136,6 +136,14 @@ config CMD_ADDR_FIND sufficiently large to hold a file. If successful, it sets the loadaddr variable to this address. +config CMD_BACKTRACE + bool "backtrace" + depends on BACKTRACE + default y if BACKTRACE + help + This command prints a backtrace showing the current call stack. + This can be useful for debugging. + config CMD_ADDRMAP bool "addrmap" depends on ADDR_MAP diff --git a/cmd/Makefile b/cmd/Makefile index 2c6a16752bd..ebf66ea0d3c 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -14,6 +14,7 @@ obj-y += version.o # command obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_2048) += 2048.o +obj-$(CONFIG_CMD_BACKTRACE) += backtrace.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDR_FIND) += addr_find.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o diff --git a/cmd/backtrace.c b/cmd/backtrace.c new file mode 100644 index 00000000000..c54ac057f16 --- /dev/null +++ b/cmd/backtrace.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Backtrace command + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <backtrace.h> +#include <command.h> + +static int do_backtrace(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + ret = backtrace_show(); + if (ret) { + printf("backtrace failed: %d\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +U_BOOT_CMD(backtrace, 1, 1, do_backtrace, + "Print backtrace", + "\n" + " - Print a backtrace of the current call stack" +); diff --git a/doc/usage/cmd/backtrace.rst b/doc/usage/cmd/backtrace.rst new file mode 100644 index 00000000000..37acb0b3067 --- /dev/null +++ b/doc/usage/cmd/backtrace.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: backtrace (command) + +backtrace command +================= + +Synopsis +-------- + +:: + + backtrace + +Description +----------- + +The *backtrace* command prints a backtrace of the current call stack. This can +be useful for debugging to see how a particular code path was reached. + +The output shows each stack frame with the function name, source file, and line +number (when debug information is available). This includes static functions. + +Example +------- + +:: + + => backtrace + backtrace: 14 addresses + backtrace_show() at /home/user/u-boot/lib/backtrace.c:17 + do_backtrace() at /home/user/u-boot/cmd/backtrace.c:18 + cmd_process() at /home/user/u-boot/common/command.c:637 + run_list_real() at /home/user/u-boot/common/cli_hush.c:1868 + parse_stream_outer() at /home/user/u-boot/common/cli_hush.c:3207 + parse_string_outer() at /home/user/u-boot/common/cli_hush.c:3257 + run_command_list() at /home/user/u-boot/common/cli.c:168 + sandbox_main_loop_init() at /home/user/u-boot/arch/sandbox/cpu/start.c:153 + board_init_r() at /home/user/u-boot/common/board_r.c:774 + ... + +Configuration +------------- + +The backtrace command is enabled by CONFIG_CMD_BACKTRACE which depends on +CONFIG_BACKTRACE. Currently this is only available on sandbox. + +The sandbox implementation uses libbacktrace (bundled with GCC) to provide +detailed symbol information including function names, source files, and line +numbers. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index e8dbabfa9d2..8913c0a4f9b 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -31,6 +31,7 @@ Shell commands cmd/addrmap cmd/armffa cmd/askenv + cmd/backtrace cmd/base cmd/bdinfo cmd/bind diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 4d8f93e2551..c43aefb4eb3 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -14,6 +14,7 @@ obj-y += exit.o obj-$(CONFIG_X86) += cpuid.o msr.o obj-$(CONFIG_CMD_ADDR_FIND) += addr_find.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o +obj-$(CONFIG_CMD_BACKTRACE) += backtrace.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o obj-$(CONFIG_CMD_CHID) += chid.o diff --git a/test/cmd/backtrace.c b/test/cmd/backtrace.c new file mode 100644 index 00000000000..2d999e20f31 --- /dev/null +++ b/test/cmd/backtrace.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for backtrace command + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test 'backtrace' command */ +static int cmd_test_backtrace(struct unit_test_state *uts) +{ + /* for now, just run the command */ + ut_assertok(run_command("backtrace", 0)); + + return 0; +} +DM_TEST(cmd_test_backtrace, UTF_SCAN_FDT); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a short section about the backtrace feature to the sandbox docs. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- doc/arch/sandbox/sandbox.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index fc2b7c482f4..9e9b027be8b 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -616,6 +616,23 @@ Here is an example trace:: Hit any key to stop autoboot: 1 +Backtrace Support +----------------- + +Sandbox supports printing a backtrace of the current call stack, which can be +useful for debugging. The :doc:`backtrace <../../usage/cmd/backtrace>` command +prints a backtrace showing function names, source files, and line numbers. + +This uses the libbacktrace library (bundled with GCC) to provide detailed symbol +information, including for static functions. + +To use it, simply run:: + + => backtrace + +This command is enabled with ``CONFIG_CMD_BACKTRACE``. + + Debugging the init sequence --------------------------- -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Display relative paths instead of absolute paths in backtrace output, making the output cleaner and more portable across different build environments. This works by adding a SRCTREE define to lib/backtrace.c and stripping it from filenames when printing. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- doc/usage/cmd/backtrace.rst | 18 +++++++++--------- lib/Makefile | 1 + lib/backtrace.c | 17 ++++++++++++++++- test/cmd/backtrace.c | 7 ++++++- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/doc/usage/cmd/backtrace.rst b/doc/usage/cmd/backtrace.rst index 37acb0b3067..edee54ccf22 100644 --- a/doc/usage/cmd/backtrace.rst +++ b/doc/usage/cmd/backtrace.rst @@ -29,15 +29,15 @@ Example => backtrace backtrace: 14 addresses - backtrace_show() at /home/user/u-boot/lib/backtrace.c:17 - do_backtrace() at /home/user/u-boot/cmd/backtrace.c:18 - cmd_process() at /home/user/u-boot/common/command.c:637 - run_list_real() at /home/user/u-boot/common/cli_hush.c:1868 - parse_stream_outer() at /home/user/u-boot/common/cli_hush.c:3207 - parse_string_outer() at /home/user/u-boot/common/cli_hush.c:3257 - run_command_list() at /home/user/u-boot/common/cli.c:168 - sandbox_main_loop_init() at /home/user/u-boot/arch/sandbox/cpu/start.c:153 - board_init_r() at /home/user/u-boot/common/board_r.c:774 + 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 ... Configuration diff --git a/lib/Makefile b/lib/Makefile index b696e81c496..15a43b2cc5e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -148,6 +148,7 @@ obj-$(CONFIG_LIB_UUID) += uuid.o obj-$(CONFIG_LIB_RAND) += rand.o obj-y += panic.o obj-$(CONFIG_BACKTRACE) += backtrace.o +CFLAGS_backtrace.o += -DSRCTREE='"$(srctree)/"' ifeq ($(CONFIG_XPL_BUILD),y) # SPL U-Boot may use full-printf, tiny-printf or none at all diff --git a/lib/backtrace.c b/lib/backtrace.c index e3d93b80b8a..715d7d1d05e 100644 --- a/lib/backtrace.c +++ b/lib/backtrace.c @@ -8,6 +8,21 @@ #include <backtrace.h> #include <stdio.h> +#include <string.h> + +static void print_sym(const char *sym) +{ + const char *p; + + /* Look for SRCTREE prefix in the string and skip it */ + p = strstr(sym, SRCTREE); + if (p) { + /* Print part before SRCTREE, then the rest after SRCTREE */ + printf(" %.*s%s\n", (int)(p - sym), sym, p + strlen(SRCTREE)); + } else { + printf(" %s\n", sym); + } +} int backtrace_show(void) { @@ -29,7 +44,7 @@ int backtrace_show(void) printf("backtrace: %d addresses\n", ctx.count); for (i = 0; i < ctx.count; i++) { if (ctx.syms[i]) - printf(" %s\n", ctx.syms[i]); + print_sym(ctx.syms[i]); else printf(" %p\n", ctx.addrs[i]); } diff --git a/test/cmd/backtrace.c b/test/cmd/backtrace.c index 2d999e20f31..9d55c74769e 100644 --- a/test/cmd/backtrace.c +++ b/test/cmd/backtrace.c @@ -14,9 +14,14 @@ /* Test 'backtrace' command */ static int cmd_test_backtrace(struct unit_test_state *uts) { - /* for now, just run the command */ ut_assertok(run_command("backtrace", 0)); + ut_assert_nextlinen("backtrace:"); + ut_assert_nextlinen(" backtrace_show() at lib/backtrace.c:"); + ut_assert_nextlinen(" do_backtrace() at cmd/backtrace.c:"); + ut_assert_nextlinen(" cmd_process() at common/command.c:"); + ut_assert_skip_to_linen(" cmd_test_backtrace() at test/cmd/backtrace.c:"); + return 0; } DM_TEST(cmd_test_backtrace, UTF_SCAN_FDT); -- 2.43.0
participants (1)
-
Simon Glass