Signals.inc revision b7e2188f7fb9a1c1cb6dbd32b206e44b11b4a157
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/PathV1.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 = ::LoadLibrary("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/// CRTReportHook - Function called on a CRT debugging event. 197static int CRTReportHook(int ReportType, char *Message, int *Return) { 198 // Don't cause a DebugBreak() on return. 199 if (Return) 200 *Return = 0; 201 202 switch (ReportType) { 203 default: 204 case _CRT_ASSERT: 205 fprintf(stderr, "CRT assert: %s\n", Message); 206 // FIXME: Is there a way to just crash? Perhaps throw to the unhandled 207 // exception code? Perhaps SetErrorMode() handles this. 208 _exit(3); 209 break; 210 case _CRT_ERROR: 211 fprintf(stderr, "CRT error: %s\n", Message); 212 // FIXME: Is there a way to just crash? Perhaps throw to the unhandled 213 // exception code? Perhaps SetErrorMode() handles this. 214 _exit(3); 215 break; 216 case _CRT_WARN: 217 fprintf(stderr, "CRT warn: %s\n", Message); 218 break; 219 } 220 221 // Don't call _CrtDbgReport. 222 return TRUE; 223} 224#endif 225 226static void RegisterHandler() { 227#if __MINGW32__ && !defined(__MINGW64_VERSION_MAJOR) 228 // On MinGW.org, we need to load up the symbols explicitly, because the 229 // Win32 framework they include does not have support for the 64-bit 230 // versions of the APIs we need. If we cannot load up the APIs (which 231 // would be unexpected as they should exist on every version of Windows 232 // we support), we will bail out since there would be nothing to report. 233 if (!load64BitDebugHelp()) { 234 assert(false && "These APIs should always be available"); 235 return; 236 } 237#endif 238 239 if (RegisteredUnhandledExceptionFilter) { 240 EnterCriticalSection(&CriticalSection); 241 return; 242 } 243 244 // Now's the time to create the critical section. This is the first time 245 // through here, and there's only one thread. 246 InitializeCriticalSection(&CriticalSection); 247 248 // Enter it immediately. Now if someone hits CTRL/C, the console handler 249 // can't proceed until the globals are updated. 250 EnterCriticalSection(&CriticalSection); 251 252 RegisteredUnhandledExceptionFilter = true; 253 OldFilter = SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter); 254 SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE); 255 256#ifdef _MSC_VER 257 const char *EnableMsgbox = getenv("LLVM_ENABLE_CRT_REPORT"); 258 if (!EnableMsgbox || strcmp("0", EnableMsgbox) == 0) { 259 // Setting a report hook overrides the default behavior of popping an "abort, 260 // retry, or ignore" dialog. 261 _CrtSetReportHook(AvoidMessageBoxHook); 262 } 263#endif 264 265 // Environment variable to disable any kind of crash dialog. 266 if (getenv("LLVM_DISABLE_CRASH_REPORT")) { 267#ifdef _MSC_VER 268 _CrtSetReportHook(CRTReportHook); 269#endif 270 SetErrorMode(SEM_FAILCRITICALERRORS | 271 SEM_NOGPFAULTERRORBOX | 272 SEM_NOOPENFILEERRORBOX); 273 ExitOnUnhandledExceptions = true; 274 } 275 276 // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or 277 // else multi-threading problems will ensue. 278} 279 280// RemoveFileOnSignal - The public API 281bool sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { 282 RegisterHandler(); 283 284 if (CleanupExecuted) { 285 if (ErrMsg) 286 *ErrMsg = "Process terminating -- cannot register for removal"; 287 return true; 288 } 289 290 if (FilesToRemove == NULL) 291 FilesToRemove = new std::vector<std::string>; 292 293 FilesToRemove->push_back(Filename); 294 295 LeaveCriticalSection(&CriticalSection); 296 return false; 297} 298 299// DontRemoveFileOnSignal - The public API 300void sys::DontRemoveFileOnSignal(StringRef Filename) { 301 if (FilesToRemove == NULL) 302 return; 303 304 RegisterHandler(); 305 306 FilesToRemove->push_back(Filename); 307 std::vector<std::string>::reverse_iterator I = 308 std::find(FilesToRemove->rbegin(), FilesToRemove->rend(), Filename); 309 if (I != FilesToRemove->rend()) 310 FilesToRemove->erase(I.base()-1); 311 312 LeaveCriticalSection(&CriticalSection); 313} 314 315/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or 316/// SIGSEGV) is delivered to the process, print a stack trace and then exit. 317void sys::PrintStackTraceOnErrorSignal() { 318 RegisterHandler(); 319 LeaveCriticalSection(&CriticalSection); 320} 321 322void llvm::sys::PrintStackTrace(FILE *) { 323 // FIXME: Implement. 324} 325 326 327void sys::SetInterruptFunction(void (*IF)()) { 328 RegisterHandler(); 329 InterruptFunction = IF; 330 LeaveCriticalSection(&CriticalSection); 331} 332 333 334/// AddSignalHandler - Add a function to be called when a signal is delivered 335/// to the process. The handler can have a cookie passed to it to identify 336/// what instance of the handler it is. 337void sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) { 338 if (CallBacksToRun == 0) 339 CallBacksToRun = new std::vector<std::pair<void(*)(void*), void*> >(); 340 CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie)); 341 RegisterHandler(); 342 LeaveCriticalSection(&CriticalSection); 343} 344} 345 346static void Cleanup() { 347 EnterCriticalSection(&CriticalSection); 348 349 // Prevent other thread from registering new files and directories for 350 // removal, should we be executing because of the console handler callback. 351 CleanupExecuted = true; 352 353 // FIXME: open files cannot be deleted. 354 355 if (FilesToRemove != NULL) 356 while (!FilesToRemove->empty()) { 357 sys::Path(FilesToRemove->back()).eraseFromDisk(); 358 FilesToRemove->pop_back(); 359 } 360 361 if (CallBacksToRun) 362 for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i) 363 (*CallBacksToRun)[i].first((*CallBacksToRun)[i].second); 364 365 LeaveCriticalSection(&CriticalSection); 366} 367 368void llvm::sys::RunInterruptHandlers() { 369 Cleanup(); 370} 371 372static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { 373 Cleanup(); 374 375 // Initialize the STACKFRAME structure. 376 STACKFRAME64 StackFrame; 377 memset(&StackFrame, 0, sizeof(StackFrame)); 378 379 DWORD machineType; 380#if defined(_M_X64) 381 machineType = IMAGE_FILE_MACHINE_AMD64; 382 StackFrame.AddrPC.Offset = ep->ContextRecord->Rip; 383 StackFrame.AddrPC.Mode = AddrModeFlat; 384 StackFrame.AddrStack.Offset = ep->ContextRecord->Rsp; 385 StackFrame.AddrStack.Mode = AddrModeFlat; 386 StackFrame.AddrFrame.Offset = ep->ContextRecord->Rbp; 387 StackFrame.AddrFrame.Mode = AddrModeFlat; 388#elif defined(_M_IX86) 389 machineType = IMAGE_FILE_MACHINE_I386; 390 StackFrame.AddrPC.Offset = ep->ContextRecord->Eip; 391 StackFrame.AddrPC.Mode = AddrModeFlat; 392 StackFrame.AddrStack.Offset = ep->ContextRecord->Esp; 393 StackFrame.AddrStack.Mode = AddrModeFlat; 394 StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp; 395 StackFrame.AddrFrame.Mode = AddrModeFlat; 396#endif 397 398 HANDLE hProcess = GetCurrentProcess(); 399 HANDLE hThread = GetCurrentThread(); 400 401 // Initialize the symbol handler. 402 SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES); 403 SymInitialize(hProcess, NULL, TRUE); 404 405 while (true) { 406 if (!StackWalk64(machineType, hProcess, hThread, &StackFrame, 407 ep->ContextRecord, NULL, SymFunctionTableAccess64, 408 SymGetModuleBase64, NULL)) { 409 break; 410 } 411 412 if (StackFrame.AddrFrame.Offset == 0) 413 break; 414 415 // Print the PC in hexadecimal. 416 DWORD64 PC = StackFrame.AddrPC.Offset; 417#if defined(_M_X64) 418 fprintf(stderr, "0x%016llX", PC); 419#elif defined(_M_IX86) 420 fprintf(stderr, "0x%08lX", static_cast<DWORD>(PC)); 421#endif 422 423 // Print the parameters. Assume there are four. 424#if defined(_M_X64) 425 fprintf(stderr, " (0x%016llX 0x%016llX 0x%016llX 0x%016llX)", 426 StackFrame.Params[0], 427 StackFrame.Params[1], 428 StackFrame.Params[2], 429 StackFrame.Params[3]); 430#elif defined(_M_IX86) 431 fprintf(stderr, " (0x%08lX 0x%08lX 0x%08lX 0x%08lX)", 432 static_cast<DWORD>(StackFrame.Params[0]), 433 static_cast<DWORD>(StackFrame.Params[1]), 434 static_cast<DWORD>(StackFrame.Params[2]), 435 static_cast<DWORD>(StackFrame.Params[3])); 436#endif 437 // Verify the PC belongs to a module in this process. 438 if (!SymGetModuleBase64(hProcess, PC)) { 439 fputs(" <unknown module>\n", stderr); 440 continue; 441 } 442 443 // Print the symbol name. 444 char buffer[512]; 445 IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast<IMAGEHLP_SYMBOL64 *>(buffer); 446 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64)); 447 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); 448 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64); 449 450 DWORD64 dwDisp; 451 if (!SymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) { 452 fputc('\n', stderr); 453 continue; 454 } 455 456 buffer[511] = 0; 457 if (dwDisp > 0) 458 fprintf(stderr, ", %s() + 0x%llX bytes(s)", symbol->Name, dwDisp); 459 else 460 fprintf(stderr, ", %s", symbol->Name); 461 462 // Print the source file and line number information. 463 IMAGEHLP_LINE64 line; 464 DWORD dwLineDisp; 465 memset(&line, 0, sizeof(line)); 466 line.SizeOfStruct = sizeof(line); 467 if (SymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) { 468 fprintf(stderr, ", %s, line %lu", line.FileName, line.LineNumber); 469 if (dwLineDisp > 0) 470 fprintf(stderr, " + 0x%lX byte(s)", dwLineDisp); 471 } 472 473 fputc('\n', stderr); 474 } 475 476 if (ExitOnUnhandledExceptions) 477 _exit(ep->ExceptionRecord->ExceptionCode); 478 479 // Allow dialog box to pop up allowing choice to start debugger. 480 if (OldFilter) 481 return (*OldFilter)(ep); 482 else 483 return EXCEPTION_CONTINUE_SEARCH; 484} 485 486static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) { 487 // We are running in our very own thread, courtesy of Windows. 488 EnterCriticalSection(&CriticalSection); 489 Cleanup(); 490 491 // If an interrupt function has been set, go and run one it; otherwise, 492 // the process dies. 493 void (*IF)() = InterruptFunction; 494 InterruptFunction = 0; // Don't run it on another CTRL-C. 495 496 if (IF) { 497 // Note: if the interrupt function throws an exception, there is nothing 498 // to catch it in this thread so it will kill the process. 499 IF(); // Run it now. 500 LeaveCriticalSection(&CriticalSection); 501 return TRUE; // Don't kill the process. 502 } 503 504 // Allow normal processing to take place; i.e., the process dies. 505 LeaveCriticalSection(&CriticalSection); 506 return FALSE; 507} 508 509#if __MINGW32__ 510 // We turned these warnings off for this file so that MinGW-g++ doesn't 511 // complain about the ll format specifiers used. Now we are turning the 512 // warnings back on. If MinGW starts to support diagnostic stacks, we can 513 // replace this with a pop. 514 #pragma GCC diagnostic warning "-Wformat" 515 #pragma GCC diagnostic warning "-Wformat-extra-args" 516#endif 517