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(); } } }
bool kscrashsentry_installMachHandler(KSCrash_SentryContext* const context) { KSLOG_DEBUG("Installing mach exception handler."); bool attributes_created = false; pthread_attr_t attr; kern_return_t kr; int error; const task_t thisTask = mach_task_self(); exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT; if(g_installed) { KSLOG_DEBUG("Exception handler already installed."); return true; } g_installed = 1; if(ksmach_isBeingTraced()) { // Different debuggers hook into different exception types. // For example, GDB uses EXC_BAD_ACCESS for single stepping, // and LLDB uses EXC_SOFTWARE to stop a debug session. // Because of this, it's safer to not hook into the mach exception // system at all while being debugged. KSLOG_WARN("Process is being debugged. Not installing handler."); goto failed; } g_context = context; KSLOG_DEBUG("Backing up original exception ports."); kr = task_get_exception_ports(thisTask, mask, g_previousExceptionPorts.masks, &g_previousExceptionPorts.count, g_previousExceptionPorts.ports, g_previousExceptionPorts.behaviors, g_previousExceptionPorts.flavors); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_get_exception_ports: %s", mach_error_string(kr)); goto failed; } if(g_exceptionPort == MACH_PORT_NULL) { KSLOG_DEBUG("Allocating new port with receive rights."); kr = mach_port_allocate(thisTask, MACH_PORT_RIGHT_RECEIVE, &g_exceptionPort); if(kr != KERN_SUCCESS) { KSLOG_ERROR("mach_port_allocate: %s", mach_error_string(kr)); goto failed; } KSLOG_DEBUG("Adding send rights to port."); kr = mach_port_insert_right(thisTask, g_exceptionPort, g_exceptionPort, MACH_MSG_TYPE_MAKE_SEND); if(kr != KERN_SUCCESS) { KSLOG_ERROR("mach_port_insert_right: %s", mach_error_string(kr)); goto failed; } } KSLOG_DEBUG("Installing port as exception handler."); kr = task_set_exception_ports(thisTask, mask, g_exceptionPort, EXCEPTION_DEFAULT, THREAD_STATE_NONE); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_set_exception_ports: %s", mach_error_string(kr)); goto failed; } KSLOG_DEBUG("Creating secondary exception thread (suspended)."); pthread_attr_init(&attr); attributes_created = true; pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); error = pthread_create(&g_secondaryThread, &attr, &ksmachexc_i_handleExceptions, kThreadSecondary); if(error != 0) { KSLOG_ERROR("pthread_create_suspended_np: %s", strerror(error)); goto failed; } context->reservedThreads[KSCrashReservedThreadTypeMachSecondary] = pthread_mach_thread_np(g_secondaryThread); KSLOG_DEBUG("Creating primary exception thread."); error = pthread_create(&g_primaryThread, &attr, &ksmachexc_i_handleExceptions, kThreadPrimary); if(error != 0) { KSLOG_ERROR("pthread_create: %s", strerror(error)); goto failed; } pthread_attr_destroy(&attr); context->reservedThreads[KSCrashReservedThreadTypeMachPrimary] = pthread_mach_thread_np(g_primaryThread); KSLOG_DEBUG("Mach exception handler installed."); return true; failed: KSLOG_DEBUG("Failed to install mach exception handler."); if(attributes_created) { pthread_attr_destroy(&attr); } kscrashsentry_uninstallMachHandler(); return false; }
bool kscrashsentry_installMachHandler(__unused KSCrash_SentryContext* const context) { KSLOG_WARN("Mach exception handler not available on this platform."); return false; }