]> gitweb.factorcode.org Git - factor.git/blob - vm/os-windows.cpp
e48bdca8c0370a042195d2983943cb494273cda5
[factor.git] / vm / os-windows.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 HMODULE hFactorDll;
7
8 void factor_vm::init_ffi()
9 {
10         hFactorDll = GetModuleHandle(FACTOR_DLL);
11         if(!hFactorDll)
12                 fatal_error("GetModuleHandle() failed", 0);
13 }
14
15 void factor_vm::ffi_dlopen(dll *dll)
16 {
17         dll->handle = LoadLibraryEx((WCHAR *)alien_offset(dll->path), NULL, 0);
18 }
19
20 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
21 {
22         return (void *)GetProcAddress(dll ? (HMODULE)dll->handle : hFactorDll, symbol);
23 }
24
25 void *factor_vm::ffi_dlsym_raw(dll *dll, symbol_char *symbol)
26 {
27         return ffi_dlsym(dll, symbol);
28 }
29
30 void factor_vm::ffi_dlclose(dll *dll)
31 {
32         FreeLibrary((HMODULE)dll->handle);
33         dll->handle = NULL;
34 }
35
36 BOOL factor_vm::windows_stat(vm_char *path)
37 {
38         BY_HANDLE_FILE_INFORMATION bhfi;
39         HANDLE h = CreateFileW(path,
40                         GENERIC_READ,
41                         FILE_SHARE_READ,
42                         NULL,
43                         OPEN_EXISTING,
44                         FILE_FLAG_BACKUP_SEMANTICS,
45                         NULL);
46
47         if(h == INVALID_HANDLE_VALUE)
48         {
49                 // FindFirstFile is the only call that can stat c:\pagefile.sys
50                 WIN32_FIND_DATA st;
51                 HANDLE h;
52
53                 if(INVALID_HANDLE_VALUE == (h = FindFirstFile(path, &st)))
54                         return false;
55                 FindClose(h);
56                 return true;
57         }
58         BOOL ret = GetFileInformationByHandle(h, &bhfi);
59         CloseHandle(h);
60         return ret;
61 }
62
63 void factor_vm::windows_image_path(vm_char *full_path, vm_char *temp_path, unsigned int length)
64 {
65         wcsncpy(temp_path, full_path, length - 1);
66         size_t full_path_len = wcslen(full_path);
67         if (full_path_len < length - 1)
68                 wcsncat(temp_path, L".image", length - full_path_len - 1);
69         temp_path[length - 1] = 0;
70 }
71
72 /* You must free() this yourself. */
73 const vm_char *factor_vm::default_image_path()
74 {
75         vm_char full_path[MAX_UNICODE_PATH];
76         vm_char *ptr;
77         vm_char temp_path[MAX_UNICODE_PATH];
78
79         if(!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
80                 fatal_error("GetModuleFileName() failed", 0);
81
82         if((ptr = wcsrchr(full_path, '.')))
83                 *ptr = 0;
84
85         wcsncpy(temp_path, full_path, MAX_UNICODE_PATH - 1);
86         size_t full_path_len = wcslen(full_path);
87         if (full_path_len < MAX_UNICODE_PATH - 1)
88                 wcsncat(temp_path, L".image", MAX_UNICODE_PATH - full_path_len - 1);
89         temp_path[MAX_UNICODE_PATH - 1] = 0;
90
91         return safe_strdup(temp_path);
92 }
93
94 /* You must free() this yourself. */
95 const vm_char *factor_vm::vm_executable_path()
96 {
97         vm_char full_path[MAX_UNICODE_PATH];
98         if(!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
99                 fatal_error("GetModuleFileName() failed", 0);
100         return safe_strdup(full_path);
101 }
102
103 void factor_vm::primitive_existsp()
104 {
105         vm_char *path = untag_check<byte_array>(ctx->pop())->data<vm_char>();
106         ctx->push(tag_boolean(windows_stat(path)));
107 }
108
109 segment::segment(cell size_, bool executable_p)
110 {
111         size = size_;
112
113         char *mem;
114         DWORD ignore;
115
116         if((mem = (char *)VirtualAlloc(NULL, getpagesize() * 2 + size,
117                 MEM_COMMIT, executable_p ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE)) == 0)
118                 out_of_memory();
119
120         if (!VirtualProtect(mem, getpagesize(), PAGE_NOACCESS, &ignore))
121                 fatal_error("Cannot allocate low guard page", (cell)mem);
122
123         if (!VirtualProtect(mem + size + getpagesize(),
124                 getpagesize(), PAGE_NOACCESS, &ignore))
125                 fatal_error("Cannot allocate high guard page", (cell)mem);
126
127         start = (cell)mem + getpagesize();
128         end = start + size;
129 }
130
131 segment::~segment()
132 {
133         SYSTEM_INFO si;
134         GetSystemInfo(&si);
135         if(!VirtualFree((void*)(start - si.dwPageSize), 0, MEM_RELEASE))
136                 fatal_error("Segment deallocation failed",0);
137 }
138
139 long getpagesize()
140 {
141         static long g_pagesize = 0;
142         if(!g_pagesize)
143         {
144                 SYSTEM_INFO system_info;
145                 GetSystemInfo (&system_info);
146                 g_pagesize = system_info.dwPageSize;
147         }
148         return g_pagesize;
149 }
150
151 void code_heap::guard_safepoint()
152 {
153         DWORD ignore;
154         if (!VirtualProtect(safepoint_page, getpagesize(), PAGE_NOACCESS, &ignore))
155                 fatal_error("Cannot protect safepoint guard page", (cell)safepoint_page);
156 }
157
158 void code_heap::unguard_safepoint()
159 {
160         DWORD ignore;
161         if (!VirtualProtect(safepoint_page, getpagesize(), PAGE_READWRITE, &ignore))
162                 fatal_error("Cannot unprotect safepoint guard page", (cell)safepoint_page);
163 }
164
165 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
166 {
167         if(MoveFileEx((path1),(path2),MOVEFILE_REPLACE_EXISTING) == false)
168                 general_error(ERROR_IO,tag_fixnum(GetLastError()),false_object);
169 }
170
171 void factor_vm::init_signals() {}
172
173 THREADHANDLE start_thread(void *(*start_routine)(void *), void *args)
174 {
175         return (void *)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, args, 0, 0);
176 }
177
178 u64 nano_count()
179 {
180         static double scale_factor;
181
182         static u32 hi = 0;
183         static u32 lo = 0;
184
185         LARGE_INTEGER count;
186         BOOL ret = QueryPerformanceCounter(&count);
187         if(ret == 0)
188                 fatal_error("QueryPerformanceCounter", 0);
189
190         if(scale_factor == 0.0)
191         {
192                 LARGE_INTEGER frequency;
193                 BOOL ret = QueryPerformanceFrequency(&frequency);
194                 if(ret == 0)
195                         fatal_error("QueryPerformanceFrequency", 0);
196                 scale_factor = (1000000000.0 / frequency.QuadPart);
197         }
198
199 #ifdef FACTOR_64
200         hi = count.HighPart;
201 #else
202         /* On VirtualBox, QueryPerformanceCounter does not increment
203         the high part every time the low part overflows.  Workaround. */
204         if(lo > count.LowPart)
205                 hi++;
206 #endif
207         lo = count.LowPart;
208
209         return (u64)((((u64)hi << 32) | (u64)lo) * scale_factor);
210 }
211
212 void sleep_nanos(u64 nsec)
213 {
214         Sleep((DWORD)(nsec/1000000));
215 }
216
217 typedef enum _EXCEPTION_DISPOSITION
218 {
219         ExceptionContinueExecution = 0,
220         ExceptionContinueSearch = 1,
221         ExceptionNestedException = 2,
222         ExceptionCollidedUnwind = 3
223 } EXCEPTION_DISPOSITION;
224
225 LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch)
226 {
227         switch (e->ExceptionCode)
228         {
229         case EXCEPTION_ACCESS_VIOLATION:
230                 signal_fault_addr = e->ExceptionInformation[1];
231                 verify_memory_protection_error(signal_fault_addr);
232                 dispatch_signal_handler(
233                         (cell*)&c->ESP,
234                         (cell*)&c->EIP,
235                         (cell)factor::memory_signal_handler_impl
236                 );
237                 break;
238
239         case STATUS_FLOAT_DENORMAL_OPERAND:
240         case STATUS_FLOAT_DIVIDE_BY_ZERO:
241         case STATUS_FLOAT_INEXACT_RESULT:
242         case STATUS_FLOAT_INVALID_OPERATION:
243         case STATUS_FLOAT_OVERFLOW:
244         case STATUS_FLOAT_STACK_CHECK:
245         case STATUS_FLOAT_UNDERFLOW:
246         case STATUS_FLOAT_MULTIPLE_FAULTS:
247         case STATUS_FLOAT_MULTIPLE_TRAPS:
248 #ifdef FACTOR_64
249                 signal_fpu_status = fpu_status(MXCSR(c));
250 #else
251                 signal_fpu_status = fpu_status(X87SW(c) | MXCSR(c));
252
253                 /* This seems to have no effect */
254                 X87SW(c) = 0;
255 #endif
256                 MXCSR(c) &= 0xffffffc0;
257                 dispatch_signal_handler(
258                         (cell*)&c->ESP,
259                         (cell*)&c->EIP,
260                         (cell)factor::fp_signal_handler_impl
261                 );
262                 break;
263         default:
264                 signal_number = e->ExceptionCode;
265                 dispatch_signal_handler(
266                         (cell*)&c->ESP,
267                         (cell*)&c->EIP,
268                         (cell)factor::synchronous_signal_handler_impl
269                 );
270                 break;
271         }
272
273         return ExceptionContinueExecution;
274 }
275
276 VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch)
277 {
278         if (factor_vm::fatal_erroring_p)
279                 return ExceptionContinueSearch;
280
281         factor_vm *vm = current_vm_p();
282         if (vm)
283                 return vm->exception_handler(e,frame,c,dispatch);
284         else
285                 return ExceptionContinueSearch;
286 }
287
288 static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
289 {
290         switch (dwCtrlType) {
291         case CTRL_C_EVENT:
292                 {
293                         /* The CtrlHandler runs in its own thread without stopping the main thread.
294                         Since in practice nobody uses the multi-VM stuff yet, we just grab the first
295                         VM we can get. This will not be a good idea when we actually support native
296                         threads. */
297                         FACTOR_ASSERT(thread_vms.size() == 1);
298                         factor_vm *vm = thread_vms.begin()->second;
299                         vm->safepoint.enqueue_fep(vm);
300                         return TRUE;
301                 }
302         default:
303                 return FALSE;
304         }
305 }
306
307 void factor_vm::open_console()
308 {
309         handle_ctrl_c();
310 }
311
312 void factor_vm::ignore_ctrl_c()
313 {
314         SetConsoleCtrlHandler(factor::ctrl_handler, FALSE);
315 }
316
317 void factor_vm::handle_ctrl_c()
318 {
319         SetConsoleCtrlHandler(factor::ctrl_handler, TRUE);
320 }
321
322 void factor_vm::lock_console()
323 {
324 }
325
326 void factor_vm::unlock_console()
327 {
328 }
329
330 void factor_vm::close_console()
331 {
332 }
333
334 void factor_vm::sampler_thread_loop()
335 {
336         LARGE_INTEGER counter, new_counter, units_per_second;
337         DWORD ok;
338
339         ok = QueryPerformanceFrequency(&units_per_second);
340         FACTOR_ASSERT(ok);
341
342         ok = QueryPerformanceCounter(&counter);
343         FACTOR_ASSERT(ok);
344
345         counter.QuadPart *= samples_per_second;
346         while (atomic::load(&sampling_profiler_p))
347         {
348                 SwitchToThread();
349                 ok = QueryPerformanceCounter(&new_counter);
350                 FACTOR_ASSERT(ok);
351                 new_counter.QuadPart *= samples_per_second;
352                 cell samples = 0;
353                 while (new_counter.QuadPart - counter.QuadPart > units_per_second.QuadPart)
354                 {
355                         ++samples;
356                         counter.QuadPart += units_per_second.QuadPart;
357                 }
358
359                 if (samples > 0)
360                 {
361                         DWORD suscount = SuspendThread(thread);
362                         FACTOR_ASSERT(suscount == 0);
363
364                         CONTEXT context;
365                         memset((void*)&context, 0, sizeof(CONTEXT));
366                         context.ContextFlags = CONTEXT_CONTROL;
367                         BOOL context_ok = GetThreadContext(thread, &context);
368                         FACTOR_ASSERT(context_ok);
369
370                         suscount = ResumeThread(thread);
371                         FACTOR_ASSERT(suscount == 1);
372
373                         safepoint.enqueue_samples(this, samples, context.EIP, false);
374                 }
375         }
376 }
377
378 static DWORD WINAPI sampler_thread_entry(LPVOID parent_vm)
379 {
380         static_cast<factor_vm*>(parent_vm)->sampler_thread_loop();
381         return 0;
382 }
383
384 void factor_vm::start_sampling_profiler_timer()
385 {
386         sampler_thread = CreateThread(
387                 NULL,
388                 0,
389                 &sampler_thread_entry,
390                 static_cast<LPVOID>(this),
391                 0,
392                 NULL
393         );
394 }
395
396 void factor_vm::end_sampling_profiler_timer()
397 {
398         atomic::store(&sampling_profiler_p, false);
399         DWORD wait_result = WaitForSingleObject(sampler_thread,
400                 3000*(DWORD)samples_per_second);
401         if (wait_result != WAIT_OBJECT_0)
402                 TerminateThread(sampler_thread, 0);
403         sampler_thread = NULL;
404 }
405
406 void abort()
407 {
408         ::abort();
409 }
410
411 }