void __cdecl crt_sig_handler(int sig, int num) { jl_ptls_t ptls = jl_get_ptls_states(); CONTEXT Context; switch (sig) { case SIGFPE: fpreset(); signal(SIGFPE, (void (__cdecl *)(int))crt_sig_handler); switch(num) { case _FPE_INVALID: case _FPE_OVERFLOW: case _FPE_UNDERFLOW: default: jl_errorf("Unexpected FPE Error 0x%X", num); break; case _FPE_ZERODIVIDE: jl_throw(jl_diverror_exception); break; } break; case SIGINT: signal(SIGINT, (void (__cdecl *)(int))crt_sig_handler); if (exit_on_sigint) jl_exit(130); // 128 + SIGINT jl_try_throw_sigint(); break; default: // SIGSEGV, (SSIGTERM, IGILL) memset(&Context, 0, sizeof(Context)); RtlCaptureContext(&Context); jl_critical_error(sig, &Context, ptls->bt_data, &ptls->bt_size); raise(sig); } }
//exc_server uses dlsym to find symbol DLLEXPORT kern_return_t catch_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_exception_state64_t exc_state; x86_thread_state64_t state; #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port, thread, task, exception, code, code_count); } #endif kern_return_t ret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&exc_state, &exc_count); HANDLE_MACH_ERROR("thread_get_state", ret); uint64_t fault_addr = exc_state.__faultvaddr; #ifdef SEGV_EXCEPTION if (1) { #else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) == 0) { // check if this was a valid address #endif jl_value_t *excpt; if (is_addr_on_stack((void*)fault_addr)) { excpt = jl_stackovf_exception; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address excpt = jl_segv_exception; } #endif else { if (!(exc_state.__err & WRITE_FAULT)) return KERN_INVALID_ARGUMENT; // rethrow the SEGV since it wasn't an error with writing to read-only memory excpt = jl_readonlymemory_exception; } jl_throw_in_thread(0, thread, excpt); return KERN_SUCCESS; } else { kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); jl_critical_error(SIGSEGV, (unw_context_t*)&state, jl_bt_data, &jl_bt_size); return KERN_INVALID_ARGUMENT; } } void attach_exception_port() { kern_return_t ret; // http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/osfmk/man/thread_set_exception_ports.html ret = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, segv_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("thread_set_exception_ports", ret); }
void sigdie_handler(int sig, siginfo_t *info, void *context) { sigset_t sset; uv_tty_reset_mode(); sigfillset(&sset); sigprocmask(SIG_UNBLOCK, &sset, NULL); signal(sig, SIG_DFL); #ifdef __APPLE__ jl_critical_error(sig, (bt_context_t)&((ucontext64_t*)context)->uc_mcontext64->__ss, jl_bt_data, &jl_bt_size); #else jl_critical_error(sig, (ucontext_t*)context, jl_bt_data, &jl_bt_size); #endif if (sig != SIGSEGV && sig != SIGBUS && sig != SIGILL) { raise(sig); } // fall-through return to re-execute faulting statement (but without the error handler) }
void sigdie_handler(int sig, siginfo_t *info, void *context) { sigset_t sset; uv_tty_reset_mode(); jl_critical_error(sig, jl_to_bt_context(context), jl_bt_data, &jl_bt_size); sigfillset(&sset); sigprocmask(SIG_UNBLOCK, &sset, NULL); signal(sig, SIG_DFL); if (sig != SIGSEGV && sig != SIGBUS && sig != SIGILL) { raise(sig); } // fall-through return to re-execute faulting statement (but without the error handler) }
void __cdecl crt_sig_handler(int sig, int num) { CONTEXT Context; switch (sig) { case SIGFPE: fpreset(); signal(SIGFPE, (void (__cdecl *)(int))crt_sig_handler); switch(num) { case _FPE_INVALID: case _FPE_OVERFLOW: case _FPE_UNDERFLOW: default: jl_errorf("Unexpected FPE Error 0x%X", num); break; case _FPE_ZERODIVIDE: jl_throw(jl_diverror_exception); break; } break; case SIGINT: signal(SIGINT, (void (__cdecl *)(int))crt_sig_handler); if (jl_defer_signal) { jl_signal_pending = sig; } else { jl_signal_pending = 0; jl_sigint_action(); } break; default: // SIGSEGV, (SSIGTERM, IGILL) memset(&Context, 0, sizeof(Context)); RtlCaptureContext(&Context); jl_critical_error(sig, &Context, jl_bt_data, &jl_bt_size); raise(sig); } }
static LONG WINAPI _exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo, int in_ctx) { jl_ptls_t ptls = jl_get_ptls_states(); if (ExceptionInfo->ExceptionRecord->ExceptionFlags == 0) { switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_INT_DIVIDE_BY_ZERO: fpreset(); jl_throw_in_ctx(jl_diverror_exception, ExceptionInfo->ContextRecord,in_ctx); return EXCEPTION_CONTINUE_EXECUTION; case EXCEPTION_STACK_OVERFLOW: jl_throw_in_ctx(jl_stackovf_exception, ExceptionInfo->ContextRecord,in_ctx&&pSetThreadStackGuarantee); return EXCEPTION_CONTINUE_EXECUTION; case EXCEPTION_ACCESS_VIOLATION: if (jl_addr_is_safepoint(ExceptionInfo->ExceptionRecord->ExceptionInformation[1])) { #ifdef JULIA_ENABLE_THREADING jl_set_gc_and_wait(); // Do not raise sigint on worker thread if (ptls->tid != 0) return EXCEPTION_CONTINUE_EXECUTION; #endif if (ptls->defer_signal) { jl_safepoint_defer_sigint(); } else if (jl_safepoint_consume_sigint()) { jl_clear_force_sigint(); jl_throw_in_ctx(jl_interrupt_exception, ExceptionInfo->ContextRecord, in_ctx); } return EXCEPTION_CONTINUE_EXECUTION; } if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) { // writing to read-only memory (e.g. mmap) jl_throw_in_ctx(jl_readonlymemory_exception, ExceptionInfo->ContextRecord,in_ctx); return EXCEPTION_CONTINUE_EXECUTION; } } jl_safe_printf("\nPlease submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.\nException: "); switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: jl_safe_printf("EXCEPTION_ACCESS_VIOLATION"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: jl_safe_printf("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); break; case EXCEPTION_BREAKPOINT: jl_safe_printf("EXCEPTION_BREAKPOINT"); break; case EXCEPTION_DATATYPE_MISALIGNMENT: jl_safe_printf("EXCEPTION_DATATYPE_MISALIGNMENT"); break; case EXCEPTION_FLT_DENORMAL_OPERAND: jl_safe_printf("EXCEPTION_FLT_DENORMAL_OPERAND"); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: jl_safe_printf("EXCEPTION_FLT_DIVIDE_BY_ZERO"); break; case EXCEPTION_FLT_INEXACT_RESULT: jl_safe_printf("EXCEPTION_FLT_INEXACT_RESULT"); break; case EXCEPTION_FLT_INVALID_OPERATION: jl_safe_printf("EXCEPTION_FLT_INVALID_OPERATION"); break; case EXCEPTION_FLT_OVERFLOW: jl_safe_printf("EXCEPTION_FLT_OVERFLOW"); break; case EXCEPTION_FLT_STACK_CHECK: jl_safe_printf("EXCEPTION_FLT_STACK_CHECK"); break; case EXCEPTION_FLT_UNDERFLOW: jl_safe_printf("EXCEPTION_FLT_UNDERFLOW"); break; case EXCEPTION_ILLEGAL_INSTRUCTION: jl_safe_printf("EXCEPTION_ILLEGAL_INSTRUCTION"); break; case EXCEPTION_IN_PAGE_ERROR: jl_safe_printf("EXCEPTION_IN_PAGE_ERROR"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: jl_safe_printf("EXCEPTION_INT_DIVIDE_BY_ZERO"); break; case EXCEPTION_INT_OVERFLOW: jl_safe_printf("EXCEPTION_INT_OVERFLOW"); break; case EXCEPTION_INVALID_DISPOSITION: jl_safe_printf("EXCEPTION_INVALID_DISPOSITION"); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: jl_safe_printf("EXCEPTION_NONCONTINUABLE_EXCEPTION"); break; case EXCEPTION_PRIV_INSTRUCTION: jl_safe_printf("EXCEPTION_PRIV_INSTRUCTION"); break; case EXCEPTION_SINGLE_STEP: jl_safe_printf("EXCEPTION_SINGLE_STEP"); break; case EXCEPTION_STACK_OVERFLOW: jl_safe_printf("EXCEPTION_STACK_OVERFLOW"); break; default: jl_safe_printf("UNKNOWN"); break; } jl_safe_printf(" at 0x%Ix -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_gdblookup((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_critical_error(0, ExceptionInfo->ContextRecord, ptls->bt_data, &ptls->bt_size); static int recursion = 0; if (recursion++) exit(1); else jl_exit(1); } return EXCEPTION_CONTINUE_SEARCH; }
static void *signal_listener(void *arg) { static uintptr_t bt_data[JL_MAX_BT_SIZE + 1]; static size_t bt_size = 0; sigset_t sset; unw_context_t *signal_context; int sig, critical, profile; int i; jl_sigsetset(&sset); while (1) { profile = 0; sigwait(&sset, &sig); #ifndef HAVE_MACH # ifdef HAVE_ITIMER profile = (sig == SIGPROF); # else profile = (sig == SIGUSR1); # endif #endif if (sig == SIGINT) { if (exit_on_sigint) { critical = 1; } else { jl_try_deliver_sigint(); continue; } } else { critical = 0; } critical |= (sig == SIGTERM); critical |= (sig == SIGABRT); critical |= (sig == SIGQUIT); #ifdef SIGINFO critical |= (sig == SIGINFO); #else critical |= (sig == SIGUSR1 && !profile); #endif bt_size = 0; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) for (i = jl_n_threads; i-- > 0; ) { // notify thread to stop jl_thread_suspend_and_get_state(i, &signal_context); // do backtrace on thread contexts for critical signals // this part must be signal-handler safe if (critical) { bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / jl_n_threads - 1, signal_context); bt_data[bt_size++] = 0; } // do backtrace for profiler if (profile && running) { if (bt_size_cur < bt_size_max - 1) { // Get backtrace data bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, signal_context); // Mark the end of this block with 0 bt_data_prof[bt_size_cur++] = 0; } if (bt_size_cur >= bt_size_max - 1) { // Buffer full: Delete the timer jl_profile_stop_timer(); } } // notify thread to resume jl_thread_resume(i, sig); } // this part is async with the running of the rest of the program // and must be thread-safe, but not necessarily signal-handler safe if (critical) { jl_critical_error(sig, NULL, bt_data, &bt_size); // FIXME // It is unsafe to run the exit handler on this thread // (this thread is not managed and has a rather limited stack space) // try harder to run this on a managed thread. #ifdef SIGINFO if (sig != SIGINFO) #else if (sig != SIGUSR1) #endif jl_exit(128 + sig); } } }
static void *signal_listener(void *arg) { static intptr_t bt_data[JL_MAX_BT_SIZE + 1]; static size_t bt_size = 0; sigset_t sset; unw_context_t *signal_context; int sig, critical, profile; int i; jl_sigsetset(&sset); #ifdef HAVE_SIGTIMEDWAIT siginfo_t info; sigaddset(&sset, SIGUSR2); sigprocmask(SIG_SETMASK, &sset, 0); #endif while (1) { profile = 0; #ifdef HAVE_SIGTIMEDWAIT if (running) { sig = sigtimedwait(&sset, &info, &timeoutprof); } else { sig = sigwaitinfo(&sset, &info); } if (sig == -1) { int err = errno; if (err == EAGAIN) { // this was a timeout event profile = 1; } else { assert(err == EINTR); continue; } } else if (sig == SIGUSR2) { // notification to toggle profiler running = !running; continue; } #else sigwait(&sset, &sig); #ifndef HAVE_MACH #ifdef HAVE_ITIMER profile = (sig == SIGPROF); #else profile = (sig == SIGUSR1); #endif #endif #endif critical = (sig == SIGINT && exit_on_sigint); critical |= (sig == SIGTERM); critical |= (sig == SIGABRT); critical |= (sig == SIGQUIT); #ifdef SIGINFO critical |= (sig == SIGINFO); #else critical |= (sig == SIGUSR1 && !profile); #endif bt_size = 0; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) for (i = jl_n_threads; i-- > 0; ) { // notify thread to stop jl_thread_suspend_and_get_state(i, &signal_context, sig); // do backtrace on thread contexts for critical signals // this part must be signal-handler safe if (critical) { bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / jl_n_threads - 1, signal_context); bt_data[bt_size++] = 0; } // do backtrace for profiler if (profile && running) { if (bt_size_cur < bt_size_max - 1) { // Get backtrace data bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, signal_context); // Mark the end of this block with 0 bt_data_prof[bt_size_cur++] = 0; } if (bt_size_cur >= bt_size_max - 1) { // Buffer full: Delete the timer jl_profile_stop_timer(); } } // notify thread to resume jl_thread_resume(i, sig); } // this part is async with the running of the rest of the program // and must be thread-safe, but not necessarily signal-handler safe if (critical) { jl_critical_error(sig, NULL, bt_data, &bt_size); #ifdef SIGINFO if (sig != SIGINFO) #else if (sig != SIGUSR1) #endif jl_exit(128 + sig); } } }
//exc_server uses dlsym to find symbol JL_DLLEXPORT kern_return_t catch_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { unsigned int count = MACHINE_THREAD_STATE_COUNT; unsigned int exc_count = X86_EXCEPTION_STATE64_COUNT; x86_exception_state64_t exc_state; x86_thread_state64_t state; #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port, thread, task, exception, code, code_count); } #endif int16_t tid; #ifdef JULIA_ENABLE_THREADING jl_tls_states_t *ptls = NULL; for (tid = 0;tid < jl_n_threads;tid++) { if (pthread_mach_thread_np(jl_all_task_states[tid].system_id) == thread) { ptls = jl_all_task_states[tid].ptls; break; } } if (!ptls) { // We don't know about this thread, let the kernel try another handler // instead. This shouldn't actually happen since we only register the // handler for the threads we know about. jl_safe_printf("ERROR: Exception handler triggered on unmanaged thread.\n"); return KERN_INVALID_ARGUMENT; } #else jl_tls_states_t *ptls = &jl_tls_states; tid = 0; #endif kern_return_t ret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&exc_state, &exc_count); HANDLE_MACH_ERROR("thread_get_state", ret); uint64_t fault_addr = exc_state.__faultvaddr; #ifdef JULIA_ENABLE_THREADING if (fault_addr == (uintptr_t)jl_gc_signal_page) { JL_LOCK_NOGC(gc_suspend); if (!jl_gc_safepoint_activated) { // GC is done before we get the message, do nothing and return JL_UNLOCK_NOGC(gc_suspend); return KERN_SUCCESS; } // Otherwise, set the gc state of the thread, suspend and record it int8_t gc_state = ptls->gc_state; ptls->gc_state = JL_GC_STATE_WAITING; uintptr_t item = tid | (((uintptr_t)gc_state) << 16); arraylist_push(&suspended_threads, (void*)item); thread_suspend(thread); JL_UNLOCK_NOGC(gc_suspend); return KERN_SUCCESS; } #endif #ifdef SEGV_EXCEPTION if (1) { #else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) == 0) { // check if this was a valid address #endif jl_value_t *excpt; if (is_addr_on_stack(ptls, (void*)fault_addr)) { excpt = jl_stackovf_exception; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address excpt = jl_segv_exception; } #endif else { if (!(exc_state.__err & WRITE_FAULT)) return KERN_INVALID_ARGUMENT; // rethrow the SEGV since it wasn't an error with writing to read-only memory excpt = jl_readonlymemory_exception; } jl_throw_in_thread(tid, thread, excpt); return KERN_SUCCESS; } else { kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); jl_critical_error(SIGSEGV, (unw_context_t*)&state, ptls->bt_data, &ptls->bt_size); return KERN_INVALID_ARGUMENT; } } static void attach_exception_port(thread_port_t thread) { kern_return_t ret; // http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/osfmk/man/thread_set_exception_ports.html ret = thread_set_exception_ports(thread, EXC_MASK_BAD_ACCESS, segv_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("thread_set_exception_ports", ret); }
static void *signal_listener(void *arg) { static uintptr_t bt_data[JL_MAX_BT_SIZE + 1]; static size_t bt_size = 0; sigset_t sset; int sig, critical, profile; jl_sigsetset(&sset); #ifdef HAVE_KEVENT struct kevent ev; int sigqueue = kqueue(); if (sigqueue == -1) { perror("signal kqueue"); } else { kqueue_signal(&sigqueue, &ev, SIGINT); kqueue_signal(&sigqueue, &ev, SIGTERM); kqueue_signal(&sigqueue, &ev, SIGABRT); kqueue_signal(&sigqueue, &ev, SIGQUIT); #ifdef SIGINFO kqueue_signal(&sigqueue, &ev, SIGINFO); #else kqueue_signal(&sigqueue, &ev, SIGUSR1); #endif #ifdef HAVE_ITIMER kqueue_signal(&sigqueue, &ev, SIGPROF); #endif } #endif while (1) { profile = 0; sig = 0; errno = 0; #ifdef HAVE_KEVENT if (sigqueue != -1) { int nevents = kevent(sigqueue, NULL, 0, &ev, 1, NULL); if (nevents == -1) { if (errno == EINTR) continue; perror("signal kevent"); } if (nevents != 1) { close(sigqueue); sigqueue = -1; continue; } sig = ev.ident; } else #endif if (sigwait(&sset, &sig)) { sig = SIGABRT; // this branch can't occur, unless we had stack memory corruption of sset } else if (!sig || errno == EINTR) { // This should never happen, but it has been observed to occur // when this thread gets used to handle run a signal handler (without SA_RESTART). // It would be nice to prohibit the kernel from doing that, by blocking signals on this thread, // (so that we aren't temporarily unable to handle the signals that this thread exists to handle) // but that sometimes results in the signals never getting delivered at all. // Apparently the only consistent way to handle signals with sigwait is all-or-nothing :( // And while sigwait handles per-process signals more sanely, // it can't really handle thread-targeted signals at all. // So signals really do seem to always just be lose-lose. continue; } #ifndef HAVE_MACH # ifdef HAVE_ITIMER profile = (sig == SIGPROF); # else profile = (sig == SIGUSR1); # endif #endif if (sig == SIGINT) { if (jl_ignore_sigint()) { continue; } else if (exit_on_sigint) { critical = 1; } else { jl_try_deliver_sigint(); continue; } } else { critical = 0; } critical |= (sig == SIGTERM); critical |= (sig == SIGABRT); critical |= (sig == SIGQUIT); #ifdef SIGINFO critical |= (sig == SIGINFO); #else critical |= (sig == SIGUSR1 && !profile); #endif int doexit = critical; #ifdef SIGINFO if (sig == SIGINFO) doexit = 0; #else if (sig == SIGUSR1) doexit = 0; #endif bt_size = 0; #if !defined(JL_DISABLE_LIBUNWIND) unw_context_t *signal_context; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) for (int i = jl_n_threads; i-- > 0; ) { // notify thread to stop jl_thread_suspend_and_get_state(i, &signal_context); // do backtrace on thread contexts for critical signals // this part must be signal-handler safe if (critical) { bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / jl_n_threads - 1, signal_context); bt_data[bt_size++] = 0; } // do backtrace for profiler if (profile && running) { if (bt_size_cur < bt_size_max - 1) { // unwinding can fail, so keep track of the current state // and restore from the SEGV handler if anything happens. jl_ptls_t ptls = jl_get_ptls_states(); jl_jmp_buf *old_buf = ptls->safe_restore; jl_jmp_buf buf; ptls->safe_restore = &buf; if (jl_setjmp(buf, 0)) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } else { // Get backtrace data bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, signal_context); } ptls->safe_restore = old_buf; // Mark the end of this block with 0 bt_data_prof[bt_size_cur++] = 0; } if (bt_size_cur >= bt_size_max - 1) { // Buffer full: Delete the timer jl_profile_stop_timer(); } } // notify thread to resume jl_thread_resume(i, sig); } #endif // this part is async with the running of the rest of the program // and must be thread-safe, but not necessarily signal-handler safe if (critical) { jl_critical_error(sig, NULL, bt_data, &bt_size); if (doexit) { thread0_exit_count++; jl_exit_thread0(128 + sig); } } } return NULL; }