Exemple #1
0
void jl_throw_in_thread(int tid, mach_port_t thread, jl_value_t *exception)
{
    unsigned int count = MACHINE_THREAD_STATE_COUNT;
    x86_thread_state64_t state;
    kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count);
    HANDLE_MACH_ERROR("thread_get_state", ret);

    jl_all_task_states[tid].ptls->bt_size =
        rec_backtrace_ctx(jl_all_task_states[tid].ptls->bt_data,
                          JL_MAX_BT_SIZE, (bt_context_t)&state);
    jl_all_task_states[tid].ptls->exception_in_transit = exception;

    uint64_t rsp = (uint64_t)jl_all_task_states[tid].signal_stack + sig_stack_size;
    rsp &= -16; // ensure 16-byte alignment

    // push (null) $RIP onto the stack
    rsp -= sizeof(void*);
    *(void**)rsp = NULL;

    state.__rsp = rsp; // set stack pointer
    state.__rip = (uint64_t)&jl_rethrow; // "call" the function

    ret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, count);
    HANDLE_MACH_ERROR("thread_set_state",ret);
}
Exemple #2
0
size_t rec_backtrace(uintptr_t *data, size_t maxsize)
{
    bt_context_t context;
    memset(&context, 0, sizeof(context));
    jl_unw_get(&context);
    return rec_backtrace_ctx(data, maxsize, &context);
}
Exemple #3
0
static void jl_throw_in_ctx(jl_ptls_t ptls, jl_value_t *e, void *sigctx)
{
    if (!ptls->safe_restore)
        ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE,
                                          jl_to_bt_context(sigctx));
    ptls->exception_in_transit = e;
    jl_call_in_ctx(ptls, &jl_rethrow, sigctx);
}
Exemple #4
0
static void JL_NORETURN jl_throw_in_ctx(jl_value_t *e, void *sigctx)
{
    if (!jl_safe_restore)
        jl_bt_size = rec_backtrace_ctx(jl_bt_data, JL_MAX_BT_SIZE,
                                       jl_to_bt_context(sigctx));
    jl_exception_in_transit = e;
    // TODO throw the error by modifying sigctx for supported platforms
    // This will avoid running the atexit handler on the signal stack
    // if no excepiton handler is registered.
    jl_rethrow();
}
Exemple #5
0
// 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();
}
Exemple #6
0
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);
    }
}
Exemple #7
0
// 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();
}
Exemple #8
0
void jl_throw_in_ctx(jl_value_t* excpt, CONTEXT *ctxThread, int bt)
{
    bt_size = bt ? rec_backtrace_ctx(bt_data, MAX_BT_SIZE, ctxThread) : 0;
    jl_exception_in_transit = excpt;
#if defined(_CPU_X86_64_)
    ctxThread->Rip = (DWORD64)&jl_rethrow;
    ctxThread->Rsp &= (DWORD64)-16;
    ctxThread->Rsp -= 8; //fix up the stack pointer -- this seems to be correct by observation
#elif defined(_CPU_X86_)
    ctxThread->Eip = (DWORD)&jl_rethrow;
    ctxThread->Esp &= (DWORD)-16;
    ctxThread->Esp -= 4; //fix up the stack pointer
#else
#error WIN16 not supported :P
#endif
}
Exemple #9
0
static DWORD WINAPI profile_bt( LPVOID lparam )
{
    // Note: illegal to use jl_* functions from this thread

    TIMECAPS tc;
    if (MMSYSERR_NOERROR!=timeGetDevCaps(&tc, sizeof(tc))) {
        fputs("failed to get timer resolution",stderr);
        hBtThread = 0;
        return 0;
    }
    while (1) {
        if (running && bt_size_cur < bt_size_max) {
            DWORD timeout = nsecprof/GIGA;
            timeout = min(max(timeout,tc.wPeriodMin*2),tc.wPeriodMax/2);
            Sleep(timeout);
            if ((DWORD)-1 == SuspendThread(hMainThread)) {
                fputs("failed to suspend main thread. aborting profiling.",stderr);
                break;
            }
            CONTEXT ctxThread;
            memset(&ctxThread, 0, sizeof(CONTEXT));
            ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
            if (!GetThreadContext(hMainThread, &ctxThread)) {
                fputs("failed to get context from main thread. aborting profiling.",stderr);
                break;
            }
            // Get backtrace data
            bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur,
                bt_size_max - bt_size_cur - 1, &ctxThread);
            // Mark the end of this block with 0
            bt_data_prof[bt_size_cur] = 0;
            bt_size_cur++;
            if ((DWORD)-1 == ResumeThread(hMainThread)) {
                fputs("failed to resume main thread! aborting.",stderr);
                gc_debug_critical_error();
                abort();
            }
        }
        else {
            SuspendThread(GetCurrentThread());
        }
    }
    hBtThread = 0;
    return 0;
}
Exemple #10
0
void jl_throw_in_ctx(jl_value_t *excpt, CONTEXT *ctxThread, int bt)
{
    assert(excpt != NULL);
#if defined(_CPU_X86_64_)
    DWORD64 Rsp = (ctxThread->Rsp&(DWORD64)-16) - 8;
#elif defined(_CPU_X86_)
    DWORD32 Esp = (ctxThread->Esp&(DWORD32)-16) - 4;
#else
#error WIN16 not supported :P
#endif
    bt_size = bt ? rec_backtrace_ctx(bt_data, MAX_BT_SIZE, ctxThread) : 0;
    jl_exception_in_transit = excpt;
#if defined(_CPU_X86_64_)
    *(DWORD64*)Rsp = 0;
    ctxThread->Rsp = Rsp;
    ctxThread->Rip = (DWORD64)&jl_rethrow;
#elif defined(_CPU_X86_)
    *(DWORD32*)Esp = 0;
    ctxThread->Esp = Esp;
    ctxThread->Eip = (DWORD)&jl_rethrow;
#endif
}
Exemple #11
0
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);
        }
    }
}
Exemple #12
0
void *mach_profile_listener(void *arg)
{
    (void)arg;
    int max_size = 512;
    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_PRINTF(JL_STDERR, "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)
            }
        }
    }
Exemple #13
0
static LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) {
    if (ExceptionInfo->ExceptionRecord->ExceptionFlags == 0) {
        switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
        case EXCEPTION_STACK_OVERFLOW:
#if defined(_CPU_X86_64_)
            ExceptionInfo->ContextRecord->Rip = (DWORD64)&win_raise_exception;
            ExceptionInfo->ContextRecord->Rcx = (DWORD64)jl_stackovf_exception;
            ExceptionInfo->ContextRecord->Rsp &= (DWORD64)-16;
            ExceptionInfo->ContextRecord->Rsp -= 8; //fix up the stack pointer -- this seems to be correct by observation
#elif defined(_CPU_X86_)
            ExceptionInfo->ContextRecord->Eip = (DWORD)&win_raise_exception;
            ExceptionInfo->ContextRecord->Ecx = (DWORD)jl_stackovf_exception;
            ExceptionInfo->ContextRecord->Esp &= (DWORD)-16;
            ExceptionInfo->ContextRecord->Esp -= 4; //fix up the stack pointer
#else
#error WIN16 not supported :P
#endif
            return EXCEPTION_CONTINUE_EXECUTION;
        default:
            ios_puts("Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.\nException: ", ios_stderr);
            switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
                case EXCEPTION_ACCESS_VIOLATION:
                    ios_puts("EXCEPTION_ACCESS_VIOLATION", ios_stderr); break;
                case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
                    ios_puts("EXCEPTION_ARRAY_BOUNDS_EXCEEDED", ios_stderr); break;
                case EXCEPTION_BREAKPOINT:
                    ios_puts("EXCEPTION_BREAKPOINT", ios_stderr); break;
                case EXCEPTION_DATATYPE_MISALIGNMENT:
                    ios_puts("EXCEPTION_DATATYPE_MISALIGNMENT", ios_stderr); break;
                case EXCEPTION_FLT_DENORMAL_OPERAND:
                    ios_puts("EXCEPTION_FLT_DENORMAL_OPERAND", ios_stderr); break;
                case EXCEPTION_FLT_DIVIDE_BY_ZERO:
                    ios_puts("EXCEPTION_FLT_DIVIDE_BY_ZERO", ios_stderr); break;
                case EXCEPTION_FLT_INEXACT_RESULT:
                    ios_puts("EXCEPTION_FLT_INEXACT_RESULT", ios_stderr); break;
                case EXCEPTION_FLT_INVALID_OPERATION:
                    ios_puts("EXCEPTION_FLT_INVALID_OPERATION", ios_stderr); break;
                case EXCEPTION_FLT_OVERFLOW:
                    ios_puts("EXCEPTION_FLT_OVERFLOW", ios_stderr); break;
                case EXCEPTION_FLT_STACK_CHECK:
                    ios_puts("EXCEPTION_FLT_STACK_CHECK", ios_stderr); break;
                case EXCEPTION_FLT_UNDERFLOW:
                    ios_puts("EXCEPTION_FLT_UNDERFLOW", ios_stderr); break;
                case EXCEPTION_ILLEGAL_INSTRUCTION:
                    ios_puts("EXCEPTION_ILLEGAL_INSTRUCTION", ios_stderr); break;
                case EXCEPTION_IN_PAGE_ERROR:
                    ios_puts("EXCEPTION_IN_PAGE_ERROR", ios_stderr); break;
                case EXCEPTION_INT_DIVIDE_BY_ZERO:
                    ios_puts("EXCEPTION_INT_DIVIDE_BY_ZERO", ios_stderr); break;
                case EXCEPTION_INT_OVERFLOW:
                    ios_puts("EXCEPTION_INT_OVERFLOW", ios_stderr); break;
                case EXCEPTION_INVALID_DISPOSITION:
                    ios_puts("EXCEPTION_INVALID_DISPOSITION", ios_stderr); break;
                case EXCEPTION_NONCONTINUABLE_EXCEPTION:
                    ios_puts("EXCEPTION_NONCONTINUABLE_EXCEPTION", ios_stderr); break;
                case EXCEPTION_PRIV_INSTRUCTION:
                    ios_puts("EXCEPTION_PRIV_INSTRUCTION", ios_stderr); break;
                case EXCEPTION_SINGLE_STEP:
                    ios_puts("EXCEPTION_SINGLE_STEP", ios_stderr); break;
                case EXCEPTION_STACK_OVERFLOW:
                    ios_puts("EXCEPTION_STACK_OVERFLOW", ios_stderr); break;
                default:
                    ios_puts("UNKNOWN", ios_stderr); break;
            }
            ios_printf(ios_stderr," 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();
            break;
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;
}
Exemple #14
0
static LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
    if (ExceptionInfo->ExceptionRecord->ExceptionFlags == 0) {
        switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
        case EXCEPTION_STACK_OVERFLOW:
            jl_throw_in_ctx(jl_stackovf_exception, ExceptionInfo->ContextRecord, 0);
            return EXCEPTION_CONTINUE_EXECUTION;
        default:
            ios_puts("Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.\nException: ", ios_stderr);
            switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
                case EXCEPTION_ACCESS_VIOLATION:
                    ios_puts("EXCEPTION_ACCESS_VIOLATION", ios_stderr); break;
                case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
                    ios_puts("EXCEPTION_ARRAY_BOUNDS_EXCEEDED", ios_stderr); break;
                case EXCEPTION_BREAKPOINT:
                    ios_puts("EXCEPTION_BREAKPOINT", ios_stderr); break;
                case EXCEPTION_DATATYPE_MISALIGNMENT:
                    ios_puts("EXCEPTION_DATATYPE_MISALIGNMENT", ios_stderr); break;
                case EXCEPTION_FLT_DENORMAL_OPERAND:
                    ios_puts("EXCEPTION_FLT_DENORMAL_OPERAND", ios_stderr); break;
                case EXCEPTION_FLT_DIVIDE_BY_ZERO:
                    ios_puts("EXCEPTION_FLT_DIVIDE_BY_ZERO", ios_stderr); break;
                case EXCEPTION_FLT_INEXACT_RESULT:
                    ios_puts("EXCEPTION_FLT_INEXACT_RESULT", ios_stderr); break;
                case EXCEPTION_FLT_INVALID_OPERATION:
                    ios_puts("EXCEPTION_FLT_INVALID_OPERATION", ios_stderr); break;
                case EXCEPTION_FLT_OVERFLOW:
                    ios_puts("EXCEPTION_FLT_OVERFLOW", ios_stderr); break;
                case EXCEPTION_FLT_STACK_CHECK:
                    ios_puts("EXCEPTION_FLT_STACK_CHECK", ios_stderr); break;
                case EXCEPTION_FLT_UNDERFLOW:
                    ios_puts("EXCEPTION_FLT_UNDERFLOW", ios_stderr); break;
                case EXCEPTION_ILLEGAL_INSTRUCTION:
                    ios_puts("EXCEPTION_ILLEGAL_INSTRUCTION", ios_stderr); break;
                case EXCEPTION_IN_PAGE_ERROR:
                    ios_puts("EXCEPTION_IN_PAGE_ERROR", ios_stderr); break;
                case EXCEPTION_INT_DIVIDE_BY_ZERO:
                    ios_puts("EXCEPTION_INT_DIVIDE_BY_ZERO", ios_stderr); break;
                case EXCEPTION_INT_OVERFLOW:
                    ios_puts("EXCEPTION_INT_OVERFLOW", ios_stderr); break;
                case EXCEPTION_INVALID_DISPOSITION:
                    ios_puts("EXCEPTION_INVALID_DISPOSITION", ios_stderr); break;
                case EXCEPTION_NONCONTINUABLE_EXCEPTION:
                    ios_puts("EXCEPTION_NONCONTINUABLE_EXCEPTION", ios_stderr); break;
                case EXCEPTION_PRIV_INSTRUCTION:
                    ios_puts("EXCEPTION_PRIV_INSTRUCTION", ios_stderr); break;
                case EXCEPTION_SINGLE_STEP:
                    ios_puts("EXCEPTION_SINGLE_STEP", ios_stderr); break;
                case EXCEPTION_STACK_OVERFLOW:
                    ios_puts("EXCEPTION_STACK_OVERFLOW", ios_stderr); break;
                default:
                    ios_puts("UNKNOWN", ios_stderr); break;
            }
            ios_printf(ios_stderr," 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();
            break;
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;
}
Exemple #15
0
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);
        }
    }
}
Exemple #16
0
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)
            }
        }
    }
}
Exemple #17
0
//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);
}
Exemple #18
0
void darwin_accerr_handler(unw_context_t *uc)
{
    bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, uc);
    jl_exception_in_transit = jl_readonlymemory_exception;
    jl_rethrow();
}
Exemple #19
0
void darwin_stack_overflow_handler(unw_context_t *uc)
{
    bt_size = rec_backtrace_ctx(bt_data, MAX_BT_SIZE, uc);
    jl_exception_in_transit = jl_stackovf_exception;
    jl_rethrow();
}
Exemple #20
0
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;
}
Exemple #21
0
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;
}