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(); } } }
/** 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); }
/** 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; }