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:
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
122
lib/backtrace.c
122
lib/backtrace.c
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user