Porting of Unix implementation to Win32.
[oota-llvm.git] / lib / System / Win32 / Signals.cpp
1 //===- Win32/Signals.cpp - Win32 Signals Implementation ---------*- C++ -*-===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Jeff Cohen and is distributed under the 
6 // University of Illinois Open Source 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 "Win32.h"
15 #include <llvm/System/Signals.h>
16 #include <vector>
17
18 #include "dbghelp.h"
19 #include "psapi.h"
20
21 #pragma comment(lib, "psapi.lib")
22 #pragma comment(lib, "dbghelp.lib")
23
24 // Forward declare.
25 static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep);
26 static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType);
27
28 static std::vector<std::string> *FilesToRemove = NULL;
29 static std::vector<llvm::sys::Path> *DirectoriesToRemove = NULL;
30 static bool RegisteredUnhandledExceptionFilter = false;
31
32 // Windows creates a new thread to execute the console handler when an event
33 // (such as CTRL/C) occurs.  This causes concurrency issues with the above
34 // globals which this critical section addresses.
35 static CRITICAL_SECTION CriticalSection;
36
37 namespace llvm {
38
39 //===----------------------------------------------------------------------===//
40 //=== WARNING: Implementation here must contain only Win32 specific code 
41 //===          and must not be UNIX code
42 //===----------------------------------------------------------------------===//
43
44
45 static void RegisterHandler() { 
46   if (RegisteredUnhandledExceptionFilter)
47   {
48     EnterCriticalSection(&CriticalSection);
49     return;
50   }
51
52   // Now's the time to create the critical section.  This is the first time
53   // through here, and there's only one thread.
54   InitializeCriticalSection(&CriticalSection);
55
56   // Enter it immediately.  Now if someone hits CTRL/C, the console handler
57   // can't proceed until the globals are updated.
58   EnterCriticalSection(&CriticalSection);
59
60   RegisteredUnhandledExceptionFilter = true;
61   SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter);
62   SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE);
63
64   // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or
65   // else multi-threading problems will ensue.
66 }
67
68 // RemoveFileOnSignal - The public API
69 void sys::RemoveFileOnSignal(const std::string &Filename) {
70   RegisterHandler();
71
72   if (FilesToRemove == NULL)
73     FilesToRemove = new std::vector<std::string>;
74
75   FilesToRemove->push_back(Filename);
76
77   LeaveCriticalSection(&CriticalSection);
78 }
79
80 // RemoveDirectoryOnSignal - The public API
81 void sys::RemoveDirectoryOnSignal(const sys::Path& path) {
82   RegisterHandler();
83
84   if (path.is_directory()) {
85     if (DirectoriesToRemove == NULL)
86       DirectoriesToRemove = new std::vector<sys::Path>;
87
88     DirectoriesToRemove->push_back(path);
89   }
90
91   LeaveCriticalSection(&CriticalSection);
92 }
93
94 /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
95 /// SIGSEGV) is delivered to the process, print a stack trace and then exit.
96 void sys::PrintStackTraceOnErrorSignal() {
97   RegisterHandler();
98   LeaveCriticalSection(&CriticalSection);
99 }
100
101 }
102
103 static void Cleanup() {
104   EnterCriticalSection(&CriticalSection);
105
106   if (FilesToRemove != NULL)
107     while (!FilesToRemove->empty()) {
108       try {
109         std::remove(FilesToRemove->back().c_str());
110       } catch (...) {
111       }
112       FilesToRemove->pop_back();
113     }
114
115   if (DirectoriesToRemove != NULL)
116     while (!DirectoriesToRemove->empty()) {
117       try {
118         DirectoriesToRemove->back().destroy_directory(true);
119       } catch (...) {
120       }
121       DirectoriesToRemove->pop_back();
122     }
123
124   LeaveCriticalSection(&CriticalSection);
125 }
126
127 static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
128   try {
129     Cleanup();
130
131     // Initialize the STACKFRAME structure.
132     STACKFRAME StackFrame;
133     memset(&StackFrame, 0, sizeof(StackFrame));
134
135     StackFrame.AddrPC.Offset = ep->ContextRecord->Eip;
136     StackFrame.AddrPC.Mode = AddrModeFlat;
137     StackFrame.AddrStack.Offset = ep->ContextRecord->Esp;
138     StackFrame.AddrStack.Mode = AddrModeFlat;
139     StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp;
140     StackFrame.AddrFrame.Mode = AddrModeFlat;
141
142     HANDLE hProcess = GetCurrentProcess();
143     HANDLE hThread = GetCurrentThread();
144
145     // Initialize the symbol handler.
146     SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES);
147     SymInitialize(GetCurrentProcess(), NULL, TRUE);
148
149     while (true) {
150       if (!StackWalk(IMAGE_FILE_MACHINE_I386, hProcess, hThread, &StackFrame,
151                      ep->ContextRecord, NULL, SymFunctionTableAccess,
152                      SymGetModuleBase, NULL)) {
153         break;
154       }
155
156       if (StackFrame.AddrFrame.Offset == 0)
157         break;
158
159       // Print the PC in hexadecimal.
160       DWORD PC = StackFrame.AddrPC.Offset;
161       fprintf(stderr, "%04X:%08X", ep->ContextRecord->SegCs, PC);
162
163       // Print the parameters.  Assume there are four.
164       fprintf(stderr, " (0x%08X 0x%08X 0x%08X 0x%08X)", StackFrame.Params[0],
165               StackFrame.Params[1], StackFrame.Params[2], StackFrame.Params[3]);
166
167       // Verify the PC belongs to a module in this process.
168       if (!SymGetModuleBase(hProcess, PC)) {
169         fputc('\n', stderr);
170         continue;
171       }
172
173       // Print the symbol name.
174       char buffer[512];
175       IMAGEHLP_SYMBOL *symbol = reinterpret_cast<IMAGEHLP_SYMBOL *>(buffer);
176       memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL));
177       symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
178       symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL);
179
180       DWORD dwDisp;
181       if (!SymGetSymFromAddr(hProcess, PC, &dwDisp, symbol)) {
182         fputc('\n', stderr);
183         continue;
184       }
185
186       buffer[511] = 0;
187       if (dwDisp > 0)
188         fprintf(stderr, ", %s()+%04d bytes(s)", symbol->Name, dwDisp);
189       else
190         fprintf(stderr, ", %s", symbol->Name);
191
192       // Print the source file and line number information.
193       IMAGEHLP_LINE line;
194       memset(&line, 0, sizeof(line));
195       line.SizeOfStruct = sizeof(line);
196       if (SymGetLineFromAddr(hProcess, PC, &dwDisp, &line)) {
197         fprintf(stderr, ", %s, line %d", line.FileName, line.LineNumber);
198         if (dwDisp > 0)
199           fprintf(stderr, "+%04d byte(s)", dwDisp);
200       }
201
202       fputc('\n', stderr);
203     }
204   }
205   catch (...)
206   {
207       assert(!"Crashed in LLVMUnhandledExceptionFilter");
208   }
209
210   // Allow dialog box to pop up allowing choice to start debugger.
211   return EXCEPTION_CONTINUE_SEARCH;
212 }
213
214 static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {
215   // FIXME: This handler executes on a different thread.  The main thread
216   // is still running, potentially creating new files to be cleaned up
217   // in the tiny window between the call to Cleanup() and process termination.
218   // Also, any files currently open cannot be deleted.
219   Cleanup();
220
221   // Allow normal processing to take place; i.e., the process dies.
222   return FALSE;
223 }
224
225 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab