]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: parameterize sampling rate
[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/samples_per_second;
161         timer.it_interval.tv_usec = 1000000/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(1, (cell)UAP_PROGRAM_COUNTER(uap), false);
213         else if (thread_vms.size() == 1) {
214                 factor_vm *the_only_vm = thread_vms.begin()->second;
215                 the_only_vm->enqueue_safepoint_sample(1, (cell)UAP_PROGRAM_COUNTER(uap), true);
216         }
217 }
218
219 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
220 {
221 }
222
223 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
224 {
225         factor_vm *vm = current_vm();
226         vm->signal_number = signal;
227         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
228         uap_clear_fpu_status(uap);
229
230         vm->dispatch_signal(uap,
231                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
232                 ? factor::synchronous_signal_handler_impl
233                 : factor::fp_signal_handler_impl);
234 }
235
236 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
237 {
238         int ret;
239         do
240         {
241                 ret = sigaction(signum, act, oldact);
242         }
243         while(ret == -1 && errno == EINTR);
244
245         if(ret == -1)
246                 fatal_error("sigaction failed", 0);
247 }
248
249 static void init_sigaction_with_handler(struct sigaction *act,
250         void (*handler)(int, siginfo_t*, void*))
251 {
252         memset(act, 0, sizeof(struct sigaction));
253         sigemptyset(&act->sa_mask);
254         act->sa_sigaction = handler;
255         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
256 }
257
258 void factor_vm::unix_init_signals()
259 {
260         struct sigaction memory_sigaction;
261         struct sigaction synchronous_sigaction;
262         struct sigaction enqueue_sigaction;
263         struct sigaction fep_sigaction;
264         struct sigaction sample_sigaction;
265         struct sigaction fpe_sigaction;
266         struct sigaction ignore_sigaction;
267
268         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
269         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
270         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
271         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
272
273         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
274         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
275
276         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
277         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
278         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
279
280         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
281         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
282         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
283         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
284 #ifdef SIGINFO
285         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
286 #endif
287
288         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
289         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
290         sigaction_safe(SIGINT,&fep_sigaction,NULL);
291
292         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
293         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
294
295         /* We don't use SA_IGN here because then the ignore action is inherited
296         by subprocesses, which we don't want. There is a unit test in
297         io.launcher.unix for this. */
298         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
299         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
300 }
301
302 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
303 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
304 so we kludge around this by spawning a thread, which waits on a control pipe
305 for a signal, upon receiving this signal it reads one block of data from stdin
306 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
307 the size pipe, indicating how much data was written to the data pipe.
308
309 The read end of the size pipe can be set to non-blocking. */
310 extern "C" {
311         int stdin_read;
312         int stdin_write;
313
314         int control_read;
315         int control_write;
316
317         int size_read;
318         int size_write;
319 }
320
321 void safe_close(int fd)
322 {
323         if(close(fd) < 0)
324                 fatal_error("error closing fd",errno);
325 }
326
327 bool check_write(int fd, void *data, ssize_t size)
328 {
329         if(write(fd,data,size) == size)
330                 return true;
331         else
332         {
333                 if(errno == EINTR)
334                         return check_write(fd,data,size);
335                 else
336                         return false;
337         }
338 }
339
340 void safe_write(int fd, void *data, ssize_t size)
341 {
342         if(!check_write(fd,data,size))
343                 fatal_error("error writing fd",errno);
344 }
345
346 bool safe_read(int fd, void *data, ssize_t size)
347 {
348         ssize_t bytes = read(fd,data,size);
349         if(bytes < 0)
350         {
351                 if(errno == EINTR)
352                         return safe_read(fd,data,size);
353                 else
354                 {
355                         fatal_error("error reading fd",errno);
356                         return false;
357                 }
358         }
359         else
360                 return (bytes == size);
361 }
362
363 void *stdin_loop(void *arg)
364 {
365         unsigned char buf[4096];
366         bool loop_running = true;
367
368         sigset_t mask;
369         sigfillset(&mask);
370         pthread_sigmask(SIG_BLOCK, &mask, NULL);
371
372         while(loop_running)
373         {
374                 if(!safe_read(control_read,buf,1))
375                         break;
376
377                 if(buf[0] != 'X')
378                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
379
380                 for(;;)
381                 {
382                         ssize_t bytes = read(0,buf,sizeof(buf));
383                         if(bytes < 0)
384                         {
385                                 if(errno == EINTR)
386                                         continue;
387                                 else
388                                 {
389                                         loop_running = false;
390                                         break;
391                                 }
392                         }
393                         else if(bytes >= 0)
394                         {
395                                 safe_write(size_write,&bytes,sizeof(bytes));
396
397                                 if(!check_write(stdin_write,buf,bytes))
398                                         loop_running = false;
399                                 break;
400                         }
401                 }
402         }
403
404         safe_close(stdin_write);
405         safe_close(control_read);
406
407         return NULL;
408 }
409
410 void safe_pipe(int *in, int *out)
411 {
412         int filedes[2];
413
414         if(pipe(filedes) < 0)
415                 fatal_error("Error opening pipe",errno);
416
417         *in = filedes[0];
418         *out = filedes[1];
419
420         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
421                 fatal_error("Error with fcntl",errno);
422
423         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
424                 fatal_error("Error with fcntl",errno);
425 }
426
427 void open_console()
428 {
429         safe_pipe(&control_read,&control_write);
430         safe_pipe(&size_read,&size_write);
431         safe_pipe(&stdin_read,&stdin_write);
432         start_thread(stdin_loop,NULL);
433 }
434
435 }