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 }