1 /**
2 * NetBSD implementation of glibc's $(LINK2 http://www.gnu.org/software/libc/manual/html_node/Backtraces.html backtrace) facility.
3 *
4 * Copyright: Copyright Martin Nowak 2012.
5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 * Authors: Martin Nowak
7 * Source: $(DRUNTIMESRC core/sys/netbsd/_execinfo.d)
8 */
9 module core.sys.netbsd.execinfo;
10
11 version (NetBSD):
12 extern (C):
13 nothrow:
14
15 version (GNU)
16 version = BacktraceExternal;
17
18 version (BacktraceExternal)
19 {
20 size_t backtrace(void**, size_t);
21 char** backtrace_symbols(const(void*)*, size_t);
22 void backtrace_symbols_fd(const(void*)*, size_t, int);
23 char** backtrace_symbols_fmt(const(void*)*, size_t, const char*);
24 int backtrace_symbols_fd_fmt(const(void*)*, size_t, int, const char*);
25 }
26 else
27 {
28 import core.sys.netbsd.dlfcn;
29
30 // Use extern (D) so that these functions don't collide with libexecinfo.
31
32 extern (D) int backtrace(void** buffer, int size)
33 {
34 import core.thread : thread_stackBottom;
35
36 void** p, pend=cast(void**)thread_stackBottom();
37 version (D_InlineAsm_X86)
38 asm nothrow @trusted { mov p[EBP], EBP; }
39 else version (D_InlineAsm_X86_64)
40 asm nothrow @trusted { mov p[RBP], RBP; }
41 else
42 static assert(false, "Architecture not supported.");
43
44 int i;
45 for (; i < size && p < pend; ++i)
46 {
47 buffer[i] = *(p + 1);
48 auto pnext = cast(void**)*p;
49 if (pnext <= p) break;
50 p = pnext;
51 }
52 return i;
53 }
54
55
56 extern (D) char** backtrace_symbols(const(void*)* buffer, int size)
57 {
58 static void* realloc(void* p, size_t len) nothrow
59 {
60 static import cstdlib=core.stdc.stdlib;
61 auto res = cstdlib.realloc(p, len);
62 if (res is null) cstdlib.free(p);
63 return res;
64 }
65
66 if (size <= 0) return null;
67
68 size_t pos = size * (char*).sizeof;
69 char** p = cast(char**)realloc(null, pos);
70 if (p is null) return null;
71
72 Dl_info info;
73 foreach (i, addr; buffer[0 .. size])
74 {
75 if (dladdr(addr, &info) == 0)
76 (cast(ubyte*)&info)[0 .. info.sizeof] = 0;
77 fixupDLInfo(addr, info);
78
79 immutable len = formatStackFrame(null, 0, addr, info);
80 assert(len > 0);
81
82 p = cast(char**)realloc(p, pos + len);
83 if (p is null) return null;
84
85 formatStackFrame(cast(char*)p + pos, len, addr, info) == len || assert(0);
86
87 p[i] = cast(char*)pos;
88 pos += len;
89 }
90 foreach (i; 0 .. size)
91 {
92 pos = cast(size_t)p[i];
93 p[i] = cast(char*)p + pos;
94 }
95 return p;
96 }
97
98
99 extern (D) void backtrace_symbols_fd(const(void*)* buffer, int size, int fd)
100 {
101 import core.sys.posix.unistd : write;
102 import core.stdc.stdlib : alloca;
103
104 if (size <= 0) return;
105
106 Dl_info info;
107 foreach (i, addr; buffer[0 .. size])
108 {
109 if (dladdr(addr, &info) == 0)
110 (cast(ubyte*)&info)[0 .. info.sizeof] = 0;
111 fixupDLInfo(addr, info);
112
113 enum maxAlloca = 1024;
114 enum min = (size_t a, size_t b) => a <= b ? a : b;
115 immutable len = min(formatStackFrame(null, 0, addr, info), maxAlloca);
116 assert(len > 0);
117
118 auto p = cast(char*)alloca(len);
119 if (p is null) return;
120
121 formatStackFrame(p, len, addr, info) >= len || assert(0);
122 p[len - 1] = '\n';
123 write(fd, p, len);
124 }
125 }
126
127
128 private void fixupDLInfo(const(void)* addr, ref Dl_info info)
129 {
130 if (info.dli_fname is null) info.dli_fname = "???";
131 if (info.dli_fbase is null) info.dli_fbase = null;
132 if (info.dli_sname is null) info.dli_sname = "???";
133 if (info.dli_saddr is null) info.dli_saddr = cast(void*)addr;
134 }
135
136
137 private size_t formatStackFrame(char* p, size_t plen, const(void)* addr, const ref Dl_info info)
138 {
139 import core.stdc.stdio : snprintf;
140
141 immutable off = addr - info.dli_saddr;
142 immutable len = snprintf(p, plen, "%p <%s+%zd> at %s",
143 addr, info.dli_sname, off, info.dli_fname);
144 assert(len > 0);
145 return cast(size_t)len + 1; // + '\0'
146 }
147 }