* Removed all Id tags.
[cacao.git] / src / vm / signal.c
index e47555265cabd7e4a0551c771ffb9b3d5a791d1a..56531e728b0af7f5a09f90a8eb820e1a57ef409f 100644 (file)
@@ -1,6 +1,6 @@
 /* src/vm/signal.c - machine independent signal functions
 
-   Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+   Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
    J. Wenninger, Institut f. Computersprachen - TU Wien
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Contact: cacao@cacaojvm.org
-
-   Authors: Christian Thalinger
-
-   Changes:
-
-   $Id: signal.c 5540 2006-09-20 23:22:21Z michi $
-
 */
 
 
 #include "config.h"
 
+#include <assert.h>
+#include <errno.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdlib.h>
 
-#include "vm/types.h"
+#if defined(__DARWIN__)
+/* If we compile with -ansi on darwin, <sys/types.h> is not
+ included. So let's do it here. */
+# include <sys/types.h>
+#endif
+
+#include "arch.h"
+
+#include "mm/memory.h"
 
 #if defined(ENABLE_THREADS)
-# include "threads/native/threads.h"
+# include "threads/threads-common.h"
+#else
+# include "threads/none/threads.h"
 #endif
 
+#include "toolbox/logging.h"
+
+#include "vm/exceptions.h"
 #include "vm/signallocal.h"
-#include "vm/options.h"
 #include "vm/vm.h"
-#include "vm/jit/stacktrace.h"
+
+#include "vm/jit/codegen-common.h"
+#include "vm/jit/disass.h"
+#include "vm/jit/patcher-common.h"
+
+#include "vmcore/options.h"
+
+#if defined(ENABLE_STATISTICS)
+# include "vmcore/statistics.h"
+#endif
 
 
 /* function prototypes ********************************************************/
 
-void signal_handler_sigquit(int sig, siginfo_t *siginfo, void *_p);
-void signal_handler_sigint(int sig, siginfo_t *siginfo, void *_p);
-void signal_handler_sigusr1(int sig, siginfo_t *siginfo, void *_p);
+void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p);
 
 
 /* signal_init *****************************************************************
@@ -63,134 +77,342 @@ void signal_handler_sigusr1(int sig, siginfo_t *siginfo, void *_p);
 
 *******************************************************************************/
 
-void signal_init(void)
+bool signal_init(void)
 {
 #if !defined(__CYGWIN__)
-       struct sigaction act;
+       sigset_t mask;
+
+#if defined(__LINUX__) && defined(ENABLE_THREADS)
+       /* XXX Remove for exact-GC. */
+       if (threads_pthreads_implementation_nptl) {
+#endif
+
+       /* Block the following signals (SIGINT for <ctrl>-c, SIGQUIT for
+          <ctrl>-\).  We enable them later in signal_thread, but only for
+          this thread. */
+
+       if (sigemptyset(&mask) != 0)
+               vm_abort("signal_init: sigemptyset failed: %s", strerror(errno));
+
+#if !defined(WITH_CLASSPATH_SUN)
+       /* Let OpenJDK handle SIGINT itself. */
+
+       if (sigaddset(&mask, SIGINT) != 0)
+               vm_abort("signal_init: sigaddset failed: %s", strerror(errno));
+#endif
+
+#if !defined(__FREEBSD__)
+       if (sigaddset(&mask, SIGQUIT) != 0)
+               vm_abort("signal_init: sigaddset failed: %s", strerror(errno));
+#endif
 
-       /* install signal handlers we need to convert to exceptions */
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+               vm_abort("signal_init: sigprocmask failed: %s", strerror(errno));
 
-       sigemptyset(&act.sa_mask);
+#if defined(__LINUX__) && defined(ENABLE_THREADS)
+       /* XXX Remove for exact-GC. */
+       }
+#endif
+
+#if defined(ENABLE_GC_BOEHM)
+       /* Allocate something so the garbage collector's signal handlers
+          are installed. */
+
+       (void) GCNEW(int);
+#endif
 
+       /* Install signal handlers for signals we want to catch in all
+          threads. */
 
 #if defined(ENABLE_JIT)
 # if defined(ENABLE_INTRP)
        if (!opt_intrp) {
 # endif
-               /* catch NullPointerException/StackOverFlowException */
+               /* SIGSEGV handler */
 
-               if (!checknull) {
-                       act.sa_sigaction = md_signal_handler_sigsegv;
-                       act.sa_flags = SA_NODEFER | SA_SIGINFO;
+               signal_register_signal(SIGSEGV, (functionptr) md_signal_handler_sigsegv,
+                                                          SA_NODEFER | SA_SIGINFO);
 
-#if defined(SIGSEGV)
-                       sigaction(SIGSEGV, &act, NULL);
+#  if defined(SIGBUS)
+               signal_register_signal(SIGBUS, (functionptr) md_signal_handler_sigsegv,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
+
+#  if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+               /* SIGFPE handler */
+
+               signal_register_signal(SIGFPE, (functionptr) md_signal_handler_sigfpe,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
+
+#  if defined(__ARM__) || defined(__S390__)
+               /* XXX use better defines for that (in arch.h) */
+               /* SIGILL handler */
+
+               signal_register_signal(SIGILL, (functionptr) md_signal_handler_sigill,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
+
+#  if defined(__POWERPC__)
+               /* XXX use better defines for that (in arch.h) */
+               /* SIGTRAP handler */
+
+               signal_register_signal(SIGTRAP, (functionptr) md_signal_handler_sigtrap,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
+# if defined(ENABLE_INTRP)
+       }
+# endif
+#endif /* !defined(ENABLE_INTRP) */
+
+#if defined(ENABLE_THREADS)
+       /* SIGHUP handler for threads_thread_interrupt */
+
+       signal_register_signal(SIGHUP, (functionptr) signal_handler_sighup, 0);
 #endif
 
-#if defined(SIGBUS)
-                       sigaction(SIGBUS, &act, NULL);
+#if defined(ENABLE_THREADS) && defined(ENABLE_PROFILING)
+       /* SIGUSR2 handler for profiling sampling */
+
+       signal_register_signal(SIGUSR2, (functionptr) md_signal_handler_sigusr2,
+                                                  SA_SIGINFO);
 #endif
+
+#endif /* !defined(__CYGWIN__) */
+
+       return true;
+}
+
+
+/* signal_register_signal ******************************************************
+
+   Register the specified handler with the specified signal.
+
+*******************************************************************************/
+
+void signal_register_signal(int signum, functionptr handler, int flags)
+{
+       struct sigaction act;
+
+       void (*function)(int, siginfo_t *, void *);
+
+       function = (void (*)(int, siginfo_t *, void *)) handler;
+
+       if (sigemptyset(&act.sa_mask) != 0)
+               vm_abort("signal_register_signal: sigemptyset failed: %s",
+                                strerror(errno));
+
+       act.sa_sigaction = function;
+       act.sa_flags     = flags;
+
+       if (sigaction(signum, &act, NULL) != 0)
+               vm_abort("signal_register_signal: sigaction failed: %s",
+                                strerror(errno));
+}
+
+
+/* signal_handle ***************************************************************
+
+   Handles the signal caught by a signal handler and calls the correct
+   function.
+
+*******************************************************************************/
+
+void *signal_handle(void *xpc, int type, intptr_t val)
+{
+       void          *p;
+       int32_t        index;
+       java_object_t *o;
+
+       switch (type) {
+       case EXCEPTION_HARDWARE_NULLPOINTER:
+               p = exceptions_new_nullpointerexception();
+               break;
+
+       case EXCEPTION_HARDWARE_ARITHMETIC:
+               p = exceptions_new_arithmeticexception();
+               break;
+
+       case EXCEPTION_HARDWARE_ARRAYINDEXOUTOFBOUNDS:
+               index = (s4) val;
+               p = exceptions_new_arrayindexoutofboundsexception(index);
+               break;
+
+       case EXCEPTION_HARDWARE_CLASSCAST:
+               o = (java_object_t *) val;
+               p = exceptions_new_classcastexception(o);
+               break;
+
+       case EXCEPTION_HARDWARE_EXCEPTION:
+               p = exceptions_fillinstacktrace();
+               break;
+
+       case EXCEPTION_HARDWARE_PATCHER:
+#if defined(ENABLE_REPLACEMENT)
+               if (replace_me_wrapper(xpc)) {
+                       p = NULL;
+                       break;
                }
+#endif
+               p = patcher_handler(xpc);
+               break;
+
+       default:
+               /* Let's try to get a backtrace. */
 
+               codegen_get_pv_from_pc(xpc);
 
-               /* catch ArithmeticException */
+               /* If that does not work, print more debug info. */
 
-#if defined(__I386__) || defined(__X86_64__)
-               act.sa_sigaction = md_signal_handler_sigfpe;
-               act.sa_flags = SA_NODEFER | SA_SIGINFO;
-               sigaction(SIGFPE, &act, NULL);
+               log_println("exceptions_new_hardware_exception: unknown exception type %d", type);
+
+#if SIZEOF_VOID_P == 8
+               log_println("PC=0x%016lx", xpc);
+#else
+               log_println("PC=0x%08x", xpc);
 #endif
-# if defined(ENABLE_INTRP)
+
+#if defined(ENABLE_DISASSEMBLER)
+               log_println("machine instruction at PC:");
+               disassinstr(xpc);
+#endif
+
+               vm_abort("Exiting...");
+
+               /* keep compiler happy */
+
+               p = NULL;
        }
-# endif
-#endif /* !defined(ENABLE_INTRP) */
 
+       return p;
+}
 
-       /* catch SIGINT for exiting properly on <ctrl>-c */
 
-       act.sa_sigaction = signal_handler_sigint;
-       act.sa_flags = SA_NODEFER | SA_SIGINFO;
-       sigaction(SIGINT, &act, NULL);
+/* signal_thread ************************************************************
 
+   This thread sets the signal mask to catch the user input signals
+   (SIGINT, SIGQUIT).  We use such a thread, so we don't get the
+   signals on every single thread running.
 
-       /* catch SIGQUIT for thread dump */
+*******************************************************************************/
 
-#if defined(ENABLE_THREADS)
-#if !defined(__FREEBSD__)
-       act.sa_sigaction = signal_handler_sigquit;
-       act.sa_flags = SA_SIGINFO;
-       sigaction(SIGQUIT, &act, NULL);
+static void signal_thread(void)
+{
+       threadobject *t;
+       sigset_t      mask;
+       int           sig;
 
-       /* XXX boehm uses SIGUSR1 for suspend on freebsd */
+       t = THREADOBJECT;
 
-       act.sa_sigaction = signal_handler_sigusr1;
-       act.sa_flags = SA_SIGINFO;
-       sigaction(SIGUSR1, &act, NULL);
+       if (sigemptyset(&mask) != 0)
+               vm_abort("signal_thread: sigemptyset failed: %s", strerror(errno));
+
+#if !defined(WITH_CLASSPATH_SUN)
+       /* Let OpenJDK handle SIGINT itself. */
+
+       if (sigaddset(&mask, SIGINT) != 0)
+               vm_abort("signal_thread: sigaddset failed: %s", strerror(errno));
 #endif
+
+#if !defined(__FREEBSD__)
+       if (sigaddset(&mask, SIGQUIT) != 0)
+               vm_abort("signal_thread: sigaddset failed: %s", strerror(errno));
 #endif
 
-#if defined(ENABLE_THREADS) && defined(ENABLE_PROFILING)
-       /* install signal handler for profiling sampling */
+       for (;;) {
+               /* just wait for a signal */
 
-       act.sa_sigaction = md_signal_handler_sigusr2;
-       act.sa_flags = SA_SIGINFO;
-       sigaction(SIGUSR2, &act, NULL);
+#if defined(ENABLE_THREADS)
+               threads_thread_state_waiting(t);
 #endif
 
-#endif /* !defined(__CYGWIN__) */
+               /* XXX We don't check for an error here, although the man-page
+                  states sigwait does not return an error (which is wrong!),
+                  but it seems to make problems with Boehm-GC.  We should
+                  revisit this code with our new exact-GC. */
+
+/*             if (sigwait(&mask, &sig) != 0) */
+/*                     vm_abort("signal_thread: sigwait failed: %s", strerror(errno)); */
+               (void) sigwait(&mask, &sig);
+
+#if defined(ENABLE_THREADS)
+               threads_thread_state_runnable(t);
+#endif
+
+               /* Handle the signal. */
+
+               signal_thread_handler(sig);
+       }
 }
 
 
-/* signal_handler_sigquit ******************************************************
+/* signal_thread_handler *******************************************************
 
-   XXX
+   Handles the signals caught in the signal handler thread.  Also used
+   from sun.misc.Signal with OpenJDK.
 
 *******************************************************************************/
 
-#if defined(ENABLE_THREADS)
-void signal_handler_sigquit(int sig, siginfo_t *siginfo, void *_p)
+void signal_thread_handler(int sig)
 {
-       /* do thread dump */
+       switch (sig) {
+       case SIGINT:
+               /* exit the vm properly */
 
-       threads_dump();
-}
+               vm_exit(0);
+               break;
+
+       case SIGQUIT:
+               /* print a thread dump */
+#if defined(ENABLE_THREADS)
+               threads_dump();
 #endif
 
+#if defined(ENABLE_STATISTICS)
+               if (opt_stat)
+                       statistics_print_memory_usage();
+#endif
+               break;
+       }
+}
 
-/* signal_handler_sigint *******************************************************
 
-   Handler for SIGINT (<ctrl>-c) which shuts down CACAO properly with
-   Runtime.exit(I)V.
+/* signal_start_thread *********************************************************
+
+   Starts the signal handler thread.
 
 *******************************************************************************/
 
-void signal_handler_sigint(int sig, siginfo_t *siginfo, void *_p)
+bool signal_start_thread(void)
 {
-       /* if we are already in Runtime.exit(), just do it hardcore */
+#if defined(ENABLE_THREADS)
+       utf *name;
 
-       if (vm_exiting) {
-               fprintf(stderr, "Caught SIGINT while already shutting down. Shutdown aborted...\n");
-               exit(0);
-       }
+       name = utf_new_char("Signal Handler");
 
-       /* exit the vm properly */
+       if (!threads_thread_start_internal(name, signal_thread))
+               return false;
 
-       vm_exit(0);
+       /* everything's ok */
+
+       return true;
+#else
+#warning FIX ME!
+#endif
 }
 
 
-/* signal_handler_sigusr1 ******************************************************
+/* signal_handler_sighup *******************************************************
 
-   XXX
+   This handler is required by threads_thread_interrupt and does
+   nothing.
 
 *******************************************************************************/
 
 #if defined(ENABLE_THREADS)
-void signal_handler_sigusr1(int sig, siginfo_t *siginfo, void *_p)
+void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
 {
-       /* call stacktrace function */
-
-       stacktrace_dump_trace();
+       /* do nothing */
 }
 #endif