1 // GNU D Compiler routines for stack backtrace support.
2 // Copyright (C) 2013-2021 Free Software Foundation, Inc.
3 
4 // GCC is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3, or (at your option) any later
7 // version.
8 
9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 // for more details.
13 
14 // Under Section 7 of GPL version 3, you are granted additional
15 // permissions described in the GCC Runtime Library Exception, version
16 // 3.1, as published by the Free Software Foundation.
17 
18 // You should have received a copy of the GNU General Public License and
19 // a copy of the GCC Runtime Library Exception along with this program;
20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21 // <http://www.gnu.org/licenses/>.
22 
23 module gcc.backtrace;
24 
25 import gcc.libbacktrace;
26 
27 // Max size per line of the traceback.
28 private enum MAX_BUFSIZE = 1536;
29 
30 static if (BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC)
31 {
32     import core.stdc.stdint, core.stdc.string, core.stdc.stdio;
33     private enum MAXFRAMES = 128;
34 
simpleCallback(void * data,uintptr_t pc)35     extern(C) int simpleCallback(void* data, uintptr_t pc)
36     {
37         auto context = cast(LibBacktrace)data;
38 
39         if (context.numPCs == MAXFRAMES)
40             return 1;
41 
42         context.pcs[context.numPCs++] = pc;
43         return 0;
44     }
45 
46     /*
47      * Used for backtrace_create_state and backtrace_simple
48      */
simpleErrorCallback(void * data,const (char)* msg,int errnum)49     extern(C) void simpleErrorCallback(void* data, const(char)* msg, int errnum)
50     {
51         if (data) // context is not available in backtrace_create_state
52         {
53             auto context = cast(LibBacktrace)data;
54             strncpy(context.errorBuf.ptr, msg, context.errorBuf.length - 1);
55             context.error = errnum;
56         }
57     }
58 
59     /*
60      * Used for backtrace_pcinfo
61      */
pcinfoCallback(void * data,uintptr_t pc,const (char)* filename,int lineno,const (char)* func)62     extern(C) int pcinfoCallback(void* data, uintptr_t pc, const(char)* filename,
63                                  int lineno, const(char)* func)
64     {
65         auto context = cast(SymbolCallbackInfo*)data;
66 
67         // Try to get the function name via backtrace_syminfo
68         if (func is null)
69         {
70             SymbolCallbackInfo2 info;
71             info.base = context;
72             info.filename = filename;
73             info.lineno = lineno;
74             if (backtrace_syminfo(context.state, pc, &syminfoCallback2, null, &info) != 0)
75             {
76                 return context.retval;
77             }
78         }
79 
80         auto sym = SymbolOrError(0, SymbolInfo(func, filename, lineno, cast(void*)pc));
81         context.retval = context.applyCB(context.num, sym);
82         context.num++;
83 
84         return context.retval;
85     }
86 
87     /*
88      * Used for backtrace_pcinfo and backtrace_syminfo
89      */
pcinfoErrorCallback(void * data,const (char)* msg,int errnum)90     extern(C) void pcinfoErrorCallback(void* data, const(char)* msg, int errnum)
91     {
92         auto context = cast(SymbolCallbackInfo*)data;
93 
94         if (errnum == -1)
95         {
96             context.noInfo = true;
97             return;
98         }
99 
100         SymbolOrError symError;
101         symError.errnum = errnum;
102         symError.msg = msg;
103 
104         size_t i = 0;
105         context.retval = context.applyCB(i, symError);
106     }
107 
108     /*
109      * Used for backtrace_syminfo (in opApply)
110      */
syminfoCallback(void * data,uintptr_t pc,const (char)* symname,uintptr_t symval)111     extern(C) void syminfoCallback(void* data, uintptr_t pc,
112                                    const(char)* symname, uintptr_t symval)
113     {
114         auto context = cast(SymbolCallbackInfo*)data;
115 
116         auto sym = SymbolOrError(0, SymbolInfo(symname, null, 0, cast(void*)pc));
117         context.retval = context.applyCB(context.num, sym);
118 
119         context.num++;
120     }
121 
122     /*
123      * This callback is used if backtrace_syminfo is called from the pcinfoCallback
124      * callback. It merges it's information with the information from pcinfoCallback.
125      */
syminfoCallback2(void * data,uintptr_t pc,const (char)* symname,uintptr_t symval)126     extern(C) void syminfoCallback2(void* data, uintptr_t pc,
127                                     const(char)* symname, uintptr_t symval)
128     {
129         auto context = cast(SymbolCallbackInfo2*)data;
130 
131         auto sym = SymbolOrError(0, SymbolInfo(symname, context.filename, context.lineno,
132             cast(void*)pc));
133         context.base.retval = context.base.applyCB(context.base.num, sym);
134 
135         context.base.num++;
136     }
137 
138     /*
139      * The callback type used with the opApply overload which returns a SymbolOrError
140      */
141     private alias int delegate(ref size_t, ref SymbolOrError) ApplyCallback;
142 
143     /*
144      * Passed to syminfoCallback, pcinfoCallback and pcinfoErrorCallback
145      */
146     struct SymbolCallbackInfo
147     {
148         bool noInfo = false;       // True if debug info / symbol table is not available
149         size_t num = 0;            // Counter for opApply
150         int retval;                // Value returned by applyCB
151         backtrace_state* state;
152 
153         // info.fileName / funcName / errmsg may become invalid after this delegate returned
154         ApplyCallback applyCB;
155 
resetSymbolCallbackInfo156         void reset()
157         {
158             noInfo = false;
159             num = 0;
160         }
161     }
162 
163     /*
164      * Passed to the syminfoCallback2 callback. That function merges it's
165      * funcName with this information and updates base as all other callbacks do.
166      */
167     struct SymbolCallbackInfo2
168     {
169         SymbolCallbackInfo* base;
170         const(char)* filename;
171         int lineno;
172     }
173 
174     /*
175      * Contains a valid symbol or an error message if errnum is != 0.
176      */
177     struct SymbolOrError
178     {
179         int errnum; // == 0: No error
180         union
181         {
182             SymbolInfo symbol;
183             const(char)* msg;
184         }
185     }
186 
187     // FIXME: state is never freed as libbacktrace doesn't provide a free function...
188     public class LibBacktrace : Throwable.TraceInfo
189     {
190         static void initLibBacktrace()
191         {
192             if (!initialized)
193             {
194                 state = backtrace_create_state(null, false, &simpleErrorCallback, null);
195                 initialized = true;
196             }
197         }
198 
199         this(int firstFrame)
200         {
201             _firstFrame = firstFrame;
202 
203             initLibBacktrace();
204 
205             if (state)
206             {
207                 backtrace_simple(state, _firstFrame, &simpleCallback,
208                                  &simpleErrorCallback, cast(void*)this);
209             }
210         }
211 
212         override int opApply(scope int delegate(ref const(char[])) dg) const
213         {
214             return opApply(
215                 (ref size_t, ref const(char[]) buf)
216                 {
217                     return dg(buf);
218                 }
219             );
220         }
221 
222         override int opApply(scope int delegate(ref size_t, ref const(char[])) dg) const
223         {
224             return opApply(
225                 (ref size_t i, ref SymbolOrError sym)
226                 {
227                     char[MAX_BUFSIZE] buffer = void;
228                     char[] msg;
229                     if (sym.errnum != 0)
230                     {
231                         auto retval = snprintf(buffer.ptr, buffer.length,
232                                                "libbacktrace error: '%s' errno: %d", sym.msg, sym.errnum);
233                         if (retval >= buffer.length)
234                             retval = buffer.length - 1; // Ignore zero terminator
235                         if (retval > 0)
236                             msg = buffer[0 .. retval];
237                         return dg(i, msg);
238                     }
239                     else
240                     {
241                         msg = formatLine(sym.symbol, buffer);
242                         int ret = dg(i, msg);
243 
244                         if (!ret && sym.symbol.funcName && strcmp(sym.symbol.funcName, "_Dmain") == 0)
245                             return 1;
246                         return ret;
247                     }
248                 }
249             );
250         }
251 
252         int opApply(scope ApplyCallback dg) const
253         {
254             initLibBacktrace();
255 
256             // If backtrace_simple produced an error report it and exit
257             if (!state || error != 0)
258             {
259                 size_t pos = 0;
260                 SymbolOrError symError;
261                 if (!state)
262                 {
263                     symError.msg = "libbacktrace failed to initialize\0";
264                     symError.errnum = 1;
265                 }
266                 else
267                 {
268                     symError.errnum = error;
269                     symError.msg = errorBuf.ptr;
270                 }
271 
272                 return dg(pos, symError);
273             }
274 
275             SymbolCallbackInfo cinfo;
276             cinfo.applyCB = dg;
277             cinfo.state = cast(backtrace_state*)state;
278 
279             // Try using debug info first
280             foreach (i, pc; pcs[0 .. numPCs])
281             {
282                 // FIXME: We may violate const guarantees here...
283                 if (backtrace_pcinfo(cast(backtrace_state*)state, pc, &pcinfoCallback,
284                     &pcinfoErrorCallback, &cinfo) != 0)
285                 {
286                     break; // User delegate requested abort or no debug info at all
287                 }
288             }
289 
290             // If no error or other error which has already been reported via callback
291             if (!cinfo.noInfo)
292                 return cinfo.retval;
293 
294             // Try using symbol table
295             cinfo.reset();
296             foreach (pc; pcs[0 .. numPCs])
297             {
298                 if (backtrace_syminfo(cast(backtrace_state*)state, pc, &syminfoCallback,
299                     &pcinfoErrorCallback, &cinfo) == 0)
300                 {
301                     break;
302                 }
303             }
304 
305             if (!cinfo.noInfo)
306                 return cinfo.retval;
307 
308             // No symbol table
309             foreach (i, pc; pcs[0 .. numPCs])
310             {
311                 auto sym = SymbolOrError(0, SymbolInfo(null, null, 0, cast(void*)pc));
312                 if (auto ret = dg(i, sym) != 0)
313                     return ret;
314             }
315 
316             return 0;
317         }
318 
319         override string toString() const
320         {
321             string buf;
322             foreach (i, const(char[]) line; this)
323                 buf ~= i ? "\n" ~ line : line;
324             return buf;
325         }
326 
327     private:
328         static backtrace_state* state = null;
329         static bool initialized       = false;
330         size_t                 numPCs = 0;
331         uintptr_t[MAXFRAMES] pcs;
332 
333         int       error = 0;
334         int _firstFrame = 0;
335         char[128] errorBuf = "\0";
336     }
337 }
338 else
339 {
340     /*
341      * Our fallback backtrace implementation using libgcc's unwind
342      * and backtrace support. In theory libbacktrace should be available
343      * everywhere where this code works. We keep it anyway till libbacktrace
344      * is well-tested.
345      */
346     public class UnwindBacktrace : Throwable.TraceInfo
347     {
348         this(int firstFrame)
349         {
350             _firstFrame = firstFrame;
351             _callstack = getBacktrace();
352             _framelist = getBacktraceSymbols(_callstack);
353         }
354 
355         override int opApply(scope int delegate(ref const(char[])) dg) const
356         {
357             return opApply(
358                 (ref size_t, ref const(char[]) buf)
359                 {
360                     return dg(buf);
361                 }
362             );
363         }
364 
365         override int opApply(scope int delegate(ref size_t, ref const(char[])) dg) const
366         {
367             char[MAX_BUFSIZE] buffer = void;
368             int ret = 0;
369 
370             for (int i = _firstFrame; i < _framelist.entries; ++i)
371             {
372                 auto pos = cast(size_t)(i - _firstFrame);
373                 auto msg = formatLine(_framelist.symbols[i], buffer);
374                 ret = dg(pos, msg);
375                 if (ret)
376                     break;
377             }
378             return ret;
379         }
380 
381         override string toString() const
382         {
383             string buf;
384             foreach (i, line; this)
385                 buf ~= i ? "\n" ~ line : line;
386             return buf;
387         }
388 
389     private:
390         BTSymbolData     _framelist;
391         UnwindBacktraceData _callstack;
392         int              _firstFrame = 0;
393     }
394 
395     // Implementation details
396 private:
397     import gcc.unwind;
398 
399     version (linux)
400         import core.sys.linux.dlfcn;
401     else version (OSX)
402         import core.sys.darwin.dlfcn;
403     else version (FreeBSD)
404         import core.sys.freebsd.dlfcn;
405     else version (NetBSD)
406         import core.sys.netbsd.dlfcn;
407     else version (OpenBSD)
408         import core.sys.openbsd.dlfcn;
409     else version (Solaris)
410         import core.sys.solaris.dlfcn;
411     else version (Posix)
412         import core.sys.posix.dlfcn;
413 
414     private enum MAXFRAMES = 128;
415 
416     struct UnwindBacktraceData
417     {
418         void*[MAXFRAMES] callstack;
419         int numframes = 0;
420     }
421 
422     struct BTSymbolData
423     {
424         size_t entries;
425         SymbolInfo[MAXFRAMES] symbols;
426     }
427 
428     static extern (C) _Unwind_Reason_Code unwindCB(_Unwind_Context *ctx, void *d)
429     {
430         UnwindBacktraceData* bt = cast(UnwindBacktraceData*)d;
431         if (bt.numframes >= MAXFRAMES)
432             return _URC_NO_REASON;
433 
434         bt.callstack[bt.numframes] = cast(void*)_Unwind_GetIP(ctx);
435         bt.numframes++;
436         return _URC_NO_REASON;
437     }
438 
439     UnwindBacktraceData getBacktrace()
440     {
441         UnwindBacktraceData stackframe;
442         _Unwind_Backtrace(&unwindCB, &stackframe);
443         return stackframe;
444     }
445 
446     BTSymbolData getBacktraceSymbols(UnwindBacktraceData data)
447     {
448         BTSymbolData symData;
449 
450         for (auto i = 0; i < data.numframes; i++)
451         {
452             static if ( __traits(compiles, Dl_info))
453             {
454                 Dl_info funcInfo;
455 
456                 if (data.callstack[i] !is null && dladdr(data.callstack[i], &funcInfo) != 0)
457                 {
458                     symData.symbols[symData.entries].funcName = funcInfo.dli_sname;
459 
460                     symData.symbols[symData.entries].address = data.callstack[i];
461                     symData.entries++;
462                 }
463                 else
464                 {
465                     symData.symbols[symData.entries].address = data.callstack[i];
466                     symData.entries++;
467                 }
468             }
469             else
470             {
471                 symData.symbols[symData.entries].address = data.callstack[i];
472                 symData.entries++;
473             }
474         }
475 
476         return symData;
477     }
478 }
479 
480 /*
481  * Struct representing a symbol (function) in the backtrace
482  */
483 struct SymbolInfo
484 {
485     const(char)* funcName, fileName;
486     size_t line;
487     const(void)* address;
488 }
489 
490 /*
491  * Format one output line for symbol sym.
492  * Returns a slice of buffer.
493  */
494 char[] formatLine(const SymbolInfo sym, return ref char[MAX_BUFSIZE] buffer)
495 {
496     import core.demangle, core.stdc.config;
497     import core.stdc.stdio : snprintf, printf;
498     import core.stdc.string : strlen;
499 
500     size_t bufferLength = 0;
501 
502     void appendToBuffer(Args...)(const(char)* format, Args args)
503     {
504         const count = snprintf(buffer.ptr + bufferLength,
505                                buffer.length - bufferLength,
506                                format, args);
507         assert(count >= 0);
508         bufferLength += count;
509         if (bufferLength >= buffer.length)
510             bufferLength = buffer.length - 1;
511     }
512 
513 
514     if (sym.fileName is null)
515         appendToBuffer("??:? ");
516     else
517         appendToBuffer("%s:%d ", sym.fileName, sym.line);
518 
519     if (sym.funcName is null)
520         appendToBuffer("???");
521     else
522     {
523         char[1024] symbol = void;
524         auto demangled = demangle(sym.funcName[0 .. strlen(sym.funcName)], symbol);
525 
526         if (demangled.length > 0)
527             appendToBuffer("%.*s ", cast(int) demangled.length, demangled.ptr);
528     }
529 
530     version (D_LP64)
531         appendToBuffer("[0x%llx]", cast(size_t)sym.address);
532     else
533         appendToBuffer("[0x%x]", cast(size_t)sym.address);
534 
535     return buffer[0 .. bufferLength];
536 }
537 
538 
539 unittest
540 {
541     char[MAX_BUFSIZE] sbuf = '\0';
542     char[] result;
543     string longString;
544     for (size_t i = 0; i < 60; i++)
545         longString ~= "abcdefghij";
546     longString ~= '\0';
547 
548     auto symbol = SymbolInfo(null, null, 0, null);
549     result = formatLine(symbol, sbuf);
550     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
551 
552     symbol = SymbolInfo(longString.ptr, null, 0, null);
553     result = formatLine(symbol, sbuf);
554     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
555 
556     symbol = SymbolInfo("func", "test.d", 0, null);
557     result = formatLine(symbol, sbuf);
558     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
559 
560     symbol = SymbolInfo("func", longString.ptr, 0, null);
561     result = formatLine(symbol, sbuf);
562     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
563 
564     symbol = SymbolInfo(longString.ptr, "test.d", 0, null);
565     result = formatLine(symbol, sbuf);
566     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
567 
568     symbol = SymbolInfo(longString.ptr, longString.ptr, 0, null);
569     result = formatLine(symbol, sbuf);
570     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
571 
572     symbol = SymbolInfo("func", "test.d", 1000, null);
573     result = formatLine(symbol, sbuf);
574     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
575 
576     symbol = SymbolInfo(null, (longString[0..500] ~ '\0').ptr, 100000000, null);
577     result = formatLine(symbol, sbuf);
578     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
579 
580     symbol = SymbolInfo("func", "test.d", 0, cast(void*)0x100000);
581     result = formatLine(symbol, sbuf);
582     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
583 
584     symbol = SymbolInfo("func", null, 0, cast(void*)0x100000);
585     result = formatLine(symbol, sbuf);
586     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
587 
588     symbol = SymbolInfo(null, "test.d", 0, cast(void*)0x100000);
589     result = formatLine(symbol, sbuf);
590     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
591 
592     symbol = SymbolInfo(longString.ptr, "test.d", 0, cast(void*)0x100000);
593     result = formatLine(symbol, sbuf);
594     assert(result.length < MAX_BUFSIZE && result.ptr[result.length] == '\0' && sbuf[$-1] == '\0');
595 }
596