//for looking up functions from gdb: JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) { // This function is not allowed to reference any TLS variables since // it can be called from an unmanaged thread on OSX. // it means calling getFunctionInfo with noInline = 1 jl_frame_t *frames = NULL; int n = jl_getFunctionInfo(&frames, ip, 0, 0); int i; for (i = 0; i < n; i++) { jl_frame_t frame = frames[i]; if (!frame.func_name) { jl_safe_printf("unknown function (ip: %p)\n", (void*)ip); } else { const char *inlined = frame.inlined ? " [inlined]" : ""; if (frame.line != -1) { jl_safe_printf("%s at %s:%" PRIuPTR "%s\n", frame.func_name, frame.file_name, (uintptr_t)frame.line, inlined); } else { jl_safe_printf("%s at %s (unknown line)%s\n", frame.func_name, frame.file_name, inlined); } free(frame.func_name); free(frame.file_name); } } free(frames); }
// what to do on a critical error static void jl_critical_error(int sig, bt_context_t context, ptrint_t *bt_data, size_t *bt_size) { size_t n = *bt_size; if (sig) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); jl_safe_printf("while loading %s, in expression starting on line %d\n", jl_filename, jl_lineno); if (context) *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context); for(size_t i=0; i < n; i++) gdblookup(bt_data[i]); gc_debug_print_status(); }
// what to do on a critical error static void jl_critical_error(int sig, bt_context_t context, ptrint_t *bt_data, size_t *bt_size) { // This function is not allowed to reference any TLS variables. // We need to explicitly pass in the TLS buffer pointer when // we make `jl_filename` and `jl_lineno` thread local. size_t n = *bt_size; if (sig) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); jl_safe_printf("while loading %s, in expression starting on line %d\n", jl_filename, jl_lineno); if (context) *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context); for(size_t i=0; i < n; i++) jl_gdblookup(bt_data[i]); gc_debug_print_status(); }
// request: // 0: nothing // 1: get state // 3: throw sigint if `!defer_signal && io_wait` or if force throw threshold // is reached void usr2_handler(int sig, siginfo_t *info, void *ctx) { jl_tls_states_t *ptls = jl_get_ptls_states(); sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); if (request == 1) { signal_context = jl_to_bt_context(ctx); pthread_mutex_lock(&in_signal_lock); pthread_cond_broadcast(&signal_caught_cond); pthread_cond_wait(&exit_signal_cond, &in_signal_lock); request = jl_atomic_exchange(&ptls->signal_request, 0); assert(request == 1); (void)request; pthread_cond_broadcast(&signal_caught_cond); pthread_mutex_unlock(&in_signal_lock); } else if (request == 2) { jl_unblock_signal(sig); int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { jl_safepoint_consume_sigint(); if (force) jl_safe_printf("WARNING: Force throwing a SIGINT\n"); // Force a throw jl_clear_force_sigint(); jl_throw_in_ctx(jl_interrupt_exception, ctx); } } }
JL_DLLEXPORT void jl_uv_writecb(uv_write_t *req, int status) { free(req); if (status < 0) { jl_safe_printf("jl_uv_writecb() ERROR: %s %s\n", uv_strerror(status), uv_err_name(status)); } }
void *mach_segv_listener(void *arg) { (void)arg; while (1) { int ret = mach_msg_server(exc_server,2048,segv_port,MACH_MSG_TIMEOUT_NONE); jl_safe_printf("mach_msg_server: %s\n", mach_error_string(ret)); jl_exit(128+SIGSEGV); } }
static BOOL WINAPI sigint_handler(DWORD wsig) //This needs winapi types to guarantee __stdcall { if (exit_on_sigint) jl_exit(0); int sig; //windows signals use different numbers from unix (raise) switch(wsig) { case CTRL_C_EVENT: sig = SIGINT; break; //case CTRL_BREAK_EVENT: sig = SIGTERM; break; // etc. default: sig = SIGTERM; break; } if (jl_defer_signal) { jl_signal_pending = sig; } else { jl_signal_pending = 0; if ((DWORD)-1 == SuspendThread(hMainThread)) { //error jl_safe_printf("error: SuspendThread failed\n"); return 0; } CONTEXT ctxThread; memset(&ctxThread,0,sizeof(CONTEXT)); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; if (!GetThreadContext(hMainThread, &ctxThread)) { //error jl_safe_printf("error: GetThreadContext failed\n"); return 0; } jl_throw_in_ctx(jl_interrupt_exception, &ctxThread, 1); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; if (!SetThreadContext(hMainThread,&ctxThread)) { jl_safe_printf("error: SetThreadContext failed\n"); //error return 0; } if ((DWORD)-1 == ResumeThread(hMainThread)) { jl_safe_printf("error: ResumeThread failed\n"); //error return 0; } } return 1; }
JL_DLLEXPORT void jl_set_ptls_states_getter(jl_get_ptls_states_func f) { // only allow setting this once if (f && f != jl_get_ptls_states_init && jl_tls_states_cb == jl_get_ptls_states_init) { jl_tls_states_cb = f; } else { jl_safe_printf("ERROR: Attempt to change TLS address.\n"); exit(1); } }
// Try to throw the exception in the master thread. static void jl_try_deliver_sigint(void) { jl_ptls_t ptls2 = jl_all_tls_states[0]; jl_safepoint_enable_sigint(); jl_wake_libuv(); if ((DWORD)-1 == SuspendThread(hMainThread)) { // error jl_safe_printf("error: SuspendThread failed\n"); return; } int force = jl_check_force_sigint(); if (force || (!ptls2->defer_signal && ptls2->io_wait)) { jl_safepoint_consume_sigint(); if (force) jl_safe_printf("WARNING: Force throwing a SIGINT\n"); // Force a throw jl_clear_force_sigint(); CONTEXT ctxThread; memset(&ctxThread, 0, sizeof(CONTEXT)); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; if (!GetThreadContext(hMainThread, &ctxThread)) { // error jl_safe_printf("error: GetThreadContext failed\n"); return; } jl_throw_in_ctx(jl_interrupt_exception, &ctxThread, 1); ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; if (!SetThreadContext(hMainThread, &ctxThread)) { jl_safe_printf("error: SetThreadContext failed\n"); // error return; } } if ((DWORD)-1 == ResumeThread(hMainThread)) { jl_safe_printf("error: ResumeThread failed\n"); // error return; } }
//for looking up functions from gdb: JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) { // This function is not allowed to reference any TLS variables since // it can be called from an unmanaged thread on OSX. char *func_name; size_t line_num; char *file_name; size_t inlinedat_line; char *inlinedat_file; jl_lambda_info_t *outer_linfo; frame_info_from_ip(&func_name, &file_name, &line_num, &inlinedat_file, &inlinedat_line, &outer_linfo, ip, /* skipC */ 0, /* skipInline */ 0); if (line_num == ip) { jl_safe_printf("unknown function (ip: %p)\n", (void*)ip); } else { if (line_num != -1) { jl_safe_printf("%s at %s:%" PRIuPTR "\n", inlinedat_file ? "[inline]" : func_name, file_name, (uintptr_t)line_num); } else { jl_safe_printf("%s at %s (unknown line)\n", inlinedat_file ? "[inline]" : func_name, file_name); } if (inlinedat_file) { if (inlinedat_line != -1) { jl_safe_printf("%s at %s:%" PRIuPTR "\n", func_name, inlinedat_file, (uintptr_t)inlinedat_line); } else { jl_safe_printf("%s at %s (unknown line)\n", func_name, inlinedat_file); } } } free(func_name); free(file_name); free(inlinedat_file); }
static void jl_try_throw_sigint(void) { jl_ptls_t ptls = jl_get_ptls_states(); jl_safepoint_enable_sigint(); jl_wake_libuv(); int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { jl_safepoint_consume_sigint(); if (force) jl_safe_printf("WARNING: Force throwing a SIGINT\n"); // Force a throw jl_clear_force_sigint(); jl_throw(jl_interrupt_exception); } }
static void segv_handler(int sig, siginfo_t *info, void *context) { jl_ptls_t ptls = jl_get_ptls_states(); assert(sig == SIGSEGV || sig == SIGBUS); if (jl_addr_is_safepoint((uintptr_t)info->si_addr)) { jl_unblock_signal(sig); #ifdef JULIA_ENABLE_THREADING jl_set_gc_and_wait(); // Do not raise sigint on worker thread if (ptls->tid != 0) return; #endif if (ptls->defer_signal) { jl_safepoint_defer_sigint(); } else if (jl_safepoint_consume_sigint()) { jl_clear_force_sigint(); jl_throw_in_ctx(ptls, jl_interrupt_exception, context); } return; } if (ptls->safe_restore || is_addr_on_stack(ptls, info->si_addr)) { // stack overflow, or restarting jl_ jl_unblock_signal(sig); jl_throw_in_ctx(ptls, jl_stackovf_exception, context); } else if (jl_is_on_sigstack(ptls, info->si_addr, context)) { // This mainly happens when one of the finalizers during final cleanup // on the signal stack has a deep/infinite recursion. // There isn't anything more we can do // (we are already corrupting that stack running this function) // so just call `_exit` to terminate immediately. jl_safe_printf("ERROR: Signal stack overflow, exit\n"); _exit(sig + 128); } else if (sig == SIGSEGV && info->si_code == SEGV_ACCERR) { // writing to read-only memory (e.g., mmap) jl_unblock_signal(sig); jl_throw_in_ctx(ptls, jl_readonlymemory_exception, context); } else { #ifdef SEGV_EXCEPTION jl_unblock_signal(sig); jl_throw_in_ctx(ptls, jl_segv_exception, context); #else sigdie_handler(sig, info, context); #endif } }
void jl_depwarn(const char *msg, jl_value_t *sym) { static jl_value_t *depwarn_func = NULL; if (!depwarn_func && jl_base_module) { depwarn_func = jl_get_global(jl_base_module, jl_symbol("depwarn")); } if (!depwarn_func) { jl_safe_printf("WARNING: %s\n", msg); return; } jl_value_t **depwarn_args; JL_GC_PUSHARGS(depwarn_args, 3); depwarn_args[0] = depwarn_func; depwarn_args[1] = jl_cstr_to_string(msg); depwarn_args[2] = sym; jl_apply(depwarn_args, 3); JL_GC_POP(); }
JL_DLLEXPORT void jl_depwarn_partial_indexing(size_t n) { static jl_value_t *depwarn_func = NULL; if (!depwarn_func && jl_base_module) { depwarn_func = jl_get_global(jl_base_module, jl_symbol("_depwarn_for_trailing_indices")); } if (!depwarn_func) { jl_safe_printf("WARNING: omitting indices for non-singleton trailing dimensions is deprecated. Use " "`reshape(A, Val(%zd))` or add trailing `1` indices to make the dimensionality of the array match " "the number of indices\n", n); return; } jl_value_t **depwarn_args; JL_GC_PUSHARGS(depwarn_args, 2); depwarn_args[0] = depwarn_func; depwarn_args[1] = jl_box_long(n); jl_apply(depwarn_args, 2); JL_GC_POP(); }
void sigdie_handler(int sig, siginfo_t *info, void *context) { if (sig != SIGINFO) { sigset_t sset; uv_tty_reset_mode(); sigfillset(&sset); sigprocmask(SIG_UNBLOCK, &sset, NULL); signal(sig, SIG_DFL); } jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); #ifdef __APPLE__ bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, (bt_context_t)&((ucontext64_t*)context)->uc_mcontext64->__ss); #else bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, (ucontext_t*)context); #endif jlbacktrace(); if (sig != SIGSEGV && sig != SIGBUS && sig != SIGILL && sig != SIGINFO) { raise(sig); } }
// request: // 0: nothing // 1: get state // 2: throw sigint if `!defer_signal && io_wait` or if force throw threshold // is reached // 3: exit with `thread0_exit_state` void usr2_handler(int sig, siginfo_t *info, void *ctx) { jl_ptls_t ptls = jl_get_ptls_states(); int errno_save = errno; sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); #if !defined(JL_DISABLE_LIBUNWIND) if (request == 1) { signal_context = jl_to_bt_context(ctx); pthread_mutex_lock(&in_signal_lock); pthread_cond_broadcast(&signal_caught_cond); pthread_cond_wait(&exit_signal_cond, &in_signal_lock); request = jl_atomic_exchange(&ptls->signal_request, 0); assert(request == 1); (void)request; pthread_cond_broadcast(&signal_caught_cond); pthread_mutex_unlock(&in_signal_lock); } else #endif if (request == 2) { int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { jl_safepoint_consume_sigint(); if (force) jl_safe_printf("WARNING: Force throwing a SIGINT\n"); // Force a throw jl_clear_force_sigint(); jl_throw_in_ctx(ptls, jl_interrupt_exception, sig, ctx); } } else if (request == 3) { jl_call_in_ctx(ptls, jl_exit_thread0_cb, sig, ctx); } errno = errno_save; }
static LONG WINAPI _exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo, int in_ctx) { 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; } 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); gdblookup((ptrint_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, ExceptionInfo->ContextRecord); jlbacktrace(); 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; 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; }
//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 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; }
void jl_show_sigill(void *_ctx) { char *pc = (char*)jl_get_pc_from_ctx(_ctx); // unsupported platform if (!pc) return; #if defined(_CPU_X86_64_) || defined(_CPU_X86_) uint8_t inst[15]; // max length of x86 instruction size_t len = jl_safe_read_mem(pc, (char*)inst, sizeof(inst)); // ud2 if (len >= 2 && inst[0] == 0x0f && inst[1] == 0x0b) { jl_safe_printf("Unreachable reached at %p\n", (void*)pc); } else { jl_safe_printf("Invalid instruction at %p: ", (void*)pc); for (int i = 0;i < len;i++) { if (i == 0) { jl_safe_printf("0x%02" PRIx8, inst[i]); } else { jl_safe_printf(", 0x%02" PRIx8, inst[i]); } } jl_safe_printf("\n"); } #elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_) uint32_t inst = 0; size_t len = jl_safe_read_mem(pc, (char*)&inst, 4); if (len < 4) jl_safe_printf("Fault when reading instruction: %d bytes read\n", (int)len); if (inst == 0xd4200020) { // brk #0x1 // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. jl_safe_printf("Unreachable reached at %p\n", pc); } else { jl_safe_printf("Invalid instruction at %p: 0x%08" PRIx32 "\n", pc, inst); } #elif defined(_OS_LINUX_) && defined(_CPU_ARM_) ucontext_t *ctx = (ucontext_t*)_ctx; if (ctx->uc_mcontext.arm_cpsr & (1 << 5)) { // Thumb uint16_t inst[2] = {0, 0}; size_t len = jl_safe_read_mem(pc, (char*)&inst, 4); if (len < 2) jl_safe_printf("Fault when reading Thumb instruction: %d bytes read\n", (int)len); // LLVM and GCC uses different code for the trap... if (inst[0] == 0xdefe || inst[0] == 0xdeff) { // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. jl_safe_printf("Unreachable reached in Thumb mode at %p: 0x%04" PRIx16 "\n", (void*)pc, inst[0]); } else { jl_safe_printf("Invalid Thumb instruction at %p: 0x%04" PRIx16 ", 0x%04" PRIx16 "\n", (void*)pc, inst[0], inst[1]); } } else { uint32_t inst = 0; size_t len = jl_safe_read_mem(pc, (char*)&inst, 4); if (len < 4) jl_safe_printf("Fault when reading instruction: %d bytes read\n", (int)len); // LLVM and GCC uses different code for the trap... if (inst == 0xe7ffdefe || inst == 0xe7f000f0) { // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. jl_safe_printf("Unreachable reached in ARM mode at %p: 0x%08" PRIx32 "\n", (void*)pc, inst); } else { jl_safe_printf("Invalid ARM instruction at %p: 0x%08" PRIx32 "\n", (void*)pc, inst); } } #else // TODO for PPC (void)_ctx; #endif }
void *mach_profile_listener(void *arg) { (void)arg; int i; const int max_size = 512; attach_exception_port(); #ifdef LIBOSXUNWIND mach_profiler_thread = mach_thread_self(); #endif mig_reply_error_t *bufRequest = (mig_reply_error_t *) malloc(max_size); while (1) { kern_return_t ret = mach_msg(&bufRequest->Head, MACH_RCV_MSG, 0, max_size, profile_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); HANDLE_MACH_ERROR("mach_msg", ret); // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) for (i = jl_n_threads; i-- > 0; ) { // if there is no space left, break early if (bt_size_cur >= bt_size_max - 1) break; unw_context_t *uc; jl_thread_suspend_and_get_state(i, &uc, -1); #ifdef LIBOSXUNWIND /* * Unfortunately compact unwind info is incorrectly generated for quite a number of * libraries by quite a large number of compilers. We can fall back to DWARF unwind info * in some cases, but in quite a number of cases (especially libraries not compiled in debug * mode, only the compact unwind info may be available). Even more unfortunately, there is no * way to detect such bogus compact unwind info (other than noticing the resulting segfault). * What we do here is ugly, but necessary until the compact unwind info situation improves. * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost * entry will always be correct, and the number of cases in which this is an issue is rather small. * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling * and during stack unwinding we only ever read memory, but never write it. */ forceDwarf = 0; unw_getcontext(&profiler_uc); // will resume from this point if the next lines segfault at any point if (forceDwarf == 0) { // Save the backtrace bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); } else if (forceDwarf == 1) { bt_size_cur += rec_backtrace_ctx_dwarf((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); } else if (forceDwarf == -1) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } forceDwarf = -2; #else bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc); #endif // Mark the end of this block with 0 bt_data_prof[bt_size_cur++] = 0; // We're done! Resume the thread. jl_thread_resume(i, 0); if (running) { // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm", ret) } } } }
//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_thread_state64_t state, old_state; x86_exception_state64_t exc_state; kern_return_t ret; //memset(&state,0,sizeof(x86_thread_state64_t)); //memset(&exc_state,0,sizeof(x86_exception_state64_t)); #ifdef LIBOSXUNWIND if (thread == mach_profiler_thread) { return profiler_segv_handler(exception_port,thread,task,exception,code,code_count); } #endif ret = thread_get_state(thread,x86_EXCEPTION_STATE64,(thread_state_t)&exc_state,&exc_count); HANDLE_MACH_ERROR("thread_get_state(1)",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 ret = thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state(2)",ret); old_state = state; // memset(&state,0,sizeof(x86_thread_state64_t)); // Setup libunwind information state.__rsp = (uint64_t)signal_stack + sig_stack_size; state.__rsp -= sizeof(unw_context_t); state.__rsp &= -16; unw_context_t *uc = (unw_context_t*)state.__rsp; state.__rsp -= 512; // This is for alignment. In particular note that the sizeof(void*) is necessary // since it would usually specify the return address (i.e., we are aligning the call // frame to a 16 byte boundary as required by the abi, but the stack pointer // to point to the byte beyond that. Not doing this leads to funny behavior on // the first access to an external function will fail due to stack misalignment state.__rsp &= -16; state.__rsp -= sizeof(void*); memset(uc,0,sizeof(unw_context_t)); memcpy(uc,&old_state,sizeof(x86_thread_state64_t)); state.__rdi = (uint64_t)uc; if (is_addr_on_stack((void*)fault_addr)) { state.__rip = (uint64_t)darwin_stack_overflow_handler; } #ifdef SEGV_EXCEPTION else if (msync((void*)(fault_addr & ~(jl_page_size - 1)), 1, MS_ASYNC) != 0) { // no page mapped at this address state.__rip = (uint64_t)darwin_segv_handler; } #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 state.__rip = (uint64_t)darwin_accerr_handler; } state.__rbp = state.__rsp; ret = thread_set_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,count); HANDLE_MACH_ERROR("thread_set_state",ret); return KERN_SUCCESS; } else { ret = thread_get_state(thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state(3)",ret); jl_safe_printf("\nsignal (%d): %s\n", SIGSEGV, strsignal(SIGSEGV)); bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, (unw_context_t*)&state); jlbacktrace(); 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 *mach_profile_listener(void *arg) { (void)arg; int max_size = 512; attach_exception_port(); mach_profiler_thread = mach_thread_self(); mig_reply_error_t *bufRequest = (mig_reply_error_t *) malloc(max_size); while (1) { kern_return_t ret = mach_msg(&bufRequest->Head, MACH_RCV_MSG, 0, max_size, profile_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); HANDLE_MACH_ERROR("mach_msg",ret); if (bt_size_cur < bt_size_max) { kern_return_t ret; // Suspend the thread so we may safely sample it ret = thread_suspend(main_thread); HANDLE_MACH_ERROR("thread_suspend",ret); // Do the actual sampling unsigned int count = MACHINE_THREAD_STATE_COUNT; x86_thread_state64_t state; // Get the state of the suspended thread ret = thread_get_state(main_thread,x86_THREAD_STATE64,(thread_state_t)&state,&count); HANDLE_MACH_ERROR("thread_get_state",ret); // Initialize the unwind context with the suspend thread's state unw_context_t uc; memset(&uc,0,sizeof(unw_context_t)); memcpy(&uc,&state,sizeof(x86_thread_state64_t)); /* * Unfortunately compact unwind info is incorrectly generated for quite a number of * libraries by quite a large number of compilers. We can fall back to DWARF unwind info * in some cases, but in quite a number of cases (especially libraries not compiled in debug * mode, only the compact unwind info may be available). Even more unfortunately, there is no * way to detect such bogus compact unwind info (other than noticing the resulting segfault). * What we do here is ugly, but necessary until the compact unwind info situation improves. * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost * entry will always be correct, and the number of cases in which this is an issue is rather small. * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling * and during stack unwinding we only ever read memory, but never write it. */ forceDwarf = 0; unw_getcontext(&profiler_uc); if (forceDwarf == 0) { // Save the backtrace bt_size_cur += rec_backtrace_ctx((ptrint_t*)bt_data_prof+bt_size_cur, bt_size_max-bt_size_cur-1, &uc); } else if (forceDwarf == 1) { bt_size_cur += rec_backtrace_ctx_dwarf((ptrint_t*)bt_data_prof+bt_size_cur, bt_size_max-bt_size_cur-1, &uc); } else if (forceDwarf == -1) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } forceDwarf = -2; // Mark the end of this block with 0 bt_data_prof[bt_size_cur] = 0; bt_size_cur++; // We're done! Resume the thread. ret = thread_resume(main_thread); HANDLE_MACH_ERROR("thread_resume",ret) if (running) { // Reset the alarm ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm",ret) } } }