void kscrash_setDoNotIntrospectClasses(const char** doNotIntrospectClasses, size_t length) { const char** oldClasses = crashContext()->config.introspectionRules.restrictedClasses; size_t oldClassesLength = crashContext()->config.introspectionRules.restrictedClassesCount; const char** newClasses = nil; size_t newClassesLength = 0; if(doNotIntrospectClasses != nil && length > 0) { newClassesLength = length; newClasses = malloc(sizeof(*newClasses) * newClassesLength); if(newClasses == nil) { KSLOG_ERROR("Could not allocate memory"); return; } for(size_t i = 0; i < newClassesLength; i++) { newClasses[i] = strdup(doNotIntrospectClasses[i]); } } crashContext()->config.introspectionRules.restrictedClasses = newClasses; crashContext()->config.introspectionRules.restrictedClassesCount = newClassesLength; if(oldClasses != nil) { for(size_t i = 0; i < oldClassesLength; i++) { free((void*)oldClasses[i]); } free(oldClasses); } }
KSCrashType kscrash_setHandlingCrashTypes(KSCrashType crashTypes) { if((crashTypes & KSCrashTypeDebuggerUnsafe) && ksmach_isBeingTraced()) { KSLOGBASIC_WARN("KSCrash: App is running in a debugger. The following crash types have been disabled:"); KSCrashType disabledCrashTypes = crashTypes & KSCrashTypeDebuggerUnsafe; for(int i = 0; i < 31; i++) { KSCrashType type = 1 << i; if(disabledCrashTypes & type) { KSLOGBASIC_WARN("* %s", kscrashtype_name(type)); } } crashTypes &= KSCrashTypeDebuggerSafe; } KSCrash_Context* context = crashContext(); context->config.handlingCrashTypes = crashTypes; if(g_installed) { kscrashsentry_uninstall(~crashTypes); crashTypes = kscrashsentry_installWithContext(&context->crash, crashTypes, kscrash_i_onCrash); } return crashTypes; }
KSCrashType kscrash_install(const char* const crashReportFilePath, const char* const recrashReportFilePath, const char* stateFilePath, const char* crashID) { KSLOG_DEBUG("Installing crash reporter."); KSCrash_Context* context = crashContext(); if(g_installed) { KSLOG_DEBUG("Crash reporter already installed."); return context->config.handlingCrashTypes; } g_installed = 1; ksmach_init(); ksobjc_init(); kscrash_reinstall(crashReportFilePath, recrashReportFilePath, stateFilePath, crashID); KSCrashType crashTypes = kscrash_setHandlingCrashTypes(context->config.handlingCrashTypes); context->config.systemInfoJSON = kssysteminfo_toJSON(); context->config.processName = kssysteminfo_copyProcessName(); KSLOG_DEBUG("Installation complete."); return crashTypes; }
KSCrashType kscrash_setHandlingCrashTypes(KSCrashType crashTypes) { KSCrash_Context* context = crashContext(); context->config.handlingCrashTypes = crashTypes; if(g_installed) { kscrashsentry_uninstall(~crashTypes); crashTypes = kscrashsentry_installWithContext(&context->crash, crashTypes, kscrash_i_onCrash); } return crashTypes; }
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); } }
/** Called when a crash occurs. * * This function gets passed as a callback to a crash handler. */ void kscrash_i_onCrash(void) { KSLOG_DEBUG("Updating application state to note crash."); kscrashstate_notifyAppCrash(); KSCrash_Context* context = crashContext(); if(context->config.printTraceToStdout) { kscrashreport_logCrash(context); } if(context->crash.crashedDuringCrashHandling) { kscrashreport_writeMinimalReport(context, g_recrashReportFilePath); } else { kscrashreport_writeStandardReport(context, g_crashReportFilePath); } }
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(); }
void kscrash_setCrashNotifyCallback(const KSReportWriteCallback onCrashNotify) { KSLOG_TRACE("Set onCrashNotify to %p", onCrashNotify); crashContext()->config.onCrashNotify = onCrashNotify; }
void kscrash_setReportWhenDebuggerIsAttached(bool reportWhenDebuggerIsAttached) { crashContext()->crash.reportWhenDebuggerIsAttached = reportWhenDebuggerIsAttached; }
void kscrash_setSuspendThreadsForUserReported(bool suspendThreadsForUserReported) { crashContext()->crash.suspendThreadsForUserReported = suspendThreadsForUserReported; }
void kscrash_setIntrospectMemory(bool introspectMemory) { crashContext()->config.introspectionRules.enabled = introspectMemory; }
void kscrash_setSearchQueueNames(bool shouldSearchQueueNames) { crashContext()->config.searchQueueNames = shouldSearchQueueNames; }
void kscrash_setPrintTraceToStdout(bool printTraceToStdout) { crashContext()->config.printTraceToStdout = printTraceToStdout; }
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; }