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