void kscrashsentry_reportUserException(const char* name,
                                       const char* reason,
                                       const char* language,
                                       const char* lineOfCode,
                                       const char* stackTrace,
                                       bool terminateProgram)
{
    if(g_context == NULL)
    {
        KSLOG_WARN("User-reported exception sentry is not installed. Exception has not been recorded.");
    }
    else
    {
        kscrashsentry_beginHandlingCrash(g_context);

        KSLOG_DEBUG("Suspending all threads");
        kscrashsentry_suspendThreads();

        KSLOG_DEBUG("Fetching call stack.");
        int callstackCount = 100;
        uintptr_t callstack[callstackCount];
        callstackCount = backtrace((void**)callstack, callstackCount);
        if(callstackCount <= 0)
        {
            KSLOG_ERROR("backtrace() returned call stack length of %d", callstackCount);
            callstackCount = 0;
        }

        KSLOG_DEBUG("Filling out context.");
        g_context->crashType = KSCrashTypeUserReported;
        g_context->offendingThread = ksmach_thread_self();
        g_context->registersAreValid = false;
        g_context->crashReason = reason;
        g_context->stackTrace = callstack;
        g_context->stackTraceLength = callstackCount;
        g_context->userException.name = name;
        g_context->userException.language = language;
        g_context->userException.lineOfCode = lineOfCode;
        g_context->userException.customStackTrace = stackTrace;

        KSLOG_DEBUG("Calling main crash handler.");
        g_context->onCrash();

        if(terminateProgram)
        {
            kscrashsentry_uninstall(KSCrashTypeAll);
            kscrashsentry_resumeThreads();
            abort();
        }
        else
        {
            kscrashsentry_clearContext(g_context);
            kscrashsentry_resumeThreads();
        }
    }
}
Ejemplo n.º 2
0
/** Our custom signal handler.
 * Restore the default signal handlers, record the signal information, and
 * write a crash report.
 * Once we're done, re-raise the signal and let the default handlers deal with
 * it.
 *
 * @param signal The signal that was raised.
 *
 * @param signalInfo Information about the signal.
 *
 * @param userContext Other contextual information.
 */
void kssighndl_i_handleSignal(int sigNum,
                              siginfo_t* signalInfo,
                              void* userContext)
{
    KSLOG_DEBUG("Trapped signal %d", sigNum);
    if(g_installed)
    {
        KSLOG_DEBUG("Signal handler is installed. Continuing signal handling.");

        KSLOG_DEBUG("Suspending all threads.");
        kscrashsentry_suspendThreads();

        if(g_context->handlingCrash)
        {
            KSLOG_INFO("Detected crash in the crash reporter. Restoring original handlers.");
            g_context->crashedDuringCrashHandling = true;
            kscrashsentry_uninstall(KSCrashTypeAsyncSafe);
        }
        g_context->handlingCrash = true;


        KSLOG_DEBUG("Filling out context.");
        g_context->crashType = KSCrashTypeSignal;
        g_context->offendingThread = mach_thread_self();
        g_context->registersAreValid = true;
        g_context->faultAddress = (uintptr_t)signalInfo->si_addr;
        g_context->signal.userContext = userContext;
        g_context->signal.signalInfo = signalInfo;


        KSLOG_DEBUG("Calling main crash handler.");
        g_context->onCrash();


        KSLOG_DEBUG("Crash handling complete. Restoring original handlers.");
        kscrashsentry_uninstall(KSCrashTypeAsyncSafe);
        kscrashsentry_resumeThreads();
    }

    KSLOG_DEBUG("Re-raising signal for regular handlers to catch.");
    // This is technically not allowed, but it works in OSX and iOS.
    raise(sigNum);
}
Ejemplo n.º 3
0
/** Our exception handler thread routine.
 * Wait for an exception message, uninstall our exception port, record the
 * exception information, and write a report.
 */
void* ksmachexc_i_handleExceptions(void* const userData)
{
    #pragma unused(userData)

    MachExceptionMessage exceptionMessage = {{0}};
    MachReplyMessage replyMessage = {{0}};

    const char* threadName = (const char*) userData;
    pthread_setname_np(threadName);
    if(threadName == kThreadSecondary)
    {
        KSLOG_DEBUG("This is the secondary thread. Suspending.");
        thread_suspend(mach_thread_self());
    }

    for(;;)
    {
        KSLOG_DEBUG("Waiting for mach exception");

        // Wait for a message.
        kern_return_t kr = mach_msg(&exceptionMessage.header,
                                    MACH_RCV_MSG,
                                    0,
                                    sizeof(exceptionMessage),
                                    g_exceptionPort,
                                    MACH_MSG_TIMEOUT_NONE,
                                    MACH_PORT_NULL);
        if(kr == KERN_SUCCESS)
        {
            break;
        }

        // Loop and try again on failure.
        KSLOG_ERROR("mach_msg: %s", mach_error_string(kr));
    }

    KSLOG_DEBUG("Trapped mach exception code 0x%x, subcode 0x%x",
                exceptionMessage.code[0], exceptionMessage.code[1]);
    if(g_installed)
    {
        KSLOG_DEBUG("Exception handler is installed. Continuing exception handling.");

        KSLOG_DEBUG("Suspending all threads");
        kscrashsentry_suspendThreads();

        // Switch to the secondary thread if necessary, or uninstall the handler
        // to avoid a death loop.
        if(mach_thread_self() == pthread_mach_thread_np(g_primaryThread))
        {
            KSLOG_DEBUG("This is the primary exception thread. Activating secondary thread.");
            if(thread_resume(pthread_mach_thread_np(g_secondaryThread)) != KERN_SUCCESS)
            {
                KSLOG_DEBUG("Could not activate secondary thread. Restoring original exception ports.");
                ksmachexc_i_restoreExceptionPorts();
            }
        }
        else
        {
            KSLOG_DEBUG("This is the secondary exception thread. Restoring original exception ports.");
            ksmachexc_i_restoreExceptionPorts();
        }

        if(g_context->handlingCrash)
        {
            KSLOG_INFO("Detected crash in the crash reporter. Restoring original handlers.");
            // The crash reporter itself crashed. Make a note of this and
            // uninstall all handlers so that we don't get stuck in a loop.
            g_context->crashedDuringCrashHandling = true;
            kscrashsentry_uninstall(KSCrashTypeAsyncSafe);
        }


        // Fill out crash information
        g_context->handlingCrash = true;

        KSLOG_DEBUG("Fetching machine state.");
        _STRUCT_MCONTEXT machineContext;
        if(ksmachexc_i_fetchMachineState(exceptionMessage.thread.name, &machineContext))
        {
            if(exceptionMessage.exception == EXC_BAD_ACCESS)
            {
                g_context->faultAddress = ksmach_faultAddress(&machineContext);
            }
            else
            {
                g_context->faultAddress = ksmach_instructionAddress(&machineContext);
            }
        }

        KSLOG_DEBUG("Filling out context.");
        g_context->crashType = KSCrashTypeMachException;
        g_context->offendingThread = exceptionMessage.thread.name;
        g_context->registersAreValid = true;
        g_context->mach.type = exceptionMessage.exception;
        g_context->mach.code = exceptionMessage.code[0];
        g_context->mach.subcode = exceptionMessage.code[1];


        KSLOG_DEBUG("Calling main crash handler.");
        g_context->onCrash();


        KSLOG_DEBUG("Crash handling complete. Restoring original handlers.");
        kscrashsentry_uninstall(KSCrashTypeAsyncSafe);
        kscrashsentry_resumeThreads();
    }

    KSLOG_DEBUG("Replying to mach exception message.");
    // Send a reply saying "I didn't handle this exception".
    replyMessage.header = exceptionMessage.header;
    replyMessage.NDR = exceptionMessage.NDR;
    replyMessage.returnCode = KERN_FAILURE;

    mach_msg(&replyMessage.header,
             MACH_SEND_MSG,
             sizeof(replyMessage),
             0,
             MACH_PORT_NULL,
             MACH_MSG_TIMEOUT_NONE,
             MACH_PORT_NULL);

    return NULL;
}