numam-dpdk/lib/eal/unix/eal_debug.c
Stephen Hemminger 0efcd352e2 eal/unix: make stack dump signal safe
rte_dump_stack() needs to be usable in situations when a bug is
encountered and from signal handlers (such as SEGV).

Glibc backtrace_symbols() calls malloc which makes it
dangerous in a signal handler that is handling errors that maybe
due to memory corruption. Additionally, rte_log() is unsafe because
syslog() is not signal safe; printf() is also documented as
not being safe.

This version formats message and uses writev for each line in a manner
similar to what glibc version of backtrace_symbols_fd() does. The
FreeBSD version of backtrace_symbols_fd() is not signal safe.

Sample output:

0: ./build/app/dpdk-testpmd (rte_dump_stack+0x2b) [560a6e9c002b]
1: ./build/app/dpdk-testpmd (main+0xad) [560a6decd5ad]
2: /lib/x86_64-linux-gnu/libc.so.6 (__libc_start_main+0xcd) [7fd43d3e27fd]
3: ./build/app/dpdk-testpmd (_start+0x2a) [560a6e83628a]

Bugzilla ID: 929

Acked-by: Morten Brørup <mb@smartsharesystems.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Reviewed-by: David Marchand <david.marchand@redhat.com>
2022-06-23 13:40:50 +02:00

128 lines
2.8 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <rte_debug.h>
#ifdef RTE_BACKTRACE
#include <dlfcn.h>
#include <execinfo.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#define BACKTRACE_SIZE 256
/*
* Convert number to string and return start of string.
* Note: string does not start at beginning of buffer.
*/
static char *safe_itoa(long val, char *buf, size_t len, unsigned int radix)
{
char *bp = buf + len;
static const char hexdigit[] = "0123456789abcdef";
*--bp = '\0'; /* Null terminate the string */
do {
/* if buffer is not big enough, then truncate */
if (bp == buf)
return bp;
*--bp = hexdigit[val % radix];
val /= radix;
} while (val != 0);
return bp;
}
/*
* Dump the stack of the calling core
*
* To be safe in signal handler requires limiting what functions are
* used in this code since may be called from inside libc or
* when malloc poll is corrupt.
*
* Most of libc is therefore not safe, include RTE_LOG (calls syslog);
* backtrace_symbols (calls malloc), etc.
*/
void rte_dump_stack(void)
{
void *func[BACKTRACE_SIZE];
Dl_info info;
char buf1[8], buf2[32], buf3[32], buf4[32];
struct iovec iov[10];
int i, size;
size = backtrace(func, BACKTRACE_SIZE);
for (i = 0; i < size; i++) {
struct iovec *io = iov;
char *str;
uintptr_t base;
long offset;
void *pc = func[i];
/*
* Macro to put string onto set of iovecs.
* cast is to suppress warnings about lose of const qualifier.
*/
#define PUSH_IOV(io, str) { \
(io)->iov_base = (char *)(uintptr_t)str; \
(io)->iov_len = strlen(str); \
++io; }
/* output stack frame number */
str = safe_itoa(i, buf1, sizeof(buf1), 10);
PUSH_IOV(io, str); /* iov[0] */
PUSH_IOV(io, ": "); /* iov[1] */
/* Lookup the symbol information */
if (dladdr(pc, &info) == 0) {
PUSH_IOV(io, "?? [");
} else {
const char *fname;
if (info.dli_fname && *info.dli_fname)
fname = info.dli_fname;
else
fname = "(vdso)";
PUSH_IOV(io, fname); /* iov[2] */
PUSH_IOV(io, " ("); /* iov[3] */
if (info.dli_saddr != NULL) {
PUSH_IOV(io, info.dli_sname); /* iov[4] */
base = (uintptr_t)info.dli_saddr;
} else {
str = safe_itoa((unsigned long)info.dli_fbase,
buf3, sizeof(buf3), 16);
PUSH_IOV(io, str);
base = (uintptr_t)info.dli_fbase;
}
PUSH_IOV(io, "+0x"); /* iov[5] */
offset = (uintptr_t)pc - base;
str = safe_itoa(offset, buf4, sizeof(buf4), 16);
PUSH_IOV(io, str); /* iov[6] */
PUSH_IOV(io, ") ["); /* iov[7] */
}
str = safe_itoa((unsigned long)pc, buf2, sizeof(buf2), 16);
PUSH_IOV(io, str); /* iov[8] */
PUSH_IOV(io, "]\n"); /* iov[9] */
if (writev(STDERR_FILENO, iov, io - iov) < 0)
break;
#undef PUSH_IOV
}
}
#else /* !RTE_BACKTRACE */
/* stub if not enabled */
void rte_dump_stack(void) { }
#endif /* RTE_BACKTRACE */