* src/vm/jit/stacktrace.h (stackframeinfo): Renamed to
[cacao.git] / src / vm / signal.c
index 493e43e2b51663b6a8c66b6489b1afac5b21eaf3..090b7ef169c0bfe077a47754804c6778c38c370c 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
-
-   $Id: signal.c 6219 2006-12-19 19:20:37Z twisti $
-
 */
 
 
 #include "config.h"
 
+#include <assert.h>
 #include <errno.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <sys/mman.h>
 
 #if defined(__DARWIN__)
 /* If we compile with -ansi on darwin, <sys/types.h> is not
 # include <sys/types.h>
 #endif
 
-#include "vm/types.h"
+#include "arch.h"
+
+#include "mm/memory.h"
+
+#include "native/llni.h"
 
 #if defined(ENABLE_THREADS)
-# include "threads/native/threads.h"
+# include "threads/threads-common.h"
+#else
+# include "threads/none/threads.h"
 #endif
 
-#include "mm/memory.h"
+#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_sighup(int sig, siginfo_t *siginfo, void *_p);
-void signal_handler_sigint(int sig, siginfo_t *siginfo, void *_p);
-void signal_handler_sigquit(int sig, siginfo_t *siginfo, void *_p);
 
 
 /* signal_init *****************************************************************
@@ -71,148 +79,398 @@ void signal_handler_sigquit(int sig, siginfo_t *siginfo, void *_p);
 
 *******************************************************************************/
 
-void signal_init(void)
+bool signal_init(void)
 {
 #if !defined(__CYGWIN__)
-       void            *p;
-       int              pagesize;
-       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));
 
-       /* mmap a memory page at address 0x0, so our hardware-exceptions
-          work. */
+#if !defined(WITH_CLASSPATH_SUN)
+       /* Let OpenJDK handle SIGINT itself. */
 
-       pagesize = getpagesize();
+       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
 
-       (void) memory_mmap_anon(NULL, pagesize, PROT_NONE, MAP_PRIVATE | MAP_FIXED);
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+               vm_abort("signal_init: sigprocmask failed: %s", strerror(errno));
+
+#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(u1);
+       (void) GCNEW(int);
 #endif
 
-       /* install signal handlers we need to convert to exceptions */
-
-       sigemptyset(&act.sa_mask);
+       /* 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 */
 
-               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);
-#endif
+#  if defined(SIGBUS)
+               signal_register_signal(SIGBUS, (functionptr) md_signal_handler_sigsegv,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
 
-#if defined(SIGBUS)
-               sigaction(SIGBUS, &act, NULL);
-#endif
+#  if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+               /* SIGFPE handler */
 
-               /* catch ArithmeticException */
+               signal_register_signal(SIGFPE, (functionptr) md_signal_handler_sigfpe,
+                                                          SA_NODEFER | SA_SIGINFO);
+#  endif
 
-#if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
-               act.sa_sigaction = md_signal_handler_sigfpe;
-               act.sa_flags     = SA_NODEFER | SA_SIGINFO;
-               sigaction(SIGFPE, &act, NULL);
-#endif
+#  if defined(__ARM__) || defined(__I386__) || defined(__S390__) || defined(__X86_64__)
+               /* 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)
-       /* catch SIGHUP for threads_thread_interrupt */
+       /* SIGHUP handler for threads_thread_interrupt */
 
-       act.sa_sigaction = signal_handler_sighup;
-       act.sa_flags     = 0;
-       sigaction(SIGHUP, &act, NULL);
+       signal_register_signal(SIGHUP, (functionptr) signal_handler_sighup, 0);
 #endif
 
-       /* catch SIGINT for exiting properly on <ctrl>-c */
+#if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
+       /* SIGUSR1 handler for the exact GC to suspend threads */
 
-       act.sa_sigaction = signal_handler_sigint;
-       act.sa_flags     = SA_NODEFER | SA_SIGINFO;
-       sigaction(SIGINT, &act, NULL);
-
-#if defined(ENABLE_THREADS)
-       /* catch SIGQUIT for thread dump */
-
-# if !defined(__FREEBSD__)
-       act.sa_sigaction = signal_handler_sigquit;
-       act.sa_flags     = SA_SIGINFO;
-       sigaction(SIGQUIT, &act, NULL);
-# endif
+       signal_register_signal(SIGUSR1, (functionptr) md_signal_handler_sigusr1,
+                                                  SA_SIGINFO);
 #endif
 
 #if defined(ENABLE_THREADS) && defined(ENABLE_PROFILING)
-       /* install signal handler for profiling sampling */
+       /* SIGUSR2 handler for profiling sampling */
 
-       act.sa_sigaction = md_signal_handler_sigusr2;
-       act.sa_flags     = SA_SIGINFO;
-       sigaction(SIGUSR2, &act, NULL);
+       signal_register_signal(SIGUSR2, (functionptr) md_signal_handler_sigusr2,
+                                                  SA_SIGINFO);
 #endif
 
 #endif /* !defined(__CYGWIN__) */
+
+       return true;
 }
 
 
-/* signal_handler_sighup *******************************************************
+/* signal_register_signal ******************************************************
 
-   This handler is required by threads_thread_interrupt and does
-   nothing.
+   Register the specified handler with the specified signal.
 
 *******************************************************************************/
 
-#if defined(ENABLE_THREADS)
-void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
+void signal_register_signal(int signum, functionptr handler, int flags)
 {
-       /* do nothing */
+       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));
 }
-#endif
 
 
-/* signal_handler_sigquit ******************************************************
+/* signal_handle ***************************************************************
 
-   Handle for SIGQUIT (<ctrl>-\) which print a stacktrace for every
-   running thread.
+   Handles the signal caught by a signal handler and calls the correct
+   function.
 
 *******************************************************************************/
 
-#if defined(ENABLE_THREADS)
-void signal_handler_sigquit(int sig, siginfo_t *siginfo, void *_p)
+void *signal_handle(int type, intptr_t val,
+                                       void *pv, void *sp, void *ra, void *xpc, void *context)
 {
-       /* do thread dump */
+       stackframeinfo_t  sfi;
+       int32_t           index;
+       java_handle_t    *o;
+       methodinfo       *m;
+       java_handle_t    *p;
+
+       /* wrap the value into a handle if it is a reference */
+       /* BEFORE: creating stackframeinfo */
+
+       switch (type) {
+       case EXCEPTION_HARDWARE_CLASSCAST:
+               o = LLNI_WRAP((java_object_t *) val);
+               break;
+
+       case EXCEPTION_HARDWARE_COMPILER:
+               /* In this case the passed PV points to the compiler stub.  We
+                  get the methodinfo pointer here and set PV to NULL so
+                  stacktrace_stackframeinfo_add determines the PV for the
+                  parent Java method. */
+
+               m  = code_get_methodinfo_for_pv(pv);
+               pv = NULL;
+               break;
+
+       default:
+               /* do nothing */
+               break;
+       }
+
+       /* Fill and add a stackframeinfo. */
+
+       stacktrace_stackframeinfo_add(&sfi, pv, sp, ra, xpc);
+
+       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_ARRAYSTORE:
+               p = exceptions_new_arraystoreexception();
+               break;
+
+       case EXCEPTION_HARDWARE_CLASSCAST:
+               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, context)) {
+                       p = NULL;
+                       break;
+               }
+#endif
+               p = patcher_handler(xpc);
+               break;
+
+       case EXCEPTION_HARDWARE_COMPILER:
+               p = jit_compile_handle(m, sfi.pv, ra, (void *) val);
+               break;
+
+       default:
+               /* Let's try to get a backtrace. */
+
+               codegen_get_pv_from_pc(xpc);
+
+               /* If that does not work, print more debug info. */
+
+               log_println("signal_handle: unknown hardware exception type %d", type);
+
+#if SIZEOF_VOID_P == 8
+               log_println("PC=0x%016lx", xpc);
+#else
+               log_println("PC=0x%08x", xpc);
+#endif
 
-       threads_dump();
+#if defined(ENABLE_DISASSEMBLER)
+               log_println("machine instruction at PC:");
+               disassinstr(xpc);
+#endif
+
+               vm_abort("Exiting...");
+
+               /* keep compiler happy */
+
+               p = NULL;
+       }
+
+       /* Remove stackframeinfo. */
+
+       stacktrace_stackframeinfo_remove(&sfi);
+
+       /* unwrap and return the exception object */
+       /* AFTER: removing stackframeinfo */
+
+       if (type == EXCEPTION_HARDWARE_COMPILER)
+               return p;
+       else
+               return LLNI_UNWRAP(p);
 }
+
+
+/* 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.
+
+*******************************************************************************/
+
+static void signal_thread(void)
+{
+       threadobject *t;
+       sigset_t      mask;
+       int           sig;
+
+       t = THREADOBJECT;
+
+       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
+
+       for (;;) {
+               /* just wait for a signal */
+
+#if defined(ENABLE_THREADS)
+               threads_thread_state_waiting(t);
 #endif
 
+               /* 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_sigint *******************************************************
 
-   Handler for SIGINT (<ctrl>-c) which shuts down CACAO properly with
-   Runtime.exit(I)V.
+/* signal_thread_handler *******************************************************
+
+   Handles the signals caught in the signal handler thread.  Also used
+   from sun.misc.Signal with OpenJDK.
 
 *******************************************************************************/
 
-void signal_handler_sigint(int sig, siginfo_t *siginfo, void *_p)
+void signal_thread_handler(int sig)
 {
-       /* if we are already in Runtime.exit(), just do it hardcore */
+       switch (sig) {
+       case SIGINT:
+               /* exit the vm properly */
+
+               vm_exit(0);
+               break;
+
+       case SIGQUIT:
+               /* print a thread dump */
+#if defined(ENABLE_THREADS)
+               threads_dump();
+#endif
 
-       if (vm_exiting) {
-               fprintf(stderr, "Caught SIGINT while already shutting down. Shutdown aborted...\n");
-               exit(0);
+#if defined(ENABLE_STATISTICS)
+               if (opt_stat)
+                       statistics_print_memory_usage();
+#endif
+               break;
        }
+}
+
+
+/* signal_start_thread *********************************************************
+
+   Starts the signal handler thread.
+
+*******************************************************************************/
+
+bool signal_start_thread(void)
+{
+#if defined(ENABLE_THREADS)
+       utf *name;
+
+       name = utf_new_char("Signal Handler");
+
+       if (!threads_thread_start_internal(name, signal_thread))
+               return false;
 
-       /* exit the vm properly */
+       /* everything's ok */
 
-       vm_exit(0);
+       return true;
+#else
+#warning FIX ME!
+#endif
+}
+
+
+/* signal_handler_sighup *******************************************************
+
+   This handler is required by threads_thread_interrupt and does
+   nothing.
+
+*******************************************************************************/
+
+#if defined(ENABLE_THREADS)
+void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
+{
+       /* do nothing */
 }
+#endif
 
 
 /*