Fixes PR93.
[cacao.git] / src / vm / signal.c
1 /* src/vm/signal.c - machine independent signal functions
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <signal.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32
33 #if defined(__DARWIN__)
34 /* If we compile with -ansi on darwin, <sys/types.h> is not
35  included. So let's do it here. */
36 # include <sys/types.h>
37 # include <sys/utsname.h>
38 #endif
39
40 #include "arch.h"
41
42 #if defined(ENABLE_GC_BOEHM)
43 # include "mm/memory.h"
44 #endif
45
46 #include "threads/thread.hpp"
47
48 #include "vm/exceptions.hpp"
49 #include "vm/signallocal.h"
50 #include "vm/vm.hpp"
51
52 #include "vmcore/method.h"
53 #include "vmcore/options.h"
54
55 #if defined(ENABLE_STATISTICS)
56 # include "vmcore/statistics.h"
57 #endif
58
59
60 /* function prototypes ********************************************************/
61
62 void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p);
63
64
65 /* signal_init *****************************************************************
66
67    Initializes the signal subsystem and installs the signal handler.
68
69 *******************************************************************************/
70
71 bool signal_init(void)
72 {
73 #if !defined(__CYGWIN__)
74         sigset_t mask;
75
76         TRACESUBSYSTEMINITIALIZATION("signal_init");
77
78 #if defined(__LINUX__) && defined(ENABLE_THREADS)
79         /* XXX Remove for exact-GC. */
80         if (threads_pthreads_implementation_nptl) {
81 #endif
82
83         /* Block the following signals (SIGINT for <ctrl>-c, SIGQUIT for
84            <ctrl>-\).  We enable them later in signal_thread, but only for
85            this thread. */
86
87         if (sigemptyset(&mask) != 0)
88                 vm_abort_errno("signal_init: sigemptyset failed");
89
90 #if !defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
91         /* Let OpenJDK handle SIGINT itself. */
92
93         if (sigaddset(&mask, SIGINT) != 0)
94                 vm_abort_errno("signal_init: sigaddset failed");
95 #endif
96
97 #if !defined(__FREEBSD__)
98         if (sigaddset(&mask, SIGQUIT) != 0)
99                 vm_abort_errno("signal_init: sigaddset failed");
100 #endif
101
102         if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
103                 vm_abort_errno("signal_init: sigprocmask failed");
104
105 #if defined(__LINUX__) && defined(ENABLE_THREADS)
106         /* XXX Remove for exact-GC. */
107         }
108 #endif
109
110 #if defined(ENABLE_GC_BOEHM)
111         /* Allocate something so the garbage collector's signal handlers
112            are installed. */
113
114         (void) GCNEW(int);
115 #endif
116
117         /* Install signal handlers for signals we want to catch in all
118            threads. */
119
120 #if defined(ENABLE_JIT)
121 # if defined(ENABLE_INTRP)
122         if (!opt_intrp) {
123 # endif
124                 /* SIGSEGV handler */
125
126                 signal_register_signal(SIGSEGV, (functionptr) md_signal_handler_sigsegv,
127                                                            SA_NODEFER | SA_SIGINFO);
128
129 #  if defined(SIGBUS)
130                 signal_register_signal(SIGBUS, (functionptr) md_signal_handler_sigsegv,
131                                                            SA_NODEFER | SA_SIGINFO);
132 #  endif
133
134 #  if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
135                 /* SIGFPE handler */
136
137                 signal_register_signal(SIGFPE, (functionptr) md_signal_handler_sigfpe,
138                                                            SA_NODEFER | SA_SIGINFO);
139 #  endif
140
141 #  if defined(__ARM__) || defined(__I386__) || defined(__S390__) || defined(__X86_64__) || defined(__M68K__)
142                 /* XXX use better defines for that (in arch.h) */
143                 /* SIGILL handler */
144
145                 signal_register_signal(SIGILL, (functionptr) md_signal_handler_sigill,
146                                                            SA_NODEFER | SA_SIGINFO);
147 #  endif
148
149 #  if defined(__POWERPC__)
150                 /* XXX use better defines for that (in arch.h) */
151                 /* SIGTRAP handler */
152
153                 signal_register_signal(SIGTRAP, (functionptr) md_signal_handler_sigtrap,
154                                                            SA_NODEFER | SA_SIGINFO);
155 #  endif
156 # if defined(ENABLE_INTRP)
157         }
158 # endif
159
160 #if defined(__DARWIN__)
161         do {
162                 struct utsname name;
163                 kern_return_t kr;
164
165                 /* Check if we're on 10.4 (Tiger/8.x) or earlier */
166                 if (uname(&name) != 0) 
167                         break;
168
169                 /* Make sure the string is large enough */
170                 /* Check the major number (ascii comparison) */
171                 /* Verify that we're not looking at '10.' by checking for a trailing period. */
172                 if (name.release[0] == '\0' || name.release[0] > '8' || name.release[1] != '.')
173                         break;
174
175                 /* Reset CrashReporter's task signal handler */
176                 kr = task_set_exception_ports(mach_task_self(),
177                                                                           EXC_MASK_BAD_ACCESS
178 #  if defined(__I386__)
179                                                                           | EXC_MASK_BAD_INSTRUCTION
180 #endif
181                                                                           , MACH_PORT_NULL,
182                                                                           EXCEPTION_STATE_IDENTITY,
183                                                                           MACHINE_THREAD_STATE);
184
185                 assert(kr == KERN_SUCCESS);
186         } while (false);
187 #endif
188 #endif /* !defined(ENABLE_JIT) */
189
190 #if defined(ENABLE_THREADS)
191         /* SIGHUP handler for threads_thread_interrupt */
192
193         signal_register_signal(Signal_THREAD_INTERRUPT, (functionptr) signal_handler_sighup, 0);
194 #endif
195
196 #if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
197         /* SIGUSR1 handler for the exact GC to suspend threads */
198
199         signal_register_signal(SIGUSR1, (functionptr) md_signal_handler_sigusr1,
200                                                    SA_SIGINFO);
201 #endif
202
203 #if defined(ENABLE_THREADS) && defined(ENABLE_PROFILING)
204         /* SIGUSR2 handler for profiling sampling */
205
206         signal_register_signal(SIGUSR2, (functionptr) md_signal_handler_sigusr2,
207                                                    SA_SIGINFO);
208 #endif
209
210 #endif /* !defined(__CYGWIN__) */
211
212         return true;
213 }
214
215
216 /* signal_register_signal ******************************************************
217
218    Register the specified handler with the specified signal.
219
220 *******************************************************************************/
221
222 void signal_register_signal(int signum, functionptr handler, int flags)
223 {
224         struct sigaction act;
225
226         void (*function)(int, siginfo_t *, void *);
227
228         function = (void (*)(int, siginfo_t *, void *)) handler;
229
230         if (sigemptyset(&act.sa_mask) != 0)
231                 vm_abort_errno("signal_register_signal: sigemptyset failed");
232
233         act.sa_sigaction = function;
234         act.sa_flags     = flags;
235
236         if (sigaction(signum, &act, NULL) != 0)
237                 vm_abort_errno("signal_register_signal: sigaction failed");
238 }
239
240
241 /* signal_thread ************************************************************
242
243    This thread sets the signal mask to catch the user input signals
244    (SIGINT, SIGQUIT).  We use such a thread, so we don't get the
245    signals on every single thread running.
246
247 *******************************************************************************/
248
249 static void signal_thread(void)
250 {
251         threadobject *t;
252         sigset_t      mask;
253         int           sig;
254
255         t = THREADOBJECT;
256
257         if (sigemptyset(&mask) != 0)
258                 vm_abort_errno("signal_thread: sigemptyset failed");
259
260 #if !defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
261         /* Let OpenJDK handle SIGINT itself. */
262
263         if (sigaddset(&mask, SIGINT) != 0)
264                 vm_abort_errno("signal_thread: sigaddset failed");
265 #endif
266
267 #if !defined(__FREEBSD__)
268         if (sigaddset(&mask, SIGQUIT) != 0)
269                 vm_abort_errno("signal_thread: sigaddset failed");
270 #endif
271
272         for (;;) {
273                 /* just wait for a signal */
274
275 #if defined(ENABLE_THREADS)
276                 thread_set_state_waiting(t);
277 #endif
278
279                 /* XXX We don't check for an error here, although the man-page
280                    states sigwait does not return an error (which is wrong!),
281                    but it seems to make problems with Boehm-GC.  We should
282                    revisit this code with our new exact-GC. */
283
284 /*              if (sigwait(&mask, &sig) != 0) */
285 /*                      vm_abort_errno("signal_thread: sigwait failed"); */
286                 (void) sigwait(&mask, &sig);
287
288 #if defined(ENABLE_THREADS)
289                 thread_set_state_runnable(t);
290 #endif
291
292                 /* Handle the signal. */
293
294                 signal_thread_handler(sig);
295         }
296 }
297
298
299 /* signal_thread_handler *******************************************************
300
301    Handles the signals caught in the signal handler thread.  Also used
302    from sun.misc.Signal with OpenJDK.
303
304 *******************************************************************************/
305
306 void signal_thread_handler(int sig)
307 {
308         switch (sig) {
309         case SIGINT:
310                 /* exit the vm properly */
311
312                 vm_exit(1);
313                 break;
314
315         case SIGQUIT:
316                 /* print a thread dump */
317 #if defined(ENABLE_THREADS)
318                 threads_dump();
319 #endif
320
321 #if defined(ENABLE_STATISTICS)
322                 if (opt_stat)
323                         statistics_print_memory_usage();
324 #endif
325                 break;
326
327 #if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
328         default: {
329                 // For OpenJDK we dispatch all unknown signals to Java.
330                 methodinfo* m = class_resolvemethod(class_sun_misc_Signal, utf_dispatch, utf_int__void);
331                 (void) vm_call_method(m, NULL, sig);
332
333                 if (exceptions_get_exception()) {
334                         exceptions_print_stacktrace();
335                         vm_abort("signal_thread_handler: Java signal handler throw an exception. Exiting...");
336                 }
337                 break;
338         }
339 #endif
340         }
341 }
342
343
344 /* signal_start_thread *********************************************************
345
346    Starts the signal handler thread.
347
348 *******************************************************************************/
349
350 bool signal_start_thread(void)
351 {
352 #if defined(ENABLE_THREADS)
353         utf *name;
354
355         name = utf_new_char("Signal Handler");
356
357         if (!threads_thread_start_internal(name, signal_thread))
358                 return false;
359
360         /* everything's ok */
361
362         return true;
363 #else
364 #warning FIX ME!
365 #endif
366 }
367
368
369 /* signal_handler_sighup *******************************************************
370
371    This handler is required by threads_thread_interrupt and does
372    nothing.
373
374 *******************************************************************************/
375
376 #if defined(ENABLE_THREADS)
377 void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
378 {
379         /* do nothing */
380 }
381 #endif
382
383
384 /*
385  * These are local overrides for various environment variables in Emacs.
386  * Please do not remove this and leave it at the end of the file, where
387  * Emacs will automagically detect them.
388  * ---------------------------------------------------------------------
389  * Local variables:
390  * mode: c
391  * indent-tabs-mode: t
392  * c-basic-offset: 4
393  * tab-width: 4
394  * End:
395  */