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