Skip to content

Commit

Permalink
debug: refine backtrace facilities
Browse files Browse the repository at this point in the history
Signed-off-by: He Xian <hexian000@outlook.com>
  • Loading branch information
hexian000 committed Oct 19, 2024
1 parent e8d492a commit 6172886
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 126 deletions.
1 change: 1 addition & 0 deletions contrib/csnippets/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_subdirectory(utils)
include(CheckSymbolExists)
check_symbol_exists(syslog "syslog.h" HAVE_SYSLOG)
check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R)
check_symbol_exists(backtrace "execinfo.h" HAVE_BACKTRACE)
check_symbol_exists(backtrace_symbols "execinfo.h" HAVE_BACKTRACE_SYMBOLS)
check_symbol_exists(wcwidth "wchar.h" HAVE_WCWIDTH)

Expand Down
1 change: 1 addition & 0 deletions contrib/csnippets/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#cmakedefine01 WITH_LIBBACKTRACE
#cmakedefine01 WITH_LIBUNWIND
#cmakedefine01 HAVE_BACKTRACE
#cmakedefine01 HAVE_BACKTRACE_SYMBOLS

#endif /* CSNIPPETS_CONFIG_H */
205 changes: 125 additions & 80 deletions contrib/csnippets/utils/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
#elif WITH_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#elif HAVE_BACKTRACE_SYMBOLS
#elif HAVE_BACKTRACE
#include <execinfo.h>
#endif

#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include <wctype.h>
Expand All @@ -27,11 +30,11 @@

#define INDENT " "

void slog_extra_txt(void *data, FILE *f)
void slog_extra_txt(FILE *f, void *data)
{
const struct slog_extra_txt *restrict extradata = data;
size_t n = extradata->len;
const char *s = extradata->data;
const struct slog_extra_txt *restrict extra = data;
size_t n = extra->len;
const char *s = extra->data;
struct {
BUFFER_HDR;
unsigned char data[256];
Expand Down Expand Up @@ -95,17 +98,17 @@ void slog_extra_txt(void *data, FILE *f)
(void)fwrite(buf.data, sizeof(buf.data[0]), buf.len, f);
}

void slog_extra_bin(void *data, FILE *f)
void slog_extra_bin(FILE *f, void *data)
{
const struct slog_extra_bin *restrict extradata = data;
size_t n = extradata->len;
const struct slog_extra_bin *restrict extra = data;
size_t n = extra->len;
struct {
BUFFER_HDR;
unsigned char data[256];
} buf;
BUF_INIT(buf, 0);
const size_t wrap = 16;
const unsigned char *restrict b = extradata->data;
const unsigned char *restrict b = extra->data;
for (size_t i = 0; i < n; i += wrap) {
BUF_APPENDF(buf, INDENT "%p: ", (void *)(b + i));
for (size_t j = 0; j < wrap; j++) {
Expand Down Expand Up @@ -134,152 +137,194 @@ void slog_extra_bin(void *data, FILE *f)

#if WITH_LIBBACKTRACE

struct bt_context {
struct print_context {
struct slog_extra_stack *data;
struct backtrace_state *state;
struct buffer *buf;
int index;
FILE *f;
uintptr_t pc;
int index;
};

static void error_cb(void *data, const char *msg, const int errnum)
{
struct bt_context *restrict ctx = data;
struct print_context *restrict ctx = data;
(void)msg;
(void)errnum;
BUF_APPENDF(
*ctx->buf, INDENT "#%-3d 0x%jx <unknown>\n", ctx->index,
(void)fprintf(
ctx->f, INDENT "#%-3d 0x%jx <unknown>\n", ctx->index,
(uintmax_t)ctx->pc);
}

static void syminfo_cb(
void *data, const uintptr_t pc, const char *symname,
const uintptr_t symval, const uintptr_t symsize)
{
struct bt_context *restrict ctx = data;
struct print_context *restrict ctx = data;
(void)symsize;
if (symname == NULL) {
error_cb(data, NULL, -1);
return;
}
BUF_APPENDF(
*ctx->buf, INDENT "#%-3d 0x%jx %s+0x%jx\n", ctx->index,
(void)fprintf(
ctx->f, INDENT "#%-3d 0x%jx %s+0x%jx\n", ctx->index,
(uintmax_t)pc, symname, (uintmax_t)(pc - symval));
}

static int pcinfo_cb(
void *data, const uintptr_t pc, const char *filename, const int lineno,
const char *function)
{
struct bt_context *restrict ctx = data;
struct print_context *restrict ctx = data;
if (function != NULL && filename != NULL) {
BUF_APPENDF(
*ctx->buf, INDENT "#%-3d 0x%jx in %s (%s:%d)\n",
(void)fprintf(
ctx->f, INDENT "#%-3d 0x%jx in %s (%s:%d)\n",
ctx->index, (uintmax_t)pc, function, filename, lineno);
return 0;
}
backtrace_syminfo(ctx->state, pc, syminfo_cb, error_cb, data);
return 0;
}

static int backtrace_cb(void *data, const uintptr_t pc)
{
struct bt_context *ctx = data;
ctx->pc = pc;
(void)backtrace_pcinfo(ctx->state, pc, pcinfo_cb, error_cb, data);
ctx->index++;
return 0;
}
struct bt_context {
struct backtrace_state *state;
void **frames;
size_t i, n;
};

static void print_error_cb(void *data, const char *msg, int errnum)
static int backtrace_cb(void *data, const uintptr_t pc)
{
struct bt_context *restrict ctx = data;
BUF_APPENDF(
*ctx->buf, INDENT "backtrace error: (%d) %s\n", errnum, msg);
if (ctx->i < ctx->n) {
ctx->frames[ctx->i++] = (void *)pc;
return 0;
}
return 1;
}
#endif

#define STACK_MAXDEPTH 256

void slog_stacktrace(struct buffer *buf, int skip)
static struct backtrace_state *bt_state(void)
{
#if WITH_LIBBACKTRACE
skip++;
#if SLOG_MT_SAFE
static _Thread_local struct backtrace_state *state = NULL;
#else
static struct backtrace_state *state = NULL;
#endif

if (state != NULL) {
return state;
}
state = backtrace_create_state(NULL, 0, NULL, NULL);
return state;
}
#endif

int debug_backtrace(void **frames, int skip, const int len)
{
assert(frames != NULL && len > 0);
skip++;
#if WITH_LIBBACKTRACE
struct bt_context ctx = {
.state = state,
.buf = buf,
.state = bt_state(),
.frames = frames,
.i = 0,
.n = len,
};
if (ctx.state == NULL) {
return 0;
}
(void)backtrace_simple(ctx.state, skip, backtrace_cb, NULL, &ctx);
return ctx.i;
#elif WITH_LIBUNWIND
int n = unw_backtrace(frames, len);
int w = 0;
for (int i = skip; i < n; i++) {
frames[w++] = frames[i];
}
return w;
#elif HAVE_BACKTRACE
int n = backtrace(frames, len);
int w = 0;
for (int i = skip; i < n; i++) {
frames[w++] = frames[i];
}
return w;
#else
(void)frames;
(void)skip;
(void)len;
return 0;
#endif
}

static void
slog_extra_stack_default(FILE *f, struct slog_extra_stack *restrict extra)
{
int index = 1;
for (size_t i = 0; i < extra->len; i++) {
(void)fprintf(f, INDENT "#%-3d %p\n", index, extra->pc[i]);
index++;
}
}

void slog_extra_stack(FILE *f, void *data)
{
struct slog_extra_stack *restrict extra = data;
#if WITH_LIBBACKTRACE
struct print_context ctx = {
.data = extra,
.state = bt_state(),
.f = f,
.index = 1,
};
if (ctx.state == NULL) {
state = backtrace_create_state(NULL, 0, print_error_cb, &ctx);
if (state == NULL) {
return;
}
ctx.state = state;
slog_extra_stack_default(f, extra);
return;
}
for (size_t i = 0; i < extra->len; i++) {
ctx.pc = (uintptr_t)extra->pc[i];
(void)backtrace_pcinfo(
ctx.state, ctx.pc, pcinfo_cb, error_cb, &ctx);
ctx.index++;
}
backtrace_simple(state, skip, backtrace_cb, print_error_cb, &ctx);
#elif WITH_LIBUNWIND
unw_context_t uc;
if (unw_getcontext(&uc) != 0) {
slog_extra_stack_default(f, extra);
return;
}
unw_cursor_t cursor;
if (unw_init_local(&cursor, &uc) != 0) {
slog_extra_stack_default(f, extra);
return;
}
int index = 1;
for (int i = 0; unw_step(&cursor) > 0; i++) {
if (i < skip) {
continue;
}
unw_word_t pc;
if (unw_get_reg(&cursor, UNW_REG_IP, &pc)) {
break;
}
char sym[STACK_MAXDEPTH];
for (size_t i = 0; i < extra->len; i++) {
void *pc = extra->pc[i];
unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)pc);
unw_word_t offset;
char sym[256];
if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset)) {
BUF_APPENDF(
*buf, INDENT "#%-3d 0x%jx <unknown>\n", index,
(void)fprintf(
f, INDENT "#%-3d 0x%jx <unknown>\n", index,
(uintmax_t)pc);
} else {
BUF_APPENDF(
*buf, INDENT "#%-3d 0x%jx %s+0x%jx\n", index,
(void)fprintf(
f, INDENT "#%-3d 0x%jx %s+0x%jx\n", index,
(uintmax_t)pc, sym, (uintmax_t)offset);
}
index++;
}
#elif HAVE_BACKTRACE_SYMBOLS
skip += 2;
void *bt[STACK_MAXDEPTH];
const int n = backtrace(bt, sizeof(bt));
char **syms = backtrace_symbols(bt, n);
#elif HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
char **syms = backtrace_symbols(extra->pc, extra->len);
if (syms == NULL) {
int index = 1;
for (int i = skip; i < n; i++) {
BUF_APPENDF(*buf, INDENT "#%-3d %p\n", index, bt[i]);
index++;
}
slog_extra_stack_default(f, extra);
return;
}
int index = 1;
for (int i = skip; i < n; i++) {
BUF_APPENDF(*buf, INDENT "#%-3d %s\n", index, syms[i]);
index++;
for (size_t i = 0; i < extra->len; i++) {
(void)fprintf(f, INDENT "#%-3d %s\n", index++, syms[i]);
}
free(syms);
#else
(void)buf;
(void)skip;
slog_extra_stack_default(f, extra);
#endif
}

void slog_extra_buf(void *data, FILE *f)
{
const struct buffer *restrict buf = data;
(void)fwrite(buf->data, sizeof(buf->data[0]), buf->len, f);
}
31 changes: 19 additions & 12 deletions contrib/csnippets/utils/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,38 @@ struct slog_extra_txt {
const char *data;
size_t len;
};
void slog_extra_txt(void *data, FILE *f);
void slog_extra_txt(FILE *f, void *data);

struct slog_extra_bin {
const void *data;
size_t len;
};
void slog_extra_bin(void *data, FILE *f);
void slog_extra_bin(FILE *f, void *data);

void slog_stacktrace(struct buffer *buf, int skip);
void slog_extra_buf(void *data, FILE *f);
struct slog_extra_stack {
size_t len;
void *pc[];
};
void slog_extra_stack(FILE *f, void *data);

int debug_backtrace(void **frames, int calldepth, int len);

#define STACK_MAXDEPTH 256

#define LOG_STACK_F(level, calldepth, format, ...) \
do { \
if (!LOGLEVEL(level)) { \
break; \
} \
struct { \
BUFFER_HDR; \
unsigned char data[BUFSIZ]; \
} buf; \
BUF_INIT(buf, 0); \
slog_stacktrace((struct buffer *)&buf, (calldepth)); \
size_t len; \
void *pc[STACK_MAXDEPTH]; \
} frames; \
frames.len = debug_backtrace( \
frames.pc, (calldepth), STACK_MAXDEPTH); \
struct slog_extra extra = { \
.func = slog_extra_buf, \
.data = &buf, \
.func = slog_extra_stack, \
.data = &frames, \
}; \
slog_write( \
(LOG_LEVEL_##level), __FILE__, __LINE__, &extra, \
Expand Down Expand Up @@ -126,7 +133,7 @@ void slog_extra_buf(void *data, FILE *f);
#define FAILOOM() \
do { \
LOGF("out of memory"); \
exit(EXIT_FAILURE); \
_Exit(EXIT_FAILURE); /* no core dump */ \
} while (0)
#define CHECKOOM(ptr) \
do { \
Expand Down
Loading

0 comments on commit 6172886

Please sign in to comment.