llvm.org GIT mirror llvm / 0bcd9c7
Add Windows x64 stack walking support. Patch by Aaron Ballman! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@140906 91177308-0d34-0410-b5e6-96231b3b80d8 Michael J. Spencer 8 years ago
1 changed file(s) with 191 addition(s) and 33 deletion(s). Raw diff Collapse all Expand all
2222 #endif
2323 #include
2424
25 #ifdef __MINGW32__
25 #ifdef _MSC_VER
26 #pragma comment(lib, "psapi.lib")
27 #pragma comment(lib, "dbghelp.lib")
28 #elif __MINGW32__
2629 #if ((HAVE_LIBIMAGEHLP != 1) || (HAVE_LIBPSAPI != 1))
2730 #error "libimagehlp.a & libpsapi.a should be present"
2831 #endif
29 #else
30 #pragma comment(lib, "psapi.lib")
31 #pragma comment(lib, "dbghelp.lib")
32 #endif
32 // The version of g++ that comes with MinGW does *not* properly understand
33 // the ll format specifier for printf. However, MinGW passes the format
34 // specifiers on to the MSVCRT entirely, and the CRT understands the ll
35 // specifier. So these warnings are spurious in this case. Since we compile
36 // with -Wall, this will generate these warnings which should be ignored. So
37 // we will turn off the warnings for this just file. However, MinGW also does
38 // not support push and pop for diagnostics, so we have to manually turn it
39 // back on at the end of the file.
40 #pragma GCC diagnostic ignored "-Wformat"
41 #pragma GCC diagnostic ignored "-Wformat-extra-args"
42
43 // MinGW does not have updated support for the 64-bit versions of the DebugHlp
44 // APIs. So we will have to load them manually. The structures and method
45 // signatures were pulled from DbgHelp.h in the Windows Platform SDK, and
46 // adjusted for brevity.
47 typedef struct _IMAGEHLP_LINE64 {
48 DWORD SizeOfStruct;
49 PVOID Key;
50 DWORD LineNumber;
51 PCHAR FileName;
52 DWORD64 Address;
53 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
54
55 typedef struct _IMAGEHLP_SYMBOL64 {
56 DWORD SizeOfStruct;
57 DWORD64 Address;
58 DWORD Size;
59 DWORD Flags;
60 DWORD MaxNameLength;
61 CHAR Name[1];
62 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
63
64 typedef struct _tagADDRESS64 {
65 DWORD64 Offset;
66 WORD Segment;
67 ADDRESS_MODE Mode;
68 } ADDRESS64, *LPADDRESS64;
69
70 typedef struct _KDHELP64 {
71 DWORD64 Thread;
72 DWORD ThCallbackStack;
73 DWORD ThCallbackBStore;
74 DWORD NextCallback;
75 DWORD FramePointer;
76 DWORD64 KiCallUserMode;
77 DWORD64 KeUserCallbackDispatcher;
78 DWORD64 SystemRangeStart;
79 DWORD64 KiUserExceptionDispatcher;
80 DWORD64 StackBase;
81 DWORD64 StackLimit;
82 DWORD64 Reserved[5];
83 } KDHELP64, *PKDHELP64;
84
85 typedef struct _tagSTACKFRAME64 {
86 ADDRESS64 AddrPC;
87 ADDRESS64 AddrReturn;
88 ADDRESS64 AddrFrame;
89 ADDRESS64 AddrStack;
90 ADDRESS64 AddrBStore;
91 PVOID FuncTableEntry;
92 DWORD64 Params[4];
93 BOOL Far;
94 BOOL Virtual;
95 DWORD64 Reserved[3];
96 KDHELP64 KdHelp;
97 } STACKFRAME64, *LPSTACKFRAME64;
98
99 typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess,
100 DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize,
101 LPDWORD lpNumberOfBytesRead);
102
103 typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( HANDLE ahProcess,
104 DWORD64 AddrBase);
105
106 typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess,
107 DWORD64 Address);
108
109 typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess,
110 HANDLE hThread, LPADDRESS64 lpaddr);
111
112 typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64,
113 PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
114 PFUNCTION_TABLE_ACCESS_ROUTINE64,
115 PGET_MODULE_BASE_ROUTINE64,
116 PTRANSLATE_ADDRESS_ROUTINE64);
117 static fpStackWalk64 StackWalk64;
118
119 typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
120 static fpSymGetModuleBase64 SymGetModuleBase64;
121
122 typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64,
123 PDWORD64, PIMAGEHLP_SYMBOL64);
124 static fpSymGetSymFromAddr64 SymGetSymFromAddr64;
125
126 typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64,
127 PDWORD, PIMAGEHLP_LINE64);
128 static fpSymGetLineFromAddr64 SymGetLineFromAddr64;
129
130 typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
131 static fpSymFunctionTableAccess64 SymFunctionTableAccess64;
132
133 static bool load64BitDebugHelp(void) {
134 HMODULE hLib = ::LoadLibrary("Dbghelp.dll");
135 if (hLib) {
136 StackWalk64 = (fpStackWalk64)
137 ::GetProcAddress(hLib, "StackWalk64");
138 SymGetModuleBase64 = (fpSymGetModuleBase64)
139 ::GetProcAddress(hLib, "SymGetModuleBase64");
140 SymGetSymFromAddr64 = (fpSymGetSymFromAddr64)
141 ::GetProcAddress(hLib, "SymGetSymFromAddr64");
142 SymGetLineFromAddr64 = (fpSymGetLineFromAddr64)
143 ::GetProcAddress(hLib, "SymGetLineFromAddr64");
144 SymFunctionTableAccess64 = (fpSymFunctionTableAccess64)
145 ::GetProcAddress(hLib, "SymFunctionTableAccess64");
146 }
147 return StackWalk64 != NULL;
148 }
149 #endif // __MINGW32__
33150
34151 // Forward declare.
35152 static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep);
89206 #endif
90207
91208 static void RegisterHandler() {
209 #if __MINGW32__
210 // On MinGW, we need to load up the symbols explicitly, because the
211 // Win32 framework they include does not have support for the 64-bit
212 // versions of the APIs we need. If we cannot load up the APIs (which
213 // would be unexpected as they should exist on every version of Windows
214 // we support), we will bail out since there would be nothing to report.
215 if (!load64BitDebugHelp()) {
216 assert(false && "These APIs should always be available");
217 return;
218 }
219 #endif
220
92221 if (RegisteredUnhandledExceptionFilter) {
93222 EnterCriticalSection(&CriticalSection);
94223 return;
212341 static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
213342 Cleanup();
214343
215 #ifdef _WIN64
216 // TODO: provide a x64 friendly version of the following
217 #else
218
219344 // Initialize the STACKFRAME structure.
220 STACKFRAME StackFrame;
345 STACKFRAME64 StackFrame;
221346 memset(&StackFrame, 0, sizeof(StackFrame));
222347
348 DWORD machineType;
349 #if defined(_M_X64)
350 machineType = IMAGE_FILE_MACHINE_AMD64;
351 StackFrame.AddrPC.Offset = ep->ContextRecord->Rip;
352 StackFrame.AddrPC.Mode = AddrModeFlat;
353 StackFrame.AddrStack.Offset = ep->ContextRecord->Rsp;
354 StackFrame.AddrStack.Mode = AddrModeFlat;
355 StackFrame.AddrFrame.Offset = ep->ContextRecord->Rbp;
356 StackFrame.AddrFrame.Mode = AddrModeFlat;
357 #elif defined(_M_IX86)
358 machineType = IMAGE_FILE_MACHINE_I386;
223359 StackFrame.AddrPC.Offset = ep->ContextRecord->Eip;
224360 StackFrame.AddrPC.Mode = AddrModeFlat;
225361 StackFrame.AddrStack.Offset = ep->ContextRecord->Esp;
226362 StackFrame.AddrStack.Mode = AddrModeFlat;
227363 StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp;
228364 StackFrame.AddrFrame.Mode = AddrModeFlat;
365 #endif
229366
230367 HANDLE hProcess = GetCurrentProcess();
231368 HANDLE hThread = GetCurrentThread();
235372 SymInitialize(hProcess, NULL, TRUE);
236373
237374 while (true) {
238 if (!StackWalk(IMAGE_FILE_MACHINE_I386, hProcess, hThread, &StackFrame,
239 ep->ContextRecord, NULL, SymFunctionTableAccess,
240 SymGetModuleBase, NULL)) {
375 if (!StackWalk64(machineType, hProcess, hThread, &StackFrame,
376 ep->ContextRecord, NULL, SymFunctionTableAccess64,
377 SymGetModuleBase64, NULL)) {
241378 break;
242379 }
243380
245382 break;
246383
247384 // Print the PC in hexadecimal.
248 DWORD PC = StackFrame.AddrPC.Offset;
249 fprintf(stderr, "%08lX", PC);
385 DWORD64 PC = StackFrame.AddrPC.Offset;
386 #if defined(_M_X64)
387 fprintf(stderr, "0x%016llX", PC);
388 #elif defined(_M_IX86)
389 fprintf(stderr, "0x%08lX", static_cast(PC));
390 #endif
250391
251392 // Print the parameters. Assume there are four.
393 #if defined(_M_X64)
394 fprintf(stderr, " (0x%016llX 0x%016llX 0x%016llX 0x%016llX)",
395 StackFrame.Params[0],
396 StackFrame.Params[1],
397 StackFrame.Params[2],
398 StackFrame.Params[3]);
399 #elif defined(_M_IX86)
252400 fprintf(stderr, " (0x%08lX 0x%08lX 0x%08lX 0x%08lX)",
253 StackFrame.Params[0],
254 StackFrame.Params[1], StackFrame.Params[2], StackFrame.Params[3]);
255
401 static_cast(StackFrame.Params[0]),
402 static_cast(StackFrame.Params[1]),
403 static_cast(StackFrame.Params[2]),
404 static_cast(StackFrame.Params[3]));
405 #endif
256406 // Verify the PC belongs to a module in this process.
257 if (!SymGetModuleBase(hProcess, PC)) {
407 if (!SymGetModuleBase64(hProcess, PC)) {
258408 fputs(" \n", stderr);
259409 continue;
260410 }
261411
262412 // Print the symbol name.
263413 char buffer[512];
264 IMAGEHLP_SYMBOL *symbol = reinterpret_cast(buffer);
265 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL));
266 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
267 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL);
268
269 DWORD dwDisp;
270 if (!SymGetSymFromAddr(hProcess, PC, &dwDisp, symbol)) {
414 IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast(buffer);
415 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64));
416 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
417 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64);
418
419 DWORD64 dwDisp;
420 if (!SymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) {
271421 fputc('\n', stderr);
272422 continue;
273423 }
274424
275425 buffer[511] = 0;
276426 if (dwDisp > 0)
277 fprintf(stderr, ", %s()+%04lu bytes(s)", symbol->Name, dwDisp);
427 fprintf(stderr, ", %s() + 0x%llX bytes(s)", symbol->Name, dwDisp);
278428 else
279429 fprintf(stderr, ", %s", symbol->Name);
280430
281431 // Print the source file and line number information.
282 IMAGEHLP_LINE line;
432 IMAGEHLP_LINE64 line;
433 DWORD dwLineDisp;
283434 memset(&line, 0, sizeof(line));
284435 line.SizeOfStruct = sizeof(line);
285 if (SymGetLineFromAddr(hProcess, PC, &dwDisp, &line)) {
436 if (SymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) {
286437 fprintf(stderr, ", %s, line %lu", line.FileName, line.LineNumber);
287 if (dwDisp > 0)
288 fprintf(stderr, "+%04lu byte(s)", dwDisp);
438 if (dwLineDisp > 0)
439 fprintf(stderr, " + 0x%lX byte(s)", dwLineDisp);
289440 }
290441
291442 fputc('\n', stderr);
292443 }
293
294 #endif
295444
296445 if (ExitOnUnhandledExceptions)
297446 _exit(-3);
325474 LeaveCriticalSection(&CriticalSection);
326475 return FALSE;
327476 }
477
478 #if __MINGW32__
479 // We turned these warnings off for this file so that MinGW-g++ doesn't
480 // complain about the ll format specifiers used. Now we are turning the
481 // warnings back on. If MinGW starts to support diagnostic stacks, we can
482 // replace this with a pop.
483 #pragma GCC diagnostic warning "-Wformat"
484 #pragma GCC diagnostic warning "-Wformat-extra-args"
485 #endif