Signals.inc revision ae06a63be5a1279739e0c8a2006e72f4bc687d57
1//===- Win32/Signals.cpp - Win32 Signals Implementation ---------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file provides the Win32 specific implementation of the Signals class. 11// 12//===----------------------------------------------------------------------===// 13 14#include "llvm/Support/FileSystem.h" 15 16#include "Windows.h" 17#include <algorithm> 18#include <stdio.h> 19#include <vector> 20 21#ifdef __MINGW32__ 22 #include <imagehlp.h> 23#else 24 #include <dbghelp.h> 25#endif 26#include <psapi.h> 27 28#ifdef _MSC_VER 29 #pragma comment(lib, "psapi.lib") 30 #pragma comment(lib, "dbghelp.lib") 31#elif __MINGW32__ 32 #if ((HAVE_LIBIMAGEHLP != 1) || (HAVE_LIBPSAPI != 1)) 33 #error "libimagehlp.a & libpsapi.a should be present" 34 #endif 35 // The version of g++ that comes with MinGW does *not* properly understand 36 // the ll format specifier for printf. However, MinGW passes the format 37 // specifiers on to the MSVCRT entirely, and the CRT understands the ll 38 // specifier. So these warnings are spurious in this case. Since we compile 39 // with -Wall, this will generate these warnings which should be ignored. So 40 // we will turn off the warnings for this just file. However, MinGW also does 41 // not support push and pop for diagnostics, so we have to manually turn it 42 // back on at the end of the file. 43 #pragma GCC diagnostic ignored "-Wformat" 44 #pragma GCC diagnostic ignored "-Wformat-extra-args" 45 46 #if !defined(__MINGW64_VERSION_MAJOR) 47 // MinGW.org does not have updated support for the 64-bit versions of the 48 // DebugHlp APIs. So we will have to load them manually. The structures and 49 // method signatures were pulled from DbgHelp.h in the Windows Platform SDK, 50 // and adjusted for brevity. 51 typedef struct _IMAGEHLP_LINE64 { 52 DWORD SizeOfStruct; 53 PVOID Key; 54 DWORD LineNumber; 55 PCHAR FileName; 56 DWORD64 Address; 57 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; 58 59 typedef struct _IMAGEHLP_SYMBOL64 { 60 DWORD SizeOfStruct; 61 DWORD64 Address; 62 DWORD Size; 63 DWORD Flags; 64 DWORD MaxNameLength; 65 CHAR Name[1]; 66 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; 67 68 typedef struct _tagADDRESS64 { 69 DWORD64 Offset; 70 WORD Segment; 71 ADDRESS_MODE Mode; 72 } ADDRESS64, *LPADDRESS64; 73 74 typedef struct _KDHELP64 { 75 DWORD64 Thread; 76 DWORD ThCallbackStack; 77 DWORD ThCallbackBStore; 78 DWORD NextCallback; 79 DWORD FramePointer; 80 DWORD64 KiCallUserMode; 81 DWORD64 KeUserCallbackDispatcher; 82 DWORD64 SystemRangeStart; 83 DWORD64 KiUserExceptionDispatcher; 84 DWORD64 StackBase; 85 DWORD64 StackLimit; 86 DWORD64 Reserved[5]; 87 } KDHELP64, *PKDHELP64; 88 89 typedef struct _tagSTACKFRAME64 { 90 ADDRESS64 AddrPC; 91 ADDRESS64 AddrReturn; 92 ADDRESS64 AddrFrame; 93 ADDRESS64 AddrStack; 94 ADDRESS64 AddrBStore; 95 PVOID FuncTableEntry; 96 DWORD64 Params[4]; 97 BOOL Far; 98 BOOL Virtual; 99 DWORD64 Reserved[3]; 100 KDHELP64 KdHelp; 101 } STACKFRAME64, *LPSTACKFRAME64; 102 103typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, 104 DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, 105 LPDWORD lpNumberOfBytesRead); 106 107typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( HANDLE ahProcess, 108 DWORD64 AddrBase); 109 110typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, 111 DWORD64 Address); 112 113typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, 114 HANDLE hThread, LPADDRESS64 lpaddr); 115 116typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, 117 PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, 118 PFUNCTION_TABLE_ACCESS_ROUTINE64, 119 PGET_MODULE_BASE_ROUTINE64, 120 PTRANSLATE_ADDRESS_ROUTINE64); 121static fpStackWalk64 StackWalk64; 122 123typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); 124static fpSymGetModuleBase64 SymGetModuleBase64; 125 126typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, 127 PDWORD64, PIMAGEHLP_SYMBOL64); 128static fpSymGetSymFromAddr64 SymGetSymFromAddr64; 129 130typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, 131 PDWORD, PIMAGEHLP_LINE64); 132static fpSymGetLineFromAddr64 SymGetLineFromAddr64; 133 134typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); 135static fpSymFunctionTableAccess64 SymFunctionTableAccess64; 136 137static bool load64BitDebugHelp(void) { 138 HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll"); 139 if (hLib) { 140 StackWalk64 = (fpStackWalk64) 141 ::GetProcAddress(hLib, "StackWalk64"); 142 SymGetModuleBase64 = (fpSymGetModuleBase64) 143 ::GetProcAddress(hLib, "SymGetModuleBase64"); 144 SymGetSymFromAddr64 = (fpSymGetSymFromAddr64) 145 ::GetProcAddress(hLib, "SymGetSymFromAddr64"); 146 SymGetLineFromAddr64 = (fpSymGetLineFromAddr64) 147 ::GetProcAddress(hLib, "SymGetLineFromAddr64"); 148 SymFunctionTableAccess64 = (fpSymFunctionTableAccess64) 149 ::GetProcAddress(hLib, "SymFunctionTableAccess64"); 150 } 151 return StackWalk64 != NULL; 152} 153 #endif // !defined(__MINGW64_VERSION_MAJOR) 154#endif // __MINGW32__ 155 156// Forward declare. 157static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); 158static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType); 159 160// InterruptFunction - The function to call if ctrl-c is pressed. 161static void (*InterruptFunction)() = 0; 162 163static std::vector<std::string> *FilesToRemove = NULL; 164static std::vector<std::pair<void(*)(void*), void*> > *CallBacksToRun = 0; 165static bool RegisteredUnhandledExceptionFilter = false; 166static bool CleanupExecuted = false; 167static bool ExitOnUnhandledExceptions = false; 168static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL; 169 170// Windows creates a new thread to execute the console handler when an event 171// (such as CTRL/C) occurs. This causes concurrency issues with the above 172// globals which this critical section addresses. 173static CRITICAL_SECTION CriticalSection; 174 175namespace llvm { 176 177//===----------------------------------------------------------------------===// 178//=== WARNING: Implementation here must contain only Win32 specific code 179//=== and must not be UNIX code 180//===----------------------------------------------------------------------===// 181 182#ifdef _MSC_VER 183/// AvoidMessageBoxHook - Emulates hitting "retry" from an "abort, retry, 184/// ignore" CRT debug report dialog. "retry" raises an exception which 185/// ultimately triggers our stack dumper. 186static int AvoidMessageBoxHook(int ReportType, char *Message, int *Return) { 187 // Set *Return to the retry code for the return value of _CrtDbgReport: 188 // http://msdn.microsoft.com/en-us/library/8hyw4sy7(v=vs.71).aspx 189 // This may also trigger just-in-time debugging via DebugBreak(). 190 if (Return) 191 *Return = 1; 192 // Don't call _CrtDbgReport. 193 return TRUE; 194} 195 196#endif 197 198static void RegisterHandler() { 199#if __MINGW32__ && !defined(__MINGW64_VERSION_MAJOR) 200 // On MinGW.org, we need to load up the symbols explicitly, because the 201 // Win32 framework they include does not have support for the 64-bit 202 // versions of the APIs we need. If we cannot load up the APIs (which 203 // would be unexpected as they should exist on every version of Windows 204 // we support), we will bail out since there would be nothing to report. 205 if (!load64BitDebugHelp()) { 206 assert(false && "These APIs should always be available"); 207 return; 208 } 209#endif 210 211 if (RegisteredUnhandledExceptionFilter) { 212 EnterCriticalSection(&CriticalSection); 213 return; 214 } 215 216 // Now's the time to create the critical section. This is the first time 217 // through here, and there's only one thread. 218 InitializeCriticalSection(&CriticalSection); 219 220 // Enter it immediately. Now if someone hits CTRL/C, the console handler 221 // can't proceed until the globals are updated. 222 EnterCriticalSection(&CriticalSection); 223 224 RegisteredUnhandledExceptionFilter = true; 225 OldFilter = SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter); 226 SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE); 227 228 // Environment variable to disable any kind of crash dialog. 229 if (getenv("LLVM_DISABLE_CRASH_REPORT")) { 230#ifdef _MSC_VER 231 _CrtSetReportHook(AvoidMessageBoxHook); 232#endif 233 SetErrorMode(SEM_FAILCRITICALERRORS | 234 SEM_NOGPFAULTERRORBOX | 235 SEM_NOOPENFILEERRORBOX); 236 ExitOnUnhandledExceptions = true; 237 } 238 239 // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or 240 // else multi-threading problems will ensue. 241} 242 243// RemoveFileOnSignal - The public API 244bool sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { 245 RegisterHandler(); 246 247 if (CleanupExecuted) { 248 if (ErrMsg) 249 *ErrMsg = "Process terminating -- cannot register for removal"; 250 return true; 251 } 252 253 if (FilesToRemove == NULL) 254 FilesToRemove = new std::vector<std::string>; 255 256 FilesToRemove->push_back(Filename); 257 258 LeaveCriticalSection(&CriticalSection); 259 return false; 260} 261 262// DontRemoveFileOnSignal - The public API 263void sys::DontRemoveFileOnSignal(StringRef Filename) { 264 if (FilesToRemove == NULL) 265 return; 266 267 RegisterHandler(); 268 269 FilesToRemove->push_back(Filename); 270 std::vector<std::string>::reverse_iterator I = 271 std::find(FilesToRemove->rbegin(), FilesToRemove->rend(), Filename); 272 if (I != FilesToRemove->rend()) 273 FilesToRemove->erase(I.base()-1); 274 275 LeaveCriticalSection(&CriticalSection); 276} 277 278/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or 279/// SIGSEGV) is delivered to the process, print a stack trace and then exit. 280void sys::PrintStackTraceOnErrorSignal() { 281 RegisterHandler(); 282 LeaveCriticalSection(&CriticalSection); 283} 284 285void llvm::sys::PrintStackTrace(FILE *) { 286 // FIXME: Implement. 287} 288 289 290void sys::SetInterruptFunction(void (*IF)()) { 291 RegisterHandler(); 292 InterruptFunction = IF; 293 LeaveCriticalSection(&CriticalSection); 294} 295 296 297/// AddSignalHandler - Add a function to be called when a signal is delivered 298/// to the process. The handler can have a cookie passed to it to identify 299/// what instance of the handler it is. 300void sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) { 301 if (CallBacksToRun == 0) 302 CallBacksToRun = new std::vector<std::pair<void(*)(void*), void*> >(); 303 CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie)); 304 RegisterHandler(); 305 LeaveCriticalSection(&CriticalSection); 306} 307} 308 309static void Cleanup() { 310 EnterCriticalSection(&CriticalSection); 311 312 // Prevent other thread from registering new files and directories for 313 // removal, should we be executing because of the console handler callback. 314 CleanupExecuted = true; 315 316 // FIXME: open files cannot be deleted. 317 318 if (FilesToRemove != NULL) 319 while (!FilesToRemove->empty()) { 320 bool Existed; 321 llvm::sys::fs::remove(FilesToRemove->back(), Existed); 322 FilesToRemove->pop_back(); 323 } 324 325 if (CallBacksToRun) 326 for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i) 327 (*CallBacksToRun)[i].first((*CallBacksToRun)[i].second); 328 329 LeaveCriticalSection(&CriticalSection); 330} 331 332void llvm::sys::RunInterruptHandlers() { 333 Cleanup(); 334} 335 336static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { 337 Cleanup(); 338 339 // Initialize the STACKFRAME structure. 340 STACKFRAME64 StackFrame; 341 memset(&StackFrame, 0, sizeof(StackFrame)); 342 343 DWORD machineType; 344#if defined(_M_X64) 345 machineType = IMAGE_FILE_MACHINE_AMD64; 346 StackFrame.AddrPC.Offset = ep->ContextRecord->Rip; 347 StackFrame.AddrPC.Mode = AddrModeFlat; 348 StackFrame.AddrStack.Offset = ep->ContextRecord->Rsp; 349 StackFrame.AddrStack.Mode = AddrModeFlat; 350 StackFrame.AddrFrame.Offset = ep->ContextRecord->Rbp; 351 StackFrame.AddrFrame.Mode = AddrModeFlat; 352#elif defined(_M_IX86) 353 machineType = IMAGE_FILE_MACHINE_I386; 354 StackFrame.AddrPC.Offset = ep->ContextRecord->Eip; 355 StackFrame.AddrPC.Mode = AddrModeFlat; 356 StackFrame.AddrStack.Offset = ep->ContextRecord->Esp; 357 StackFrame.AddrStack.Mode = AddrModeFlat; 358 StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp; 359 StackFrame.AddrFrame.Mode = AddrModeFlat; 360#endif 361 362 HANDLE hProcess = GetCurrentProcess(); 363 HANDLE hThread = GetCurrentThread(); 364 365 // Initialize the symbol handler. 366 SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES); 367 SymInitialize(hProcess, NULL, TRUE); 368 369 while (true) { 370 if (!StackWalk64(machineType, hProcess, hThread, &StackFrame, 371 ep->ContextRecord, NULL, SymFunctionTableAccess64, 372 SymGetModuleBase64, NULL)) { 373 break; 374 } 375 376 if (StackFrame.AddrFrame.Offset == 0) 377 break; 378 379 // Print the PC in hexadecimal. 380 DWORD64 PC = StackFrame.AddrPC.Offset; 381#if defined(_M_X64) 382 fprintf(stderr, "0x%016llX", PC); 383#elif defined(_M_IX86) 384 fprintf(stderr, "0x%08lX", static_cast<DWORD>(PC)); 385#endif 386 387 // Print the parameters. Assume there are four. 388#if defined(_M_X64) 389 fprintf(stderr, " (0x%016llX 0x%016llX 0x%016llX 0x%016llX)", 390 StackFrame.Params[0], 391 StackFrame.Params[1], 392 StackFrame.Params[2], 393 StackFrame.Params[3]); 394#elif defined(_M_IX86) 395 fprintf(stderr, " (0x%08lX 0x%08lX 0x%08lX 0x%08lX)", 396 static_cast<DWORD>(StackFrame.Params[0]), 397 static_cast<DWORD>(StackFrame.Params[1]), 398 static_cast<DWORD>(StackFrame.Params[2]), 399 static_cast<DWORD>(StackFrame.Params[3])); 400#endif 401 // Verify the PC belongs to a module in this process. 402 if (!SymGetModuleBase64(hProcess, PC)) { 403 fputs(" <unknown module>\n", stderr); 404 continue; 405 } 406 407 // Print the symbol name. 408 char buffer[512]; 409 IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast<IMAGEHLP_SYMBOL64 *>(buffer); 410 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64)); 411 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 412 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64); 413 414 DWORD64 dwDisp; 415 if (!SymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) { 416 fputc('\n', stderr); 417 continue; 418 } 419 420 buffer[511] = 0; 421 if (dwDisp > 0) 422 fprintf(stderr, ", %s() + 0x%llX bytes(s)", symbol->Name, dwDisp); 423 else 424 fprintf(stderr, ", %s", symbol->Name); 425 426 // Print the source file and line number information. 427 IMAGEHLP_LINE64 line; 428 DWORD dwLineDisp; 429 memset(&line, 0, sizeof(line)); 430 line.SizeOfStruct = sizeof(line); 431 if (SymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) { 432 fprintf(stderr, ", %s, line %lu", line.FileName, line.LineNumber); 433 if (dwLineDisp > 0) 434 fprintf(stderr, " + 0x%lX byte(s)", dwLineDisp); 435 } 436 437 fputc('\n', stderr); 438 } 439 440 if (ExitOnUnhandledExceptions) 441 _exit(ep->ExceptionRecord->ExceptionCode); 442 443 // Allow dialog box to pop up allowing choice to start debugger. 444 if (OldFilter) 445 return (*OldFilter)(ep); 446 else 447 return EXCEPTION_CONTINUE_SEARCH; 448} 449 450static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) { 451 // We are running in our very own thread, courtesy of Windows. 452 EnterCriticalSection(&CriticalSection); 453 Cleanup(); 454 455 // If an interrupt function has been set, go and run one it; otherwise, 456 // the process dies. 457 void (*IF)() = InterruptFunction; 458 InterruptFunction = 0; // Don't run it on another CTRL-C. 459 460 if (IF) { 461 // Note: if the interrupt function throws an exception, there is nothing 462 // to catch it in this thread so it will kill the process. 463 IF(); // Run it now. 464 LeaveCriticalSection(&CriticalSection); 465 return TRUE; // Don't kill the process. 466 } 467 468 // Allow normal processing to take place; i.e., the process dies. 469 LeaveCriticalSection(&CriticalSection); 470 return FALSE; 471} 472 473#if __MINGW32__ 474 // We turned these warnings off for this file so that MinGW-g++ doesn't 475 // complain about the ll format specifiers used. Now we are turning the 476 // warnings back on. If MinGW starts to support diagnostic stacks, we can 477 // replace this with a pop. 478 #pragma GCC diagnostic warning "-Wformat" 479 #pragma GCC diagnostic warning "-Wformat-extra-args" 480#endif 481