* src/vm/os.hpp: Indent changes.
[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/globals.hpp"
50 #include "vm/method.h"
51 #include "vm/options.h"
52 #include "vm/signallocal.h"
53 #include "vm/vm.hpp"
54
55 #if defined(ENABLE_STATISTICS)
56 # include "vm/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(__ALPHA__) || 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_INTERRUPT_SYSTEM_CALL, (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         int result;
255
256         t = THREADOBJECT;
257
258         if (sigemptyset(&mask) != 0)
259                 vm_abort_errno("signal_thread: sigemptyset failed");
260
261 #if !defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
262         /* Let OpenJDK handle SIGINT itself. */
263
264         if (sigaddset(&mask, SIGINT) != 0)
265                 vm_abort_errno("signal_thread: sigaddset failed");
266 #endif
267
268 #if !defined(__FREEBSD__)
269         if (sigaddset(&mask, SIGQUIT) != 0)
270                 vm_abort_errno("signal_thread: sigaddset failed");
271 #endif
272
273         for (;;) {
274                 /* just wait for a signal */
275
276 #if defined(ENABLE_THREADS)
277                 thread_set_state_waiting(t);
278 #endif
279
280                 // sigwait can return EINTR (unlike what the Linux man-page
281                 // says).
282                 do {
283                         result = sigwait(&mask, &sig);
284                 } while (result == EINTR);
285
286                 if (result != 0)
287                         vm_abort_errnum(result, "signal_thread: sigwait failed");
288
289 #if defined(ENABLE_THREADS)
290                 thread_set_state_runnable(t);
291 #endif
292
293                 /* Handle the signal. */
294
295                 signal_thread_handler(sig);
296         }
297 }
298
299
300 /* signal_thread_handler *******************************************************
301
302    Handles the signals caught in the signal handler thread.  Also used
303    from sun.misc.Signal with OpenJDK.
304
305 *******************************************************************************/
306
307 void signal_thread_handler(int sig)
308 {
309         switch (sig) {
310         case SIGINT:
311                 /* exit the vm properly */
312
313                 vm_exit(1);
314                 break;
315
316         case SIGQUIT:
317                 /* print a thread dump */
318 #if defined(ENABLE_THREADS)
319                 threads_dump();
320 #endif
321
322 #if defined(ENABLE_STATISTICS)
323                 if (opt_stat)
324                         statistics_print_memory_usage();
325 #endif
326                 break;
327
328 #if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
329         default: {
330                 // For OpenJDK we dispatch all unknown signals to Java.
331                 methodinfo* m = class_resolvemethod(class_sun_misc_Signal, utf_dispatch, utf_int__void);
332                 (void) vm_call_method(m, NULL, sig);
333
334                 if (exceptions_get_exception()) {
335                         log_println("signal_thread_handler: Java signal handler throw an exception while dispatching signal %d:", sig);
336                         exceptions_print_stacktrace();
337                         vm_abort("signal_thread_handler: Aborting...");
338                 }
339                 break;
340         }
341 #else
342         default:
343                 vm_abort("signal_thread_handler: Unknown signal %d", sig);
344 #endif
345         }
346 }
347
348
349 /* signal_start_thread *********************************************************
350
351    Starts the signal handler thread.
352
353 *******************************************************************************/
354
355 bool signal_start_thread(void)
356 {
357 #if defined(ENABLE_THREADS)
358         utf *name;
359
360         name = utf_new_char("Signal Handler");
361
362         if (!threads_thread_start_internal(name, signal_thread))
363                 return false;
364
365         /* everything's ok */
366
367         return true;
368 #else
369 #warning FIX ME!
370 #endif
371 }
372
373
374 /* signal_handler_sighup *******************************************************
375
376    This handler is required by threads_thread_interrupt and does
377    nothing.
378
379 *******************************************************************************/
380
381 #if defined(ENABLE_THREADS)
382 void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
383 {
384         /* do nothing */
385 }
386 #endif
387
388
389 /*
390  * These are local overrides for various environment variables in Emacs.
391  * Please do not remove this and leave it at the end of the file, where
392  * Emacs will automagically detect them.
393  * ---------------------------------------------------------------------
394  * Local variables:
395  * mode: c
396  * indent-tabs-mode: t
397  * c-basic-offset: 4
398  * tab-width: 4
399  * End:
400  */