bool ksmach_getThreadQueueName(const thread_t thread, char* const buffer, size_t bufLength) { struct internal_dispatch_queue_s* pQueue; struct internal_dispatch_queue_s queue; if(bufLength > sizeof(queue.dq_label)) { bufLength = sizeof(queue.dq_label); } // Recast the opaque thread to our hacky internal thread structure pointer. const pthread_t pthread = pthread_from_mach_thread_np(thread); const internal_pthread_t const threadStruct = (internal_pthread_t)pthread; if(ksmach_copyMem(&threadStruct->tsd[dispatch_queue_key], &pQueue, sizeof(pQueue)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy queue pointer from %p", &threadStruct->tsd[dispatch_queue_key]); return false; } if(pQueue == NULL) { KSLOG_TRACE("Queue pointer is NULL"); return false; } if(ksmach_copyMem(pQueue, &queue, sizeof(queue)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy queue data from %p", pQueue); return false; } // Queue label must be a null terminated string. int iLabel; for(iLabel = 0; iLabel < (int)sizeof(queue.dq_label); iLabel++) { if(queue.dq_label[iLabel] < ' ' || queue.dq_label[iLabel] > '~') { break; } } if(queue.dq_label[iLabel] != 0) { // Found a non-null, invalid char. KSLOG_TRACE("Queue label contains invalid chars"); return false; } strncpy(buffer, queue.dq_label, bufLength); KSLOG_TRACE("Queue label = %s", buffer); return true; }
int ksmc_indexOfThread(const KSMachineContext* const context, KSThread thread) { KSLOG_TRACE("check thread vs %d threads", context->threadCount); for(int i = 0; i < (int)context->threadCount; i++) { KSLOG_TRACE("%d: %x vs %x", i, thread, context->allThreads[i]); if(context->allThreads[i] == thread) { return i; } } return -1; }
void kscrash_setUserInfoJSON(const char* const userInfoJSON) { KSLOG_TRACE("set userInfoJSON to %p", userInfoJSON); KSCrash_Context* context = crashContext(); if(context->config.userInfoJSON != NULL) { KSLOG_TRACE("Free old data at %p", context->config.userInfoJSON); free((void*)context->config.userInfoJSON); } if(userInfoJSON != NULL) { context->config.userInfoJSON = strdup(userInfoJSON); KSLOG_TRACE("Duplicated string to %p", context->config.userInfoJSON); } }
static inline bool getThreadList(KSMachineContext* context) { const task_t thisTask = mach_task_self(); KSLOG_DEBUG("Getting thread list"); kern_return_t kr; thread_act_array_t threads; mach_msg_type_number_t actualThreadCount; if((kr = task_threads(thisTask, &threads, &actualThreadCount)) != KERN_SUCCESS) { KSLOG_ERROR("task_threads: %s", mach_error_string(kr)); return false; } KSLOG_TRACE("Got %d threads", context->threadCount); int threadCount = (int)actualThreadCount; int maxThreadCount = sizeof(context->allThreads) / sizeof(context->allThreads[0]); if(threadCount > maxThreadCount) { KSLOG_ERROR("Thread count %d is higher than maximum of %d", threadCount, maxThreadCount); threadCount = maxThreadCount; } for(int i = 0; i < threadCount; i++) { context->allThreads[i] = threads[i]; } context->threadCount = threadCount; for(mach_msg_type_number_t i = 0; i < actualThreadCount; i++) { mach_port_deallocate(thisTask, context->allThreads[i]); } vm_deallocate(thisTask, (vm_address_t)threads, sizeof(thread_t) * actualThreadCount); return true; }
/** Restore the original mach exception ports. */ void ksmachexc_i_restoreExceptionPorts(void) { KSLOG_DEBUG("Restoring original exception ports."); if(g_previousExceptionPorts.count == 0) { KSLOG_DEBUG("Original exception ports were already restored."); return; } const task_t thisTask = mach_task_self(); kern_return_t kr; // Reinstall old exception ports. for(mach_msg_type_number_t i = 0; i < g_previousExceptionPorts.count; i++) { KSLOG_TRACE("Restoring port index %d", i); kr = task_set_exception_ports(thisTask, g_previousExceptionPorts.masks[i], g_previousExceptionPorts.ports[i], g_previousExceptionPorts.behaviors[i], g_previousExceptionPorts.flavors[i]); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_set_exception_ports: %s", mach_error_string(kr)); } } KSLOG_DEBUG("Exception ports restored."); g_previousExceptionPorts.count = 0; }
thread_t ksmach_machThreadFromPThread(const pthread_t pthread) { const internal_pthread_t threadStruct = (internal_pthread_t)pthread; thread_t machThread = 0; if(ksmach_copyMem(&threadStruct->kernel_thread, &machThread, sizeof(machThread)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy mach thread from %p", threadStruct->kernel_thread); return 0; } return machThread; }
void kscrash_reinstall(const char* const crashReportFilePath, const char* const recrashReportFilePath, const char* const stateFilePath, const char* const crashID) { KSLOG_TRACE("reportFilePath = %s", crashReportFilePath); KSLOG_TRACE("secondaryReportFilePath = %s", recrashReportFilePath); KSLOG_TRACE("stateFilePath = %s", stateFilePath); KSLOG_TRACE("crashID = %s", crashID); ksstring_replace((const char**)&g_stateFilePath, stateFilePath); ksstring_replace((const char**)&g_crashReportFilePath, crashReportFilePath); ksstring_replace((const char**)&g_recrashReportFilePath, recrashReportFilePath); KSCrash_Context* context = crashContext(); ksstring_replace(&context->config.crashID, crashID); if(!kscrashstate_init(g_stateFilePath, &context->state)) { KSLOG_ERROR("Failed to initialize persistent crash state"); } context->state.appLaunchTime = mach_absolute_time(); }
bool ksmc_getContextForSignal(void* signalUserContext, KSMachineContext* destinationContext) { KSLOG_DEBUG("Get context from signal user context and put into %p.", destinationContext); _STRUCT_MCONTEXT* sourceContext = ((SignalUserContext*)signalUserContext)->UC_MCONTEXT; memcpy(&destinationContext->machineContext, sourceContext, sizeof(destinationContext->machineContext)); destinationContext->thisThread = (thread_t)ksthread_self(); destinationContext->isCrashedContext = true; destinationContext->isSignalContext = true; destinationContext->isStackOverflow = isStackOverflow(destinationContext); getThreadList(destinationContext); KSLOG_TRACE("Context retrieved."); return true; }
bool ksmach_getThreadQueueName(const thread_t thread, char* const buffer, size_t bufLength) { // WARNING: This implementation is no longer async-safe! integer_t infoBuffer[THREAD_IDENTIFIER_INFO_COUNT] = {0}; thread_info_t info = infoBuffer; mach_msg_type_number_t inOutSize = THREAD_IDENTIFIER_INFO_COUNT; kern_return_t kr = 0; kr = thread_info(thread, THREAD_IDENTIFIER_INFO, info, &inOutSize); if(kr != KERN_SUCCESS) { KSLOG_TRACE("Error getting thread_info with flavor THREAD_IDENTIFIER_INFO from mach thread : %s", mach_error_string(kr)); return false; } thread_identifier_info_t idInfo = (thread_identifier_info_t)info; dispatch_queue_t* dispatch_queue_ptr = (dispatch_queue_t*)idInfo->dispatch_qaddr; //thread_handle shouldn't be 0 also, because //identifier_info->dispatch_qaddr = identifier_info->thread_handle + get_dispatchqueue_offset_from_proc(thread->task->bsd_info); if(dispatch_queue_ptr == NULL || idInfo->thread_handle == 0 || *dispatch_queue_ptr == NULL) { KSLOG_TRACE("This thread doesn't have a dispatch queue attached : %p", thread); return false; } dispatch_queue_t dispatch_queue = *dispatch_queue_ptr; const char* queue_name = dispatch_queue_get_label(dispatch_queue); if(queue_name == NULL) { KSLOG_TRACE("Error while getting dispatch queue name : %p", dispatch_queue); return false; } KSLOG_TRACE("Dispatch queue name: %s", queue_name); size_t length = strlen(queue_name); // Queue label must be a null terminated string. size_t iLabel; for(iLabel = 0; iLabel < length + 1; iLabel++) { if(queue_name[iLabel] < ' ' || queue_name[iLabel] > '~') { break; } } if(queue_name[iLabel] != 0) { // Found a non-null, invalid char. KSLOG_TRACE("Queue label contains invalid chars"); return false; } bufLength = MIN(length, bufLength - 1);//just strlen, without null-terminator strncpy(buffer, queue_name, bufLength); buffer[bufLength] = 0;//terminate string KSLOG_TRACE("Queue label = %s", buffer); return true; }
bool ksmach_getThreadName(const thread_t thread, char* const buffer, size_t bufLength) { const pthread_t pthread = ksmach_pthreadFromMachThread(thread); const internal_pthread_t threadStruct = (internal_pthread_t)pthread; size_t copyLength = bufLength > MAXTHREADNAMESIZE ? MAXTHREADNAMESIZE : bufLength; if(ksmach_copyMem(threadStruct->pthread_name, buffer, copyLength) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy thread name from %p", threadStruct->pthread_name); return false; } buffer[copyLength-1] = 0; return true; }
bool ksmc_getContextForThread(KSThread thread, KSMachineContext* destinationContext, bool isCrashedContext) { KSLOG_DEBUG("Fill thread 0x%x context into %p. is crashed = %d", thread, destinationContext, isCrashedContext); memset(destinationContext, 0, sizeof(*destinationContext)); destinationContext->thisThread = (thread_t)thread; destinationContext->isCurrentThread = thread == ksthread_self(); destinationContext->isCrashedContext = isCrashedContext; destinationContext->isSignalContext = false; if(ksmc_canHaveCPUState(destinationContext)) { kscpu_getState(destinationContext); } if(ksmc_isCrashedContext(destinationContext)) { destinationContext->isStackOverflow = isStackOverflow(destinationContext); getThreadList(destinationContext); } KSLOG_TRACE("Context retrieved."); return true; }
void kscrash_setCrashNotifyCallback(const KSReportWriteCallback onCrashNotify) { KSLOG_TRACE("Set onCrashNotify to %p", onCrashNotify); crashContext()->config.onCrashNotify = onCrashNotify; }
void kscrash_setUserInfoJSON(const char* const userInfoJSON) { KSLOG_TRACE("set userInfoJSON to %p", userInfoJSON); KSCrash_Context* context = crashContext(); ksstring_replace(&context->config.userInfoJSON, userInfoJSON); }
bool kscrash_install(const char* const crashReportFilePath, const char* const recrashReportFilePath, const char* const stateFilePath, const char* const crashID, const char* const userInfoJSON, unsigned int zombieCacheSize, const bool printTraceToStdout, const KSReportWriteCallback onCrashNotify) { KSLOG_DEBUG("Installing crash reporter."); KSLOG_TRACE("reportFilePath = %s", reportFilePath); KSLOG_TRACE("secondaryReportFilePath = %s", secondaryReportFilePath); KSLOG_TRACE("stateFilePath = %s", stateFilePath); KSLOG_TRACE("crashID = %s", crashID); KSLOG_TRACE("userInfoJSON = %p", userInfoJSON); KSLOG_TRACE("zombieCacheSize = %d", zombieCacheSize); KSLOG_TRACE("printTraceToStdout = %d", printTraceToStdout); KSLOG_TRACE("onCrashNotify = %p", onCrashNotify); static volatile sig_atomic_t initialized = 0; if(!initialized) { initialized = 1; g_stateFilePath = strdup(stateFilePath); g_crashReportFilePath = strdup(crashReportFilePath); g_recrashReportFilePath = strdup(recrashReportFilePath); KSCrash_Context* context = crashContext(); context->crash.onCrash = kscrash_i_onCrash; if(ksmach_isBeingTraced()) { KSLOGBASIC_WARN("KSCrash: App is running in a debugger. Crash handlers have been disabled for the sanity of all."); } else if(kscrashsentry_installWithContext(&context->crash, KSCrashTypeAll) == 0) { KSLOG_ERROR("Failed to install any handlers"); } if(!kscrashstate_init(g_stateFilePath, &context->state)) { KSLOG_ERROR("Failed to initialize persistent crash state"); } context->state.appLaunchTime = mach_absolute_time(); context->config.printTraceToStdout = printTraceToStdout; context->config.systemInfoJSON = kssysteminfo_toJSON(); context->config.processName = kssystemInfo_copyProcessName(); kscrash_setUserInfoJSON(userInfoJSON); context->config.crashID = strdup(crashID); context->config.onCrashNotify = onCrashNotify; if(zombieCacheSize > 0) { KSLOG_DEBUG("zombieCacheSize > 0. Installing zombie handler."); kszombie_install(zombieCacheSize); } KSLOG_DEBUG("Installation complete."); return true; } KSLOG_ERROR("Called more than once"); return false; }