1 module runtime.system.windows.dbghelp; 2 3 4 version(Windows): 5 @nogc nothrow: 6 7 import libd.threading.locks, runtime.system.windows; 8 9 __gshared Lockable!DebugHelp g_debugHelp; 10 11 private alias SymInitializeT = extern(Windows) BOOL function( 12 HANDLE hProcess, 13 PCSTR UserSearchPath, 14 BOOL fInvadeProcess 15 ); 16 17 private alias CaptureStackBackTraceT = extern(Windows) USHORT function( 18 @_In_ ULONG FramesToSkip, 19 @_In_ ULONG FramesToCapture, 20 @_Out_ PVOID *BackTrace, 21 @_Out_opt_ PULONG BackTraceHash 22 ); 23 24 private alias SymGetLineFromAddr64 = extern(Windows) BOOL function( 25 HANDLE hProcess, 26 DWORD64 qwAddr, 27 PDWORD pdwDisplacement, 28 PIMAGEHLP_LINE64 Line64 29 ); 30 31 private alias SymFromAddrT = extern(Windows) BOOL function( 32 HANDLE hProcess, 33 DWORD64 Address, 34 PDWORD64 Displacement, 35 PSYMBOL_INFO Symbol 36 ); 37 38 private alias SymSetOptions = extern(Windows) DWORD function( 39 DWORD SymOptions 40 ); 41 42 private alias SymGetOptions = extern(Windows) DWORD function(); 43 44 private struct IMAGEHLP_LINE64 { 45 DWORD SizeOfStruct; 46 PVOID Key; 47 DWORD LineNumber; 48 PCHAR FileName; 49 DWORD64 Address; 50 } 51 private alias PIMAGEHLP_LINE64 = IMAGEHLP_LINE64*; 52 53 private struct SYMBOL_INFO { 54 ULONG SizeOfStruct; 55 ULONG TypeIndex; 56 ULONG64[2] Reserved; 57 ULONG Index; 58 ULONG Size; 59 ULONG64 ModBase; 60 ULONG Flags; 61 ULONG64 Value; 62 ULONG64 Address; 63 ULONG Register; 64 ULONG Scope; 65 ULONG Tag; 66 ULONG NameLen; 67 ULONG MaxNameLen; 68 CHAR[1] Name; 69 } 70 alias PSYMBOL_INFO = SYMBOL_INFO*; 71 72 struct DebugHelpStackTrace 73 { 74 String symbol; 75 String file; 76 ulong symbolAddress; 77 uint line; 78 ulong lineAddress; 79 } 80 81 private struct DebugHelp 82 { 83 enum MAX_SYMBOL_NAME = 1024; // D symbols are about as long as the average node_modules list. 84 85 bool isAvailable; 86 SymInitializeT SymInitialize; 87 CaptureStackBackTraceT CaptureStackBackTrace; 88 SymGetLineFromAddr64 SymGetLineFromAddr; 89 SymFromAddrT SymFromAddr; 90 91 @nogc nothrow: 92 93 DebugHelpStackTrace[Amount] backtrace(size_t Amount)(ULONG framesToSkip, out size_t count) 94 { 95 typeof(return) traces; 96 97 void*[Amount] frames; 98 count = this.CaptureStackBackTrace( 99 framesToSkip+1, 100 Amount, 101 frames.ptr, 102 null 103 ); 104 105 ubyte[SYMBOL_INFO.sizeof + MAX_SYMBOL_NAME] buffer; 106 auto asInfo = cast(PSYMBOL_INFO)buffer.ptr; 107 asInfo.SizeOfStruct = SYMBOL_INFO.sizeof; 108 asInfo.MaxNameLen = MAX_SYMBOL_NAME; 109 110 auto process = GetCurrentProcess(); 111 112 foreach(i; 0..count) 113 { 114 const address = frames[i]; 115 const symFound = this.SymFromAddr( 116 process, 117 cast(DWORD64)address, 118 null, 119 asInfo 120 ); 121 122 if(!symFound) 123 { 124 traces[i] = DebugHelpStackTrace( 125 String("[COULD NOT FIND]"), 126 String("???"), 127 0, 128 0, 129 0, 130 ); 131 continue; 132 } 133 134 IMAGEHLP_LINE64 line; 135 line.SizeOfStruct = IMAGEHLP_LINE64.sizeof; 136 137 DWORD thisParamIsntOptionalBtw; 138 const lineFound = this.SymGetLineFromAddr( 139 process, 140 cast(DWORD64)address, 141 &thisParamIsntOptionalBtw, 142 &line 143 ); 144 145 version(unittest) 146 { 147 if(!lineFound && GetLastError() != 487) // invalid address 148 { 149 import libd.console; 150 consoleWriteln( 151 "[unittest-only][libd-runtime] Could not find line information: " 152 .ansi.fg(Ansi4BitColour.red), 153 asInfo.Name.ptr[0..asInfo.NameLen], 154 " -> ", 155 GetLastError(), 156 ' ', 157 GetLastErrorAsString() 158 ); 159 } 160 } 161 162 traces[i] = DebugHelpStackTrace( 163 String(asInfo.Name.ptr[0..asInfo.NameLen]), 164 String(line.FileName), 165 asInfo.Address, 166 line.LineNumber, 167 line.Address, 168 ); 169 } 170 return traces; 171 } 172 } 173 174 @nogc nothrow 175 void _d_init_dbghlp() 176 { 177 import libd.console; 178 179 auto dll = LoadLibraryA("dbghelp.dll"); 180 auto nt = LoadLibraryA("NtDll.dll"); 181 if(!dll) 182 { 183 debug consoleWriteln( 184 "[debug-only][libd-runtime] Could not load dbghelp.dll - Stack trace disabled." 185 .ansi.fg(Ansi4BitColour.yellow) 186 ); 187 return; 188 } 189 if(!nt) 190 { 191 debug consoleWriteln( 192 "[debug-only][libd-runtime] Could not load NtDll.dll - Stack trace disabled." 193 .ansi.fg(Ansi4BitColour.yellow) 194 ); 195 return; 196 } 197 198 g_debugHelp.access((scope ref help) 199 { 200 auto process = GetCurrentProcess(); 201 202 static T loadFunc(T)(HMODULE dll, LPCSTR name, ref bool wasFailure) 203 { 204 auto ptr = cast(T)GetProcAddress(dll, name); 205 if(ptr is null) 206 { 207 wasFailure = true; 208 debug consoleWriteln( 209 "[debug-only][libd-runtime] Could not load dbghelp.dll function - Stack trace disabled: " 210 .ansi.fg(Ansi4BitColour.yellow), 211 String(name) 212 ); 213 } 214 215 return ptr; 216 } 217 218 bool wasFailure; 219 auto symInit = loadFunc!SymInitializeT (dll, "SymInitialize", wasFailure); 220 auto capTrace = loadFunc!CaptureStackBackTraceT (nt, "RtlCaptureStackBackTrace", wasFailure); 221 auto getLine = loadFunc!SymGetLineFromAddr64 (dll, "SymGetLineFromAddr64", wasFailure); 222 auto fromAddr = loadFunc!SymFromAddrT (dll, "SymFromAddr", wasFailure); 223 auto setOpt = loadFunc!SymSetOptions (dll, "SymSetOptions", wasFailure); 224 auto getOpt = loadFunc!SymGetOptions (dll, "SymGetOptions", wasFailure); 225 226 if(wasFailure) 227 return; 228 229 help.SymInitialize = symInit; 230 help.CaptureStackBackTrace = capTrace; 231 help.SymGetLineFromAddr = getLine; 232 help.SymFromAddr = fromAddr; 233 234 enum SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200; 235 enum SYMOPT_LOAD_LINES = 0x00000010; 236 enum SYMOPT_NO_PROMPTS = 0x00080000; 237 enum SYMOPT_UNDNAME = 0x00000002; 238 enum SYMOPT_DEFERRED_LOADS = 0x00000004; 239 const currOpt = getOpt(); 240 setOpt( 241 currOpt 242 | SYMOPT_FAIL_CRITICAL_ERRORS 243 | SYMOPT_LOAD_LINES 244 | SYMOPT_NO_PROMPTS 245 | SYMOPT_UNDNAME 246 | SYMOPT_DEFERRED_LOADS 247 ); 248 249 if(!help.SymInitialize(process, null, true)) 250 { 251 debug consoleWriteln( 252 "[debug-only][libd-runtime] SymInitialize failed - Stack trace disabled" 253 .ansi.fg(Ansi4BitColour.yellow) 254 ); 255 return; 256 } 257 258 help.isAvailable = true; 259 }); 260 261 return; 262 }