From patchwork Wed Dec 10 00:07:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 870 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325344; bh=SGH0r+eMZHop0z7Z4vQGkxWijl6kIWWibrv929XTejg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=b2N7EbUAL7AD7v5xdXs6Fae6OMXfTWgCdulgjh7EktScyL3b65j17qHqVwJSmaFJC Fbof2ttE8SHplxkOPpWDmy34ke8g9mOFp3aIUgUbFcSqMYgFGpDGI3nMbVYoHuCwT2 r4nfyQB1f7LxDqlQk7CMKMaWxJ1nbtn2AjDu9ArnqLGwZ+JVUobrmt+u8BnTSfO8e3 KQMz5WDgyanK+FmwPIjPx/1BwvPkFLyikfObVLv8Qd/T0mTU3am9NJ7MCW3XPgRehW fMoXTSkrvaaK4GWMqmt6SlBp87nJTCrgpx0HgxVnQ+76HD2gIodypRP0JDqyejx6Pb +wGoZ4B7MBWxg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0D3B868A3E for ; Tue, 9 Dec 2025 17:09:04 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 66XkTGuXIIkb for ; Tue, 9 Dec 2025 17:09:03 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325343; bh=SGH0r+eMZHop0z7Z4vQGkxWijl6kIWWibrv929XTejg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=AxVK3z0yCyA+m2qkBAoLidUPDKo6F25nJkDaVXcTQ6tGXPfiU9nNgqhxbEOmwZYn2 1R0OhGvVmzzdcF30Nd1kcIfYiz4kVnUiD/seWwcYlbGMH3Qwv1a5bNDrur7L4FcXuF KtPzd2kYgLGY4Y+zFEm59dsbOA0FKMEgQp2MfbhsP0y4EVr3oAQVIYPeYX6mPt5JC4 VPGRWrgOQF7z7+hUojhVT8NNcNbwY8v4m0qtr2dTt4uJ6iTQnDvc002IofNuuEHxf8 ssBtE7raujbbz97LShgyAr7YyzPkXmJe/qL/W1yaYHAZiQ8nue0zPAyKJcQFJ20fh5 WJMp3HHpG0dGg== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id DFDBC6884F for ; Tue, 9 Dec 2025 17:09:03 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325342; bh=Cjbehmj145pWtuMLDCN5v5SqFjblPXbbidvkCYGYd0M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jyF/1jIUxBGK2auHV1mRvM6/si1Wv4E/FSsnsE8IDbKLT7yEKvhef1J3la/9UN0qf RspzVeqaR1H/YJFd4z5FifeAp4L3TXSkHuciJOSPKbt8z4zJ1PkzOoDWd0hhXq956F 5p7NXvfbr9eHFAHGSUPE4YvwclH1N86/1ggYM5+v3wrEHE+uvdy2GlhjJttKtl+oRF 9Zy3fN0rOJGEO3XGpExMW7fTNJBlqhfKZ4Hbjz+bgTnCGoVSLa1z+6MH1bBpl6eVHd 5EPEixR+GdGT2yLGSM+qobpbZ9GSkbyHIDTisrN/C+3pRusWq8kqN0HejgMj547KpZ I4wDqF7dfLCFQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6FAA768972; Tue, 9 Dec 2025 17:09:02 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id YsyoJ60j_FZ7; Tue, 9 Dec 2025 17:09:02 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325337; bh=uktQD1LAm6VGCnyrE4fRiwr7uHb82VL9rmh13DIK82k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e+naYp4s8CxTE+z9GzAVESzcy7dk+sDwxAJQfhL08iDUwAk8P8ZozMdU96pR2j83Z 05zq8gGkDOi/2HBRTHvDiR2mcrazBomHvyqJ9El5vtGJjanh5mVNmTWHopn88g/C6s IDy3UiecNM/2kMFi+lJLWSbvlJzLHX6kysZXLa2y8DeKAq4zrWzu7OsXirUYF39tpt jXijNr11lpBvji4k+tvzlVHPIC/T7ZYvYXbr5gNqpQKfX1ybj0SxMGJFwEJXTjq825 zWxRuAQ/+FF0OSLloei3LsHIWeNyXG0msjEKRAcvWLahcPbABq7/ro+nHzBb86Rl/v nFILSCxW4/NJw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id C4DFF687A7; Tue, 9 Dec 2025 17:08:56 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Tue, 9 Dec 2025 17:07:04 -0700 Message-ID: <20251210000737.180797-14-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251210000737.180797-1-sjg@u-boot.org> References: <20251210000737.180797-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: DOV6QWC37JI26JSIZWQF2C2LZ56QHFQV X-Message-ID-Hash: DOV6QWC37JI26JSIZWQF2C2LZ56QHFQV X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 13/35] backtrace: Add backtrace_str() for condensed backtrace output List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add a new function backtrace_str() that returns a condensed backtrace string containing function names and line numbers separated by " <-". For example: "func_a:123 <-func_b:456 <-func_c:789" This is useful for logging and debugging where a compact representation of the call stack is needed. The depth is controlled by the new CONFIG_BACKTRACE_DEPTH option (default 3). Co-developed-by: Claude Signed-off-by: Simon Glass --- include/backtrace.h | 36 +++++++++++++ lib/Kconfig | 9 ++++ lib/backtrace.c | 122 +++++++++++++++++++++++++++++++++++++++++++ test/lib/backtrace.c | 36 +++++++++++++ 4 files changed, 203 insertions(+) diff --git a/include/backtrace.h b/include/backtrace.h index 7bb2ba68bec..7d9d8c1bb88 100644 --- a/include/backtrace.h +++ b/include/backtrace.h @@ -94,4 +94,40 @@ void backtrace_uninit(struct backtrace_ctx *ctx); */ int backtrace_show(void); +/** + * backtrace_strf() - get a condensed backtrace string into a buffer + * + * Return a string containing the last CONFIG_BACKTRACE_SUMMARY_FRAMES function names + * and line number, separated by ``<-``. + * + * For example: ``func_a:12 <-func_b:34 <-func_c:56`` + * + * @skip: number of stack frames to skip (0 to include backtrace_strf itself) + * @buf: buffer to write the string to + * @size: size of buffer + * Return: pointer to buf, or NULL on error + */ +char *backtrace_strf(unsigned int skip, char *buf, int size); + +/** + * backtrace_str() - get a condensed backtrace string + * + * Return a string containing the last CONFIG_BACKTRACE_SUMMARY_FRAMES function names + * and line number, separated by ``<-``. The string is statically allocated and + * will be overwritten on the next call. + * + * For example: ``func_a:12 <-func_b:34 <-func_c:56`` + * + * @skip: number of stack frames to skip (0 to include backtrace_str itself) + * Return: pointer to static string, or NULL on error + */ +#if CONFIG_IS_ENABLED(BACKTRACE) +const char *backtrace_str(unsigned int skip); +#else +static inline const char *backtrace_str(unsigned int skip) +{ + return NULL; +} +#endif + #endif /* __BACKTRACE_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 79a75f98446..9c7eb27c392 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -36,6 +36,15 @@ config BACKTRACE stack. This is currently only available on sandbox. The backtrace command can be used to print the backtrace. +config BACKTRACE_SUMMARY_FRAMES + int "Number of frames in condensed backtrace" + depends on BACKTRACE + default 3 + help + Number of stack frames to include in the condensed backtrace + string returned by backtrace_str(). This affects BSS usage + since space must be allocated for the string. + config LIB_FORMAT_SIZE bool default y diff --git a/lib/backtrace.c b/lib/backtrace.c index b01a08af8ba..b657e8336ab 100644 --- a/lib/backtrace.c +++ b/lib/backtrace.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -54,3 +55,124 @@ int backtrace_show(void) return 0; } + +/** + * extract_func_info() - extract function name and line number from a symbol + * + * Parse a backtrace symbol string and extract function name with line number. + * The format is typically "func_name() at /path/to/file.c:line" or similar. + * + * @sym: symbol string from backtrace + * @buf: buffer to write "func_name:line" to + * @size: size of buffer + * Return: pointer to buf, or NULL if extraction failed + */ +static char *extract_func_info(const char *sym, char *buf, int size) +{ + const char *start, *end, *colon; + int len; + + if (!sym) + return NULL; + + /* + * Skip leading whitespace and any address prefix (e.g. "0x12345678 ") + * Look for the function name which ends at '+' or '(' or ' ' + */ + start = sym; + while (*start == ' ') + start++; + + /* Skip hex address if present */ + if (start[0] == '0' && start[1] == 'x') { + while (*start && *start != ' ') + start++; + while (*start == ' ') + start++; + } + + /* Find end of function name */ + end = start; + while (*end && *end != '+' && *end != '(' && *end != ' ') + end++; + + len = end - start; + if (len <= 0 || len >= size) + return NULL; + + memcpy(buf, start, len); + + /* Look for line number after last colon (file:line format) */ + colon = strrchr(sym, ':'); + if (colon && colon[1] >= '0' && colon[1] <= '9') { + buf[len++] = ':'; + colon++; + /* Copy digits */ + while (*colon >= '0' && *colon <= '9' && len < size - 1) + buf[len++] = *colon++; + } + buf[len] = '\0'; + + return buf; +} + +char *backtrace_strf(unsigned int skip, char *buf, int size) +{ + static struct backtrace_ctx ctx; + int remaining = size; + bool first = true; + char func[64]; + char *p = buf; + uint i, count; + int ret, len; + + /* skip + 1 to skip backtrace_strf() */ + ret = backtrace_init(&ctx, skip + 1); + if (ret < 0) + return NULL; + + ret = backtrace_get_syms(&ctx, NULL, 0); + if (ret) { + backtrace_uninit(&ctx); + return NULL; + } + + count = ctx.count; + if (count > CONFIG_BACKTRACE_SUMMARY_FRAMES) + count = CONFIG_BACKTRACE_SUMMARY_FRAMES; + + for (i = 0; i < count; i++) { + if (!extract_func_info(ctx.frame[i].sym, func, sizeof(func))) + continue; + + if (!first) { + if (remaining < 4) + break; + *p++ = ' '; + *p++ = '<'; + *p++ = '-'; + remaining -= 3; + } + first = false; + + len = strlen(func); + if (len >= remaining) + break; + memcpy(p, func, len); + p += len; + remaining -= len; + } + *p = '\0'; + + backtrace_uninit(&ctx); + + return buf; +} + +const char *backtrace_str(unsigned int skip) +{ + static char result[CONFIG_BACKTRACE_SUMMARY_FRAMES * 64]; + + /* skip + 1 to account for this wrapper function */ + return backtrace_strf(skip + 1, result, sizeof(result)); +} diff --git a/test/lib/backtrace.c b/test/lib/backtrace.c index 11f0d43ca7e..3f20b7854bf 100644 --- a/test/lib/backtrace.c +++ b/test/lib/backtrace.c @@ -46,3 +46,39 @@ static int lib_test_backtrace(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_backtrace, 0); + +/* Test backtrace_strf() and backtrace_str() */ +static int lib_test_backtrace_str(struct unit_test_state *uts) +{ + char pattern[128]; + char buf[256]; + const char *cstr; + char *str; + int line; + + /* Test backtrace_strf() with skip=1 to skip backtrace_strf() itself */ + line = __LINE__ + 1; + str = backtrace_strf(1, buf, sizeof(buf)); + ut_assertnonnull(str); + ut_asserteq_ptr(buf, str); + + printf("backtrace_strf: %s\n", str); + snprintf(pattern, sizeof(pattern), + "lib_test_backtrace_str:%d <-ut_run_test:\\d+ <-ut_run_test_live_flat:\\d+", + line); + ut_asserteq_regex(pattern, str); + + /* Test backtrace_str() */ + line = __LINE__ + 1; + cstr = backtrace_str(0); + ut_assertnonnull(cstr); + + printf("backtrace_str: %s\n", cstr); + snprintf(pattern, sizeof(pattern), + "lib_test_backtrace_str:%d <-ut_run_test:\\d+ <-ut_run_test_live_flat:\\d+", + line); + ut_asserteq_regex(pattern, cstr); + + return 0; +} +LIB_TEST(lib_test_backtrace_str, 0);