Files
u-boot/arch/sandbox/cpu/backtrace.c
Simon Glass e1e95b2887 backtrace: Use a static buffer in backtrace_ctx for symbols
Replace the dynamic allocation in os_backtrace_symbols() with a static
buffer embedded in struct backtrace_ctx. This avoids malloc recursion
when backtrace is called from within dlmalloc (e.g., for the upcoming
mcheck caller-tracking).

The API gets a complete rework as part of this:

- Combine addrs[] and syms[] arrays into struct backtrace_frame with
  addr and sym fields
- Store the strings in a unified buffer, with pointers from an array
- Change os_backtrace_symbols() to take ctx pointer and fill sym_buf
- Remove os_backtrace_symbols_free() as nothing needs freeing
- Rename BACKTRACE_MAX to BACKTRACE_MAX_FRAMES

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
2025-12-10 05:53:03 -07:00

120 lines
2.5 KiB
C

// 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>
/* For BACKTRACE_MAX_FRAMES - include U-Boot's header after system headers */
#include "../../../include/backtrace.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 */
}
void os_backtrace_symbols(struct backtrace_ctx *ctx)
{
char *end = ctx->sym_buf + BACKTRACE_SYM_BUFSZ;
struct backtrace_state *state;
char *p = ctx->sym_buf;
int remaining, i;
state = get_bt_state();
for (i = 0; i < ctx->count; i++) {
struct backtrace_frame *frame = &ctx->frame[i];
struct bt_sym_ctx sym_ctx;
remaining = end - p;
if (remaining <= 1) {
/* No more space, leave remaining syms as NULL */
frame->sym = NULL;
continue;
}
frame->sym = p;
sym_ctx.buf = p;
sym_ctx.size = remaining;
sym_ctx.found = 0;
if (state) {
backtrace_pcinfo(state, (uintptr_t)frame->addr,
bt_full_callback, bt_error_callback,
&sym_ctx);
}
/* Fall back to address if no symbol found */
if (!sym_ctx.found)
snprintf(p, remaining, "%p", frame->addr);
p += strlen(p) + 1;
}
}