backtrace: Add backtrace_str() for condensed backtrace output

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 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-12-02 04:57:41 +00:00
parent e1e95b2887
commit 0cf38dc8c8
4 changed files with 203 additions and 0 deletions

View File

@@ -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

View File

@@ -7,6 +7,7 @@
*/
#include <backtrace.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
@@ -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));
}