1 /** 2 * This module helps to decide whether an appropriate execinfo implementation 3 * is available in the underling C runtime or in an external library. In the 4 * latter case exactly one of the following version identifiers should be 5 * set at the time of building druntime. 6 * 7 * Possible external execinfo version IDs based on possible backtrace output 8 * formats: 9 * $(TABLE 10 * $(THEAD Version ID, Backtrace format) 11 * $(TROW $(B ExtExecinfo_BSDFmt), 0x00000000 <_D6module4funcAFZv+0x78> at module) 12 * $(TROW $(B ExtExecinfo_DarwinFmt), 1 module 0x00000000 D6module4funcAFZv + 0) 13 * $(TROW $(B ExtExecinfo_GNUFmt), module(_D6module4funcAFZv) [0x00000000] $(B or) 14 * module(_D6module4funcAFZv+0x78) [0x00000000] $(B or) module(_D6module4funcAFZv-0x78) [0x00000000]) 15 * $(TROW $(B ExtExecinfo_SolarisFmt), object'symbol+offset [pc]) 16 * ) 17 * 18 * The code also ensures that at most one format is selected (either by automatic 19 * C runtime detection or by $(B ExtExecinfo_) version IDs) and stores the 20 * corresponding values in $(LREF BacktraceFmt). 21 * 22 * With $(LREF getMangledSymbolName) we can get the original mangled symbol name 23 * from `backtrace_symbols` output of any supported version. 24 * 25 * Copyright: Copyright Digital Mars 2019. 26 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 27 * Source: $(DRUNTIMESRC core/internal/_execinfo.d) 28 */ 29 30 module core.internal.execinfo; 31 32 version (OSX) 33 version = Darwin; 34 else version (iOS) 35 version = Darwin; 36 else version (TVOS) 37 version = Darwin; 38 else version (WatchOS) 39 version = Darwin; 40 41 version (ExtExecinfo_BSDFmt) 42 version = _extExecinfo; 43 else version (ExtExecinfo_DarwinFmt) 44 version = _extExecinfo; 45 else version (ExtExecinfo_GNUFmt) 46 version = _extExecinfo; 47 else version (ExtExecinfo_SolarisFmt) 48 version = _extExecinfo; 49 50 version (linux) 51 { 52 version (CRuntime_Glibc) 53 import _execinfo = core.sys.linux.execinfo; 54 else version (CRuntime_UClibc) 55 import _execinfo = core.sys.linux.execinfo; 56 else version (_extExecinfo) 57 import _execinfo = core.sys.linux.execinfo; 58 } 59 else version (Darwin) 60 import _execinfo = core.sys.darwin.execinfo; 61 else version (FreeBSD) 62 import _execinfo = core.sys.freebsd.execinfo; 63 else version (NetBSD) 64 import _execinfo = core.sys.netbsd.execinfo; 65 else version (OpenBSD) 66 import _execinfo = core.sys.openbsd.execinfo; 67 else version (DragonFlyBSD) 68 import _execinfo = core.sys.dragonflybsd.execinfo; 69 else version (Solaris) 70 import _execinfo = core.sys.solaris.execinfo; 71 72 /// Indicates the availability of backtrace functions 73 enum bool hasExecinfo = is(_execinfo == module); 74 75 static if (hasExecinfo) 76 { 77 /// Always points to the platform's backtrace function. 78 alias backtrace = _execinfo.backtrace; 79 80 /// Always points to the platform's backtrace_symbols function. The 81 /// supported output format can be obtained by testing 82 /// $(LREF BacktraceFmt) enum values. 83 alias backtrace_symbols = _execinfo.backtrace_symbols; 84 85 /// Always points to the platform's backtrace_symbols_fd function. 86 alias backtrace_symbols_fd = _execinfo.backtrace_symbols_fd; 87 } 88 89 // Inspect possible backtrace formats 90 private 91 { 92 version (FreeBSD) 93 enum _BTFmt_BSD = true; 94 else version (DragonFlyBSD) 95 enum _BTFmt_BSD = true; 96 else version (NetBSD) 97 enum _BTFmt_BSD = true; 98 else version (OpenBSD) 99 enum _BTFmt_BSD = true; 100 else version (ExtExecinfo_BSDFmt) 101 enum _BTFmt_BSD = true; 102 else 103 enum _BTFmt_BSD = false; 104 105 version (Darwin) 106 enum _BTFmt_Darwin = true; 107 else version (ExtExecinfo_DarwinFmt) 108 enum _BTFmt_Darwin = true; 109 else 110 enum _BTFmt_Darwin = false; 111 112 version (CRuntime_Glibc) 113 enum _BTFmt_GNU = true; 114 else version (CRuntime_UClibc) 115 enum _BTFmt_GNU = true; 116 else version (ExtExecinfo_GNUFmt) 117 enum _BTFmt_GNU = true; 118 else 119 enum _BTFmt_GNU = false; 120 121 version (Solaris) 122 enum _BTFmt_Solaris = true; 123 else version (ExtExecinfo_SolarisFmt) 124 enum _BTFmt_Solaris = true; 125 else 126 enum _BTFmt_Solaris = false; 127 } 128 129 /** 130 * Indicates the backtrace format of the actual execinfo implementation. 131 * At most one of the values is allowed to be set to `true` the 132 * others should be `false`. 133 */ 134 enum BacktraceFmt : bool 135 { 136 /// 0x00000000 <_D6module4funcAFZv+0x78> at module 137 BSD = _BTFmt_BSD, 138 139 /// 1 module 0x00000000 D6module4funcAFZv + 0 140 Darwin = _BTFmt_Darwin, 141 142 /// module(_D6module4funcAFZv) [0x00000000] 143 /// $(B or) module(_D6module4funcAFZv+0x78) [0x00000000] 144 /// $(B or) module(_D6module4funcAFZv-0x78) [0x00000000] 145 GNU = _BTFmt_GNU, 146 147 /// object'symbol+offset [pc] 148 Solaris = _BTFmt_Solaris 149 } 150 151 private bool atMostOneBTFmt() 152 { 153 size_t trueCnt = 0; 154 155 foreach (fmt; __traits(allMembers, BacktraceFmt)) 156 if (__traits(getMember, BacktraceFmt, fmt)) ++trueCnt; 157 158 return trueCnt <= 1; 159 } 160 161 static assert(atMostOneBTFmt, "Cannot be set more than one BacktraceFmt at the same time."); 162 163 /** 164 * Takes a `backtrace_symbols` output and identifies the mangled symbol 165 * name in it. Optionally, also sets the begin and end indices of the symbol name in 166 * the input buffer. 167 * 168 * Params: 169 * btBuf = The input buffer containing the output of `backtrace_symbols` 170 * symBeg = Output parameter indexing the first character of the symbol's name 171 * symEnd = Output parameter indexing the first character after the symbol's name 172 * 173 * Returns: 174 * The name of the symbol 175 */ 176 static if (hasExecinfo) 177 const(char)[] getMangledSymbolName(const(char)[] btBuf, out size_t symBeg, 178 out size_t symEnd) @nogc nothrow 179 { 180 static if (BacktraceFmt.Darwin) 181 { 182 for (size_t i = 0, n = 0; i < btBuf.length; i++) 183 { 184 if (' ' == btBuf[i]) 185 { 186 n++; 187 while (i < btBuf.length && ' ' == btBuf[i]) 188 i++; 189 if (3 > n) 190 continue; 191 192 symBeg = i; 193 while (i < btBuf.length && ' ' != btBuf[i]) 194 i++; 195 symEnd = i; 196 break; 197 } 198 } 199 } 200 else 201 { 202 static if (BacktraceFmt.GNU) 203 { 204 enum bChar = '('; 205 enum eChar = ')'; 206 } 207 else static if (BacktraceFmt.BSD) 208 { 209 enum bChar = '<'; 210 enum eChar = '>'; 211 } 212 else static if (BacktraceFmt.Solaris) 213 { 214 enum bChar = '\''; 215 enum eChar = '+'; 216 } 217 218 foreach (i; 0 .. btBuf.length) 219 { 220 if (btBuf[i] == bChar) 221 { 222 foreach (j; i+1 .. btBuf.length) 223 { 224 const e = btBuf[j]; 225 if (e == eChar || e == '+' || e == '-') 226 { 227 symBeg = i + 1; 228 symEnd = j; 229 break; 230 } 231 } 232 break; 233 } 234 } 235 } 236 237 assert(symBeg <= symEnd); 238 assert(symEnd < btBuf.length); 239 240 return btBuf[symBeg .. symEnd]; 241 } 242 243 /// ditto 244 static if (hasExecinfo) 245 const(char)[] getMangledSymbolName(const(char)[] btBuf) @nogc nothrow 246 { 247 size_t symBeg, symEnd; 248 return getMangledSymbolName(btBuf, symBeg, symEnd); 249 } 250 251 @nogc nothrow unittest 252 { 253 size_t symBeg, symEnd; 254 255 static if (BacktraceFmt.BSD) 256 { 257 enum bufBSD = "0x00000000 <_D6module4funcAFZv+0x78> at module"; 258 auto resBSD = getMangledSymbolName(bufBSD, symBeg, symEnd); 259 assert("_D6module4funcAFZv" == resBSD); 260 assert(12 == symBeg); 261 assert(30 == symEnd); 262 } 263 else static if (BacktraceFmt.Darwin) 264 { 265 enum bufDarwin = "1 module 0x00000000 D6module4funcAFZv + 0"; 266 auto resDarwin = getMangledSymbolName(bufDarwin, symBeg, symEnd); 267 assert("D6module4funcAFZv" == resDarwin); 268 assert(24 == symBeg); 269 assert(41 == symEnd); 270 } 271 else static if (BacktraceFmt.GNU) 272 { 273 enum bufGNU0 = "module(_D6module4funcAFZv) [0x00000000]"; 274 auto resGNU0 = getMangledSymbolName(bufGNU0, symBeg, symEnd); 275 assert("_D6module4funcAFZv" == resGNU0); 276 assert(7 == symBeg); 277 assert(25 == symEnd); 278 279 enum bufGNU1 = "module(_D6module4funcAFZv+0x78) [0x00000000]"; 280 auto resGNU1 = getMangledSymbolName(bufGNU1, symBeg, symEnd); 281 assert("_D6module4funcAFZv" == resGNU1); 282 assert(7 == symBeg); 283 assert(25 == symEnd); 284 285 enum bufGNU2 = "/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main-0x78) [0x00000000]"; 286 auto resGNU2 = getMangledSymbolName(bufGNU2, symBeg, symEnd); 287 assert("__libc_start_main" == resGNU2); 288 assert(32 == symBeg); 289 assert(49 == symEnd); 290 } 291 else static if (BacktraceFmt.Solaris) 292 { 293 enum bufSolaris = "object'symbol+offset [pc]"; 294 auto resSolaris = getMangledSymbolName(bufSolaris, symBeg, symEnd); 295 assert("symbol" == resSolaris); 296 assert(7 == symBeg); 297 assert(13 == symEnd); 298 } 299 else 300 assert(!__traits(compiles, getMangledSymbolName)); 301 }