int ksbt_backtraceSelf(uintptr_t* const backtraceBuffer, const int maxEntries) { return ksbt_backtraceThread(ksmach_thread_self(), backtraceBuffer, maxEntries); }
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(); } } }
void kscrashsentry_uninstallMachHandler(void) { KSLOG_DEBUG("Uninstalling mach exception handler."); if(!g_installed) { KSLOG_DEBUG("Mach exception handler was already uninstalled."); return; } // NOTE: Do not deallocate the exception port. If a secondary crash occurs // it will hang the process. ksmachexc_i_restoreExceptionPorts(); thread_t thread_self = ksmach_thread_self(); if(g_primaryPThread != 0 && g_primaryMachThread != thread_self) { KSLOG_DEBUG("Cancelling primary exception thread."); if(g_context->handlingCrash) { thread_terminate(g_primaryMachThread); } else { pthread_cancel(g_primaryPThread); } g_primaryMachThread = 0; g_primaryPThread = 0; } if(g_secondaryPThread != 0 && g_secondaryMachThread != thread_self) { KSLOG_DEBUG("Cancelling secondary exception thread."); if(g_context->handlingCrash) { thread_terminate(g_secondaryMachThread); } else { pthread_cancel(g_secondaryPThread); } g_secondaryMachThread = 0; g_secondaryPThread = 0; } KSLOG_DEBUG("Mach exception handlers uninstalled."); g_installed = 0; }
bool ksmach_resumeAllThreadsExcept(thread_t* exceptThreads, int exceptThreadsCount) { kern_return_t kr; const task_t thisTask = mach_task_self(); const thread_t thisThread = ksmach_thread_self(); thread_act_array_t threads; mach_msg_type_number_t numThreads; if((kr = task_threads(thisTask, &threads, &numThreads)) != KERN_SUCCESS) { KSLOG_ERROR("task_threads: %s", mach_error_string(kr)); return false; } for(mach_msg_type_number_t i = 0; i < numThreads; i++) { thread_t thread = threads[i]; if(thread != thisThread && !isThreadInList(thread, exceptThreads, exceptThreadsCount)) { if((kr = thread_resume(thread)) != KERN_SUCCESS) { KSLOG_ERROR("thread_resume (%08x): %s", thread, mach_error_string(kr)); // Don't treat this as a fatal error. } } } for(mach_msg_type_number_t i = 0; i < numThreads; i++) { mach_port_deallocate(thisTask, threads[i]); } vm_deallocate(thisTask, (vm_address_t)threads, sizeof(thread_t) * numThreads); return true; }
/** 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) { 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(ksmach_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) { bool wasHandlingCrash = g_context->handlingCrash; kscrashsentry_beginHandlingCrash(g_context); 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(ksmach_thread_self() == g_primaryMachThread) { KSLOG_DEBUG("This is the primary exception thread. Activating secondary thread."); if(thread_resume(g_secondaryMachThread) != 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(wasHandlingCrash) { 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 KSLOG_DEBUG("Fetching machine state."); STRUCT_MCONTEXT_L 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; }