]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: groundwork for sampling profiler
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 THREADHANDLE start_thread(void *(*start_routine)(void *),void *args)
7 {
8         pthread_attr_t attr;
9         pthread_t thread;
10         if (pthread_attr_init (&attr) != 0)
11                 fatal_error("pthread_attr_init() failed",0);
12         if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) != 0)
13                 fatal_error("pthread_attr_setdetachstate() failed",0);
14         if (pthread_create (&thread, &attr, start_routine, args) != 0)
15                 fatal_error("pthread_create() failed",0);
16         pthread_attr_destroy(&attr);
17         return thread;
18 }
19
20 static void *null_dll;
21
22 void sleep_nanos(u64 nsec)
23 {
24         timespec ts;
25         timespec ts_rem;
26         int ret;
27         ts.tv_sec = nsec / 1000000000;
28         ts.tv_nsec = nsec % 1000000000;
29         ret = nanosleep(&ts,&ts_rem);
30         while(ret == -1 && errno == EINTR)
31         {
32                 memcpy(&ts, &ts_rem, sizeof(ts));
33                 ret = nanosleep(&ts, &ts_rem);
34         }
35
36         if(ret == -1)
37                 fatal_error("nanosleep failed", 0);
38 }
39
40 void factor_vm::init_ffi()
41 {
42         null_dll = dlopen(NULL,RTLD_LAZY);
43 }
44
45 void factor_vm::ffi_dlopen(dll *dll)
46 {
47         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
48 }
49
50 void *factor_vm::ffi_dlsym_raw(dll *dll, symbol_char *symbol)
51 {
52         return dlsym(dll ? dll->handle : null_dll, symbol);
53 }
54
55 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
56 {
57         return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
58 }
59
60 #ifdef FACTOR_PPC
61 void *factor_vm::ffi_dlsym_toc(dll *dll, symbol_char *symbol)
62 {
63         return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
64 }
65 #endif
66
67 void factor_vm::ffi_dlclose(dll *dll)
68 {
69         if(dlclose(dll->handle))
70                 general_error(ERROR_FFI,false_object,false_object);
71         dll->handle = NULL;
72 }
73
74 void factor_vm::primitive_existsp()
75 {
76         struct stat sb;
77         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
78         ctx->push(tag_boolean(stat(path,&sb) >= 0));
79 }
80
81 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
82 {
83         int ret = 0;
84         do
85         {
86                 ret = rename((path1),(path2));
87         }
88         while(ret < 0 && errno == EINTR);
89
90         if(ret < 0)
91                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
92 }
93
94 segment::segment(cell size_, bool executable_p)
95 {
96         size = size_;
97
98         int pagesize = getpagesize();
99
100         int prot;
101         if(executable_p)
102                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
103         else
104                 prot = (PROT_READ | PROT_WRITE);
105
106         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
107         if(array == (char*)-1) out_of_memory();
108
109         if(mprotect(array,pagesize,PROT_NONE) == -1)
110                 fatal_error("Cannot protect low guard page",(cell)array);
111
112         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
113                 fatal_error("Cannot protect high guard page",(cell)array);
114
115         start = (cell)(array + pagesize);
116         end = start + size;
117 }
118
119 segment::~segment()
120 {
121         int pagesize = getpagesize();
122         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
123         if(retval)
124                 fatal_error("Segment deallocation failed",0);
125 }
126
127 void code_heap::guard_safepoint()
128 {
129         if(mprotect(safepoint_page,getpagesize(),PROT_NONE) == -1)
130                 fatal_error("Cannot protect safepoint guard page",(cell)safepoint_page);
131 }
132
133 void code_heap::unguard_safepoint()
134 {
135         if(mprotect(safepoint_page,getpagesize(),PROT_WRITE) == -1)
136                 fatal_error("Cannot unprotect safepoint guard page",(cell)safepoint_page);
137 }
138
139 void factor_vm::dispatch_signal(void *uap, void (handler)())
140 {
141         dispatch_signal_handler(
142                 (cell*)&UAP_STACK_POINTER(uap),
143                 (cell*)&UAP_PROGRAM_COUNTER(uap),
144                 (cell)FUNCTION_CODE_POINTER(handler)
145         );
146         UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
147 }
148
149 void factor_vm::enqueue_safepoint_signal(cell signal)
150 {
151         /* to be implemented, see #297
152         code->guard_safepoint();
153         */
154 }
155
156 void factor_vm::start_sampling_profiler_timer()
157 {
158         struct itimerval timer;
159         memset((void*)&timer, 0, sizeof(struct itimerval));
160         timer.it_value.tv_usec = 1000000/FACTOR_PROFILE_SAMPLES_PER_SECOND;
161         timer.it_interval.tv_usec = 1000000/FACTOR_PROFILE_SAMPLES_PER_SECOND;
162         setitimer(ITIMER_REAL, &timer, NULL);
163 }
164
165 void factor_vm::end_sampling_profiler_timer()
166 {
167         struct itimerval timer;
168         memset((void*)&timer, 0, sizeof(struct itimerval));
169         setitimer(ITIMER_REAL, &timer, NULL);
170 }
171
172 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
173 {
174         factor_vm *vm = current_vm();
175         vm->signal_fault_addr = (cell)siginfo->si_addr;
176         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
177 }
178
179 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
180 {
181         factor_vm *vm = current_vm_p();
182         if (vm)
183         {
184                 vm->signal_number = signal;
185                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
186         } else
187                 fatal_error("Foreign thread received signal", signal);
188 }
189
190 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
191 {
192         factor_vm *vm = current_vm_p();
193         if (vm)
194                 vm->enqueue_safepoint_signal(signal);
195         else
196                 fatal_error("Foreign thread received signal", signal);
197 }
198
199 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
200 {
201         factor_vm *vm = current_vm_p();
202         if (vm)
203                 vm->enqueue_safepoint_fep();
204         else
205                 fatal_error("Foreign thread received signal", signal);
206 }
207
208 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
209 {
210         factor_vm *vm = current_vm_p();
211         if (vm)
212                 vm->enqueue_safepoint_sample();
213         else
214                 fatal_error("Foreign thread received signal", signal);
215 }
216
217 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
218 {
219 }
220
221 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
222 {
223         factor_vm *vm = current_vm();
224         vm->signal_number = signal;
225         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
226         uap_clear_fpu_status(uap);
227
228         vm->dispatch_signal(uap,
229                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
230                 ? factor::synchronous_signal_handler_impl
231                 : factor::fp_signal_handler_impl);
232 }
233
234 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
235 {
236         int ret;
237         do
238         {
239                 ret = sigaction(signum, act, oldact);
240         }
241         while(ret == -1 && errno == EINTR);
242
243         if(ret == -1)
244                 fatal_error("sigaction failed", 0);
245 }
246
247 static void init_sigaction_with_handler(struct sigaction *act,
248         void (*handler)(int, siginfo_t*, void*))
249 {
250         memset(act, 0, sizeof(struct sigaction));
251         sigemptyset(&act->sa_mask);
252         act->sa_sigaction = handler;
253         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
254 }
255
256 void factor_vm::unix_init_signals()
257 {
258         struct sigaction memory_sigaction;
259         struct sigaction synchronous_sigaction;
260         struct sigaction enqueue_sigaction;
261         struct sigaction fep_sigaction;
262         struct sigaction sample_sigaction;
263         struct sigaction fpe_sigaction;
264         struct sigaction ignore_sigaction;
265
266         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
267         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
268         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
269         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
270
271         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
272         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
273
274         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
275         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
276         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
277
278         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
279         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
280         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
281         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
282 #ifdef SIGINFO
283         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
284 #endif
285
286         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
287         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
288         sigaction_safe(SIGINT,&fep_sigaction,NULL);
289
290         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
291         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
292
293         /* We don't use SA_IGN here because then the ignore action is inherited
294         by subprocesses, which we don't want. There is a unit test in
295         io.launcher.unix for this. */
296         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
297         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
298 }
299
300 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
301 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
302 so we kludge around this by spawning a thread, which waits on a control pipe
303 for a signal, upon receiving this signal it reads one block of data from stdin
304 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
305 the size pipe, indicating how much data was written to the data pipe.
306
307 The read end of the size pipe can be set to non-blocking. */
308 extern "C" {
309         int stdin_read;
310         int stdin_write;
311
312         int control_read;
313         int control_write;
314
315         int size_read;
316         int size_write;
317 }
318
319 void safe_close(int fd)
320 {
321         if(close(fd) < 0)
322                 fatal_error("error closing fd",errno);
323 }
324
325 bool check_write(int fd, void *data, ssize_t size)
326 {
327         if(write(fd,data,size) == size)
328                 return true;
329         else
330         {
331                 if(errno == EINTR)
332                         return check_write(fd,data,size);
333                 else
334                         return false;
335         }
336 }
337
338 void safe_write(int fd, void *data, ssize_t size)
339 {
340         if(!check_write(fd,data,size))
341                 fatal_error("error writing fd",errno);
342 }
343
344 bool safe_read(int fd, void *data, ssize_t size)
345 {
346         ssize_t bytes = read(fd,data,size);
347         if(bytes < 0)
348         {
349                 if(errno == EINTR)
350                         return safe_read(fd,data,size);
351                 else
352                 {
353                         fatal_error("error reading fd",errno);
354                         return false;
355                 }
356         }
357         else
358                 return (bytes == size);
359 }
360
361 void *stdin_loop(void *arg)
362 {
363         unsigned char buf[4096];
364         bool loop_running = true;
365
366         sigset_t mask;
367         sigfillset(&mask);
368         pthread_sigmask(SIG_BLOCK, &mask, NULL);
369
370         while(loop_running)
371         {
372                 if(!safe_read(control_read,buf,1))
373                         break;
374
375                 if(buf[0] != 'X')
376                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
377
378                 for(;;)
379                 {
380                         ssize_t bytes = read(0,buf,sizeof(buf));
381                         if(bytes < 0)
382                         {
383                                 if(errno == EINTR)
384                                         continue;
385                                 else
386                                 {
387                                         loop_running = false;
388                                         break;
389                                 }
390                         }
391                         else if(bytes >= 0)
392                         {
393                                 safe_write(size_write,&bytes,sizeof(bytes));
394
395                                 if(!check_write(stdin_write,buf,bytes))
396                                         loop_running = false;
397                                 break;
398                         }
399                 }
400         }
401
402         safe_close(stdin_write);
403         safe_close(control_read);
404
405         return NULL;
406 }
407
408 void safe_pipe(int *in, int *out)
409 {
410         int filedes[2];
411
412         if(pipe(filedes) < 0)
413                 fatal_error("Error opening pipe",errno);
414
415         *in = filedes[0];
416         *out = filedes[1];
417
418         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
419                 fatal_error("Error with fcntl",errno);
420
421         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
422                 fatal_error("Error with fcntl",errno);
423 }
424
425 void open_console()
426 {
427         safe_pipe(&control_read,&control_write);
428         safe_pipe(&size_read,&size_write);
429         safe_pipe(&stdin_read,&stdin_write);
430         start_thread(stdin_loop,NULL);
431 }
432
433 }