152 lines
4.4 KiB
D
152 lines
4.4 KiB
D
|
/**
|
||
|
* FreeBSD implementation of glibc's $(LINK2 http://www.gnu.org/software/libc/manual/html_node/Backtraces.html backtrace) facility.
|
||
|
*
|
||
|
* Copyright: Copyright Martin Nowak 2012.
|
||
|
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||
|
* Authors: Martin Nowak
|
||
|
* Source: $(DRUNTIMESRC core/sys/freebsd/_execinfo.d)
|
||
|
*/
|
||
|
module core.sys.freebsd.execinfo;
|
||
|
|
||
|
version (FreeBSD):
|
||
|
extern (C):
|
||
|
nothrow:
|
||
|
|
||
|
version (GNU)
|
||
|
version = BacktraceExternal;
|
||
|
version (LDC)
|
||
|
version = BacktraceExternal;
|
||
|
|
||
|
version (BacktraceExternal)
|
||
|
{
|
||
|
size_t backtrace(void**, size_t);
|
||
|
char** backtrace_symbols(const(void*)*, size_t);
|
||
|
void backtrace_symbols_fd(const(void*)*, size_t, int);
|
||
|
char** backtrace_symbols_fmt(const(void*)*, size_t, const char*);
|
||
|
int backtrace_symbols_fd_fmt(const(void*)*, size_t, int, const char*);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
import core.sys.freebsd.dlfcn;
|
||
|
|
||
|
// Use extern (D) so that these functions don't collide with libexecinfo.
|
||
|
|
||
|
extern (D) int backtrace(void** buffer, int size)
|
||
|
{
|
||
|
import core.thread : thread_stackBottom;
|
||
|
|
||
|
void** p, pend=cast(void**)thread_stackBottom();
|
||
|
version (D_InlineAsm_X86)
|
||
|
asm nothrow @trusted { mov p[EBP], EBP; }
|
||
|
else version (D_InlineAsm_X86_64)
|
||
|
asm nothrow @trusted { mov p[RBP], RBP; }
|
||
|
else version (AArch64)
|
||
|
asm nothrow @trusted { "str x29, %0" : "=m" (p); }
|
||
|
else
|
||
|
static assert(false, "Architecture not supported.");
|
||
|
|
||
|
int i;
|
||
|
for (; i < size && p < pend; ++i)
|
||
|
{
|
||
|
buffer[i] = *(p + 1);
|
||
|
auto pnext = cast(void**)*p;
|
||
|
if (pnext <= p) break;
|
||
|
p = pnext;
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
|
||
|
extern (D) char** backtrace_symbols(const(void*)* buffer, int size)
|
||
|
{
|
||
|
static void* realloc(void* p, size_t len) nothrow
|
||
|
{
|
||
|
static import cstdlib=core.stdc.stdlib;
|
||
|
auto res = cstdlib.realloc(p, len);
|
||
|
if (res is null) cstdlib.free(p);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
if (size <= 0) return null;
|
||
|
|
||
|
size_t pos = size * (char*).sizeof;
|
||
|
char** p = cast(char**)realloc(null, pos);
|
||
|
if (p is null) return null;
|
||
|
|
||
|
Dl_info info;
|
||
|
foreach (i, addr; buffer[0 .. size])
|
||
|
{
|
||
|
if (dladdr(addr, &info) == 0)
|
||
|
(cast(ubyte*)&info)[0 .. info.sizeof] = 0;
|
||
|
fixupDLInfo(addr, info);
|
||
|
|
||
|
immutable len = formatStackFrame(null, 0, addr, info);
|
||
|
assert(len > 0);
|
||
|
|
||
|
p = cast(char**)realloc(p, pos + len);
|
||
|
if (p is null) return null;
|
||
|
|
||
|
formatStackFrame(cast(char*)p + pos, len, addr, info) == len || assert(0);
|
||
|
|
||
|
p[i] = cast(char*)pos;
|
||
|
pos += len;
|
||
|
}
|
||
|
foreach (i; 0 .. size)
|
||
|
{
|
||
|
pos = cast(size_t)p[i];
|
||
|
p[i] = cast(char*)p + pos;
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
extern (D) void backtrace_symbols_fd(const(void*)* buffer, int size, int fd)
|
||
|
{
|
||
|
import core.sys.posix.unistd : write;
|
||
|
import core.stdc.stdlib : alloca;
|
||
|
|
||
|
if (size <= 0) return;
|
||
|
|
||
|
Dl_info info;
|
||
|
foreach (i, addr; buffer[0 .. size])
|
||
|
{
|
||
|
if (dladdr(addr, &info) == 0)
|
||
|
(cast(ubyte*)&info)[0 .. info.sizeof] = 0;
|
||
|
fixupDLInfo(addr, info);
|
||
|
|
||
|
enum maxAlloca = 1024;
|
||
|
enum min = (size_t a, size_t b) => a <= b ? a : b;
|
||
|
immutable len = min(formatStackFrame(null, 0, addr, info), maxAlloca);
|
||
|
assert(len > 0);
|
||
|
|
||
|
auto p = cast(char*)alloca(len);
|
||
|
if (p is null) return;
|
||
|
|
||
|
formatStackFrame(p, len, addr, info) >= len || assert(0);
|
||
|
p[len - 1] = '\n';
|
||
|
write(fd, p, len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private void fixupDLInfo(const(void)* addr, ref Dl_info info)
|
||
|
{
|
||
|
if (info.dli_fname is null) info.dli_fname = "???";
|
||
|
if (info.dli_fbase is null) info.dli_fbase = null;
|
||
|
if (info.dli_sname is null) info.dli_sname = "???";
|
||
|
if (info.dli_saddr is null) info.dli_saddr = cast(void*)addr;
|
||
|
}
|
||
|
|
||
|
|
||
|
private size_t formatStackFrame(char* p, size_t plen, const(void)* addr, const ref Dl_info info)
|
||
|
{
|
||
|
import core.stdc.stdio : snprintf;
|
||
|
|
||
|
immutable off = addr - info.dli_saddr;
|
||
|
immutable len = snprintf(p, plen, "%p <%s+%zd> at %s",
|
||
|
addr, info.dli_sname, off, info.dli_fname);
|
||
|
assert(len > 0);
|
||
|
return cast(size_t)len + 1; // + '\0'
|
||
|
}
|
||
|
}
|