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