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