diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index c13fd5ec08..dc49cff33d 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -84,6 +84,8 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { More background information on this topic can be found in the following blog post by Landon Fuller, the developer of [PLCrashReporter](https://www.plcrashreporter.org), about writing reliable and safe crash reporting: [Reliable Crash Reporting](http://goo.gl/WvTBR) + + @warning If you start the app with the Xcode debugger attached, detecting crashes will _NOT_ be enabled! */ @interface BITCrashManager : BITHockeyBaseManager @@ -127,6 +129,27 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, assign) BITCrashManagerStatus crashManagerStatus; +/** + * Trap fatal signals via a Mach exception server. + * + * By default the SDK is using the safe and proven in-process BSD Signals for catching crashes. + * This option provides an option to enable catching fatal signals via a Mach exception server + * instead. + * + * We strongly advice _NOT_ to enable Mach exception handler in release versions of your apps! + * + * Default: _NO_ + * + * @warning The Mach exception handler executes in-process, and will interfere with debuggers when + * they attempt to suspend all active threads (which will include the Mach exception handler). + * Mach-based handling should _NOT_ be used when a debugger is attached. The SDK will not + * enabled catching exceptions if the app is started with the debugger running. If you attach + * the debugger during runtime, this may cause issues the Mach exception handler is enabled! + * @see isDebuggerAttached + */ +@property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler; + + /** Flag that determines if an "Always" option should be shown @@ -179,4 +202,19 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { */ @property (nonatomic, readonly) NSTimeInterval timeintervalCrashInLastSessionOccured; + +///----------------------------------------------------------------------------- +/// @name Helper +///----------------------------------------------------------------------------- + +/** + * Detect if a debugger is attached to the app process + * + * This is only invoked once on app startup and can not detect if the debugger is being + * attached during runtime! + * + * @return BOOL if the debugger is attached on app startup + */ +- (BOOL)isDebuggerAttached; + @end diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 12ef9d4908..cf5922d105 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -78,6 +78,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; BOOL _sendingInProgress; BOOL _isSetup; + BITPLCrashReporter *_plCrashReporter; NSUncaughtExceptionHandler *_exceptionHandler; } @@ -88,6 +89,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _showAlwaysButton = NO; _isSetup = NO; + _plCrashReporter = nil; _exceptionHandler = nil; _crashIdenticalCurrentVersion = YES; @@ -162,34 +164,6 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; #pragma mark - Private -/** - * Check if the debugger is attached - * - * Taken from https://github.com/plausiblelabs/plcrashreporter/blob/2dd862ce049e6f43feb355308dfc710f3af54c4d/Source/Crash%20Demo/main.m#L96 - * - * @return `YES` if the debugger is attached to the current process, `NO` otherwise - */ -static bool isDebuggerAttached (void) { - struct kinfo_proc info; - size_t info_size = sizeof(info); - int name[4]; - - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_PID; - name[3] = getpid(); - - if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) { - NSLog(@"sysctl() failed: %s", strerror(errno)); - return false; - } - - if ((info.kp_proc.p_flag & P_TRACED) != 0) - return true; - - return false; -} - /** * Save all settings * @@ -345,6 +319,43 @@ static bool isDebuggerAttached (void) { return useremail; } + +#pragma mark - Public + +/** + * Check if the debugger is attached + * + * Taken from https://github.com/plausiblelabs/plcrashreporter/blob/2dd862ce049e6f43feb355308dfc710f3af54c4d/Source/Crash%20Demo/main.m#L96 + * + * @return `YES` if the debugger is attached to the current process, `NO` otherwise + */ +- (BOOL)isDebuggerAttached { + static BOOL debuggerIsAttached = NO; + + static dispatch_once_t debuggerPredicate; + dispatch_once(&debuggerPredicate, ^{ + struct kinfo_proc info; + size_t info_size = sizeof(info); + int name[4]; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PID; + name[3] = getpid(); + + if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) { + NSLog(@"[HockeySDK] ERROR: Checking for a running debugger via sysctl() failed: %s", strerror(errno)); + debuggerIsAttached = false; + } + + if (!debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0) + debuggerIsAttached = true; + }); + + return debuggerIsAttached; +} + + #pragma mark - PLCrashReporter /** @@ -353,9 +364,10 @@ static bool isDebuggerAttached (void) { * Parse the new crash report and gather additional meta data from the app which will be stored along the crash report */ - (void) handleCrashReport { - BITPLCrashReporter *crashReporter = [BITPLCrashReporter sharedReporter]; NSError *error = NULL; + if (!_plCrashReporter) return; + [self loadSettings]; // check if the next call ran successfully the last time @@ -366,7 +378,7 @@ static bool isDebuggerAttached (void) { [self saveSettings]; // Try loading the crash report - NSData *crashData = [[NSData alloc] initWithData:[crashReporter loadPendingCrashReportDataAndReturnError: &error]]; + NSData *crashData = [[NSData alloc] initWithData:[_plCrashReporter loadPendingCrashReportDataAndReturnError: &error]]; NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; @@ -421,7 +433,7 @@ static bool isDebuggerAttached (void) { [self saveSettings]; - [crashReporter purgePendingCrashReport]; + [_plCrashReporter purgePendingCrashReport]; } /** @@ -555,68 +567,75 @@ static bool isDebuggerAttached (void) { if (_crashManagerStatus == BITCrashManagerStatusDisabled) return; if (!_isSetup) { - BITPLCrashReporter *crashReporter = [BITPLCrashReporter sharedReporter]; - - // Check if we previously crashed - if ([crashReporter hasPendingCrashReport]) { - _didCrashInLastSession = YES; - [self handleCrashReport]; - } - - // Don't enable PLCrashReporter exception and signal handlers if we are already running with the debugger - // attached. This will not solve the debugger being attached during runtime and then catching all the - // exceptions before PLCrashReporter has a chance to do so. - // We only check for this if we are not in the App Store environment - - BOOL debuggerIsAttached = NO; - if (![self isAppStoreEnvironment]) { - if (isDebuggerAttached()) { - debuggerIsAttached = YES; - NSLog(@"[HockeSDK] WARNING: This app is running with the debugger being attached. Catching crashes is de-activated!"); + static dispatch_once_t plcrPredicate; + dispatch_once(&plcrPredicate, ^{ + /* Configure our reporter */ + + PLCrashReporterSignalHandlerType signalHandlerType = PLCrashReporterSignalHandlerTypeBSD; + if (self.isMachExceptionHandlerEnabled) { + signalHandlerType = PLCrashReporterSignalHandlerTypeMach; } - } - - if (!debuggerIsAttached) { - // Multiple exception handlers can be set, but we can only query the top level error handler (uncaught exception handler). - // - // To check if PLCrashReporter's error handler is successfully added, we compare the top - // level one that is set before and the one after PLCrashReporter sets up its own. - // - // With delayed processing we can then check if another error handler was set up afterwards - // and can show a debug warning log message, that the dev has to make sure the "newer" error handler - // doesn't exit the process itself, because then all subsequent handlers would never be invoked. - // - // Note: ANY error handler setup BEFORE HockeySDK initialization will not be processed! + BITPLCrashReporterConfig *config = [[BITPLCrashReporterConfig alloc] initWithSignalHandlerType: signalHandlerType + symbolicationStrategy: PLCrashReporterSymbolicationStrategyAll]; + _plCrashReporter = [[BITPLCrashReporter alloc] initWithConfiguration: config]; - // get the current top level error handler - NSUncaughtExceptionHandler *initialHandler = NSGetUncaughtExceptionHandler(); + // Check if we previously crashed + if ([_plCrashReporter hasPendingCrashReport]) { + _didCrashInLastSession = YES; + [self handleCrashReport]; + } - // PLCrashReporter may only be initialized once. So make sure the developer - // can't break this - static dispatch_once_t plcrPredicate; - dispatch_once(&plcrPredicate, ^{ + // The actual signal and mach handlers are only registered when invoking `enableCrashReporterAndReturnError` + // So it is safe enough to only disable the following part when a debugger is attached no matter which + // signal handler type is set + // We only check for this if we are not in the App Store environment + + BOOL debuggerIsAttached = NO; + if (![self isAppStoreEnvironment]) { + if ([self isDebuggerAttached]) { + debuggerIsAttached = YES; + NSLog(@"[HockeySDK] WARNING: Detecting crashes is NOT enabled due to running the app with a debugger attached."); + } + } + + if (!debuggerIsAttached) { + // Multiple exception handlers can be set, but we can only query the top level error handler (uncaught exception handler). + // + // To check if PLCrashReporter's error handler is successfully added, we compare the top + // level one that is set before and the one after PLCrashReporter sets up its own. + // + // With delayed processing we can then check if another error handler was set up afterwards + // and can show a debug warning log message, that the dev has to make sure the "newer" error handler + // doesn't exit the process itself, because then all subsequent handlers would never be invoked. + // + // Note: ANY error handler setup BEFORE HockeySDK initialization will not be processed! + + // get the current top level error handler + NSUncaughtExceptionHandler *initialHandler = NSGetUncaughtExceptionHandler(); + + // PLCrashReporter may only be initialized once. So make sure the developer + // can't break this NSError *error = NULL; // Enable the Crash Reporter - if (![crashReporter enableCrashReporterAndReturnError: &error]) + if (![_plCrashReporter enableCrashReporterAndReturnError: &error]) NSLog(@"[HockeySDK] WARNING: Could not enable crash reporter: %@", [error localizedDescription]); - }); - - // get the new current top level error handler, which should now be the one from PLCrashReporter - NSUncaughtExceptionHandler *currentHandler = NSGetUncaughtExceptionHandler(); - - // do we have a new top level error handler? then we were successful - if (currentHandler && currentHandler != initialHandler) { - _exceptionHandler = currentHandler; - BITHockeyLog(@"INFO: Exception handler successfully initialized."); - } else { - // this should never happen, theoretically only if NSSetUncaugtExceptionHandler() has some internal issues - NSLog(@"[HockeySDK] ERROR: Exception handler could not be set. Make sure there is no other exception handler set up!"); + // get the new current top level error handler, which should now be the one from PLCrashReporter + NSUncaughtExceptionHandler *currentHandler = NSGetUncaughtExceptionHandler(); + + // do we have a new top level error handler? then we were successful + if (currentHandler && currentHandler != initialHandler) { + _exceptionHandler = currentHandler; + + BITHockeyLog(@"INFO: Exception handler successfully initialized."); + } else { + // this should never happen, theoretically only if NSSetUncaugtExceptionHandler() has some internal issues + NSLog(@"[HockeySDK] ERROR: Exception handler could not be set. Make sure there is no other exception handler set up!"); + } } - _isSetup = YES; - } + }); } [self performSelector:@selector(invokeDelayedProcessing) withObject:nil afterDelay:0.5]; diff --git a/Vendor/CrashReporter.framework/Versions/A/CrashReporter b/Vendor/CrashReporter.framework/Versions/A/CrashReporter index eb5535c92f..69bbf24d20 100644 Binary files a/Vendor/CrashReporter.framework/Versions/A/CrashReporter and b/Vendor/CrashReporter.framework/Versions/A/CrashReporter differ diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h b/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h index 778d7f9658..faef905806 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h @@ -34,7 +34,7 @@ // This must be included before any other PLCrashReporter includes, as // it redefines symbol names -#import "PLCrashReporterNamespace.h" +#import "PLCrashNamespace.h" #import "PLCrashReporter.h" #import "PLCrashReport.h" @@ -93,6 +93,9 @@ typedef enum { /** The crash report log file is corrupt or invalid */ PLCrashReporterErrorCrashReportInvalid = 2, + + /** An attempt to use a resource which was in use at the time in a manner which would have conflicted with the request. */ + PLCrashReporterErrorResourceBusy = 3 } PLCrashReporterError; @@ -233,7 +236,65 @@ typedef enum { */ /** - * @page mach_exceptions Mach Exceptions on iOS + * @page mach_exceptions Mach Exceptions on Mac OS X and iOS + * + * PLCrashReporter includes support for monitoring crashes via an in-process Mach exception handler. On Mac OS X, the + * Mach exception implementation is fully supported using entirely public API. On iOS, the APIs required are not fully + * public -- more details on the implications of this for exception handling on iOS may be found in + * @ref mach_exceptions_ios below. + * + * It is worth noting that even where the Mach exception APIs are fully supported, kernel-internal constants, as well + * as architecture-specific trap information, may be required to fully interpret a Mach exception's root cause. + * + * For example, the EXC_SOFTWARE exception is dispatched for four different failure types, using the exception + * code to differentiate failure types: + * - Non-existent system call invoked (SIGSYS) + * - Write on a pipe with no reader (SIGPIPE) + * - Abort program (SIGABRT) + * - Kill program (SIGKILL) + * + * Of those four types, only the constant required to interpret the SIGKILL behavior (EXC_SOFT_SIGNAL) is publicly defined. + * Of the remaining three failure types, the constant values are kernel implementation-private, defined only in the available + * kernel sources. On iOS, these sources are unavailable, and while they generally do match the Mac OS X implementation, there + * are no gaurantees that this is -- or will remain -- the case in the future. + * + * Likewise, interpretation of particular fault types requires information regarding the underlying machine traps + * that triggered the Mach exceptions. For example, a floating point trap on x86/x86-64 will trigger an EXC_ARITHMETIC, + * with a subcode value containing the value of the FPU status register. Determining the exact FPU cause requires + * extracting the actual exception flags from status register as per the x86 architecture documentation. The exact format + * of this subcode value is not actually documented outside the kernel, and may change in future releases. + * + * While we have the advantage of access to the x86 kernel sources, the situation on ARM is even less clear. The actual + * use of the Mach exception codes and subcodes is largely undefined by both headers and publicly available documentation, + * and the available x86 kernel sources are of little use in interpreting this data. + * + * As such, while Mach exceptions may catch some cases that BSD signals can not, they are not a perfect solution, + * and may also provide less insight into the actual failures that occur. By comparison, the BSD signal interface + * is both fully defined and architecture independent, with any necessary interpretation of the Mach exception + * codes handled in-kernel at the time of exception dispatch. It is generally recommended by Apple as the preferred + * interface, and should generally be preferred by PLCrashReporter API clients. + * + * @section mach_exceptions_compatibility Compatibility Issues + * + * @subsection Debuggers + * + * Enabling in-process Mach exception handlers will conflict with any attached debuggers; the debugger + * may suspend the processes Mach exception handling thread, which will result in any exception messages + * sent via the debugger being lost, as the in-process handler will be unable to receive and forward + * the messages. + * + * @subsection Managed Runtimes (Xamarin, Unity) + * + * A Mach exception handler may conflict with any managed runtime that registers a BSD signal handler that + * can safely handle otherwise fatal signals, allowing execution to proceed. This includes products + * such as Xamarin for iOS. + * + * In such a case, PLCrashReporter will write a crash report for non-fatal signals, as there is no + * immediate mechanism for determining whether a signal handler exists and that it can safely + * handle the failure. This can result in unexpected delays in application execution, increased I/O to + * disk, and other undesirable operations. + * + * @section mach_exceptions_ios Mach Exceptions on iOS * * The APIs required for Mach exception handling are not fully public on iOS. Unfortunately, there are a number * of crash states that can only be handled with Mach exception handlers: diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h new file mode 100644 index 0000000000..6d9e16c4cb --- /dev/null +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashFeatureConfig.h @@ -0,0 +1,105 @@ +/* + * Author: Landon Fuller + * + * Copyright (c) 2012-2013 Plausible Labs Cooperative, Inc. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PLCRASH_FEATURE_CONFIG_H +#define PLCRASH_FEATURE_CONFIG_H + +#include + +/** + * @internal + * + * Build-time configuration for PLCrashReporter. + * + * This is used to automatically enable/disable features on a per-platform and per-configuration + * basis; it may also be used by third-party vendors to configure a custom build of PLCrashReporter. + * + * @defgroup build_config Build Configuration + * @ingroup constants + * @{ + */ + +/* + * Defaults + */ + +/* + * For release builds, disable unused unwind implementations on targets that do not use them. For non-release + * builds, we include the unwind implementations to allow testing on a broader range of targets; it's possible + * that both compact and/or DWARF unwinding could be implemented by Apple for iOS/ARM in the future. + */ +#ifdef PLCF_RELEASE_BUILD +# if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR +# ifndef PLCRASH_FEATURE_UNWIND_DWARF +# define PLCRASH_FEATURE_UNWIND_DWARF 0 +# endif +# ifndef PLCRASH_FEATURE_UNWIND_COMPACT +# define PLCRASH_FEATURE_UNWIND_COMPACT 0 +# endif +# endif +#endif + +/* + * Configuration Flags + */ + + +#ifndef PLCRASH_FEATURE_MACH_EXCEPTIONS +/** + * If true, enable Mach exception support. On Mac OS X, the Mach exception implementation is fully supported, + * using publicly available API. On iOS, the APIs required for a complete implementation are not public. However, a + * popular commercial crash reporter is now shipping with support for Mach exceptions, which implies that either + * they've received special dispensation to use private APIs / private structures, they've found another way to do + * it, or they're just using undocumented functionality and hoping for the best. + * + * The exposed surface of undocumented API usage is relatively low, and there has been strong user demand to + * implement Mach exception handling regardless of concerns over API visiblity. Given this, we've enabled + * Mach exception handling by default, and provided both build-time and runtime configuration + * to disable its use. + * + * For more information on the potential issues with enabling mach exception support, @sa @ref mach_exceptions. + */ +# define PLCRASH_FEATURE_MACH_EXCEPTIONS 1 +#endif + +#ifndef PLCRASH_FEATURE_UNWIND_DWARF +/** If true, enable DWARF unwinding support. DWARF unwinding is currently only used by Mac OS X. */ +# define PLCRASH_FEATURE_UNWIND_DWARF 1 +#endif + + +#ifndef PLCRASH_FEATURE_UNWIND_COMPACT +/** If true, enable compact unwinding support. This is only used by Mac OS X. */ +# define PLCRASH_FEATURE_UNWIND_COMPACT 1 +#endif + +/** + * @} + */ + +#endif /* PLCRASH_FEATURE_CONFIG_H */ diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterNamespace.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h similarity index 82% rename from Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterNamespace.h rename to Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h index 3d08220a7d..a4477b1a88 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterNamespace.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashNamespace.h @@ -35,6 +35,7 @@ * This may be used to avoid symbol conflicts between multiple libraries * that may both incorporate PLCrashReporter. */ +// #define PLCRASHREPORTER_PREFIX AcmeCo #define PLCRASHREPORTER_PREFIX BIT #ifdef PLCRASHREPORTER_PREFIX @@ -66,5 +67,14 @@ #define PLCrashReportHostOperatingSystem PLNS(PLCrashReportHostOperatingSystem) #define PLCrashReporterErrorDomain PLNS(PLCrashReporterErrorDomain) #define PLCrashReporterException PLNS(PLCrashReporterException) +#define PLCrashHostInfo PLNS(PLCrashHostInfo) +#define PLCrashMachExceptionPort PLNS(PLCrashMachExceptionPort) +#define PLCrashMachExceptionPortSet PLNS(PLCrashMachExceptionPortSet) +#define PLCrashProcessInfo PLNS(PLCrashProcessInfo) +#define PLCrashReporterConfig PLNS(PLCrashReporterConfig) +#define PLCrashUncaughtExceptionHandler PLNS(PLCrashUncaughtExceptionHandler) +#define PLCrashMachExceptionForward PLNS(PLCrashMachExceptionForward) +#define PLCrashSignalHandlerForward PLNS(PLCrashSignalHandlerForward) +#define plcrash_signal_handler PLNS(plcrash_signal_handler) #endif diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReport.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReport.h index 792819b4a8..a179cd6909 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReport.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReport.h @@ -32,6 +32,7 @@ #import "PLCrashReportBinaryImageInfo.h" #import "PLCrashReportExceptionInfo.h" #import "PLCrashReportMachineInfo.h" +#import "PLCrashReportMachExceptionInfo.h" #import "PLCrashReportProcessInfo.h" #import "PLCrashReportProcessorInfo.h" #import "PLCrashReportRegisterInfo.h" @@ -98,6 +99,9 @@ typedef struct _PLCrashReportDecoder _PLCrashReportDecoder; /** Signal info */ PLCrashReportSignalInfo *_signalInfo; + + /** Mach exception info */ + PLCrashReportMachExceptionInfo *_machExceptionInfo; /** Thread info (PLCrashReportThreadInfo instances) */ NSArray *_threads; @@ -153,6 +157,18 @@ typedef struct _PLCrashReportDecoder _PLCrashReportDecoder; */ @property(nonatomic, readonly) PLCrashReportSignalInfo *signalInfo; +/** + * Mach exception information, if available. This will only be included in the + * case that encoding crash reporter's exception-based reporting was enabled, and a Mach + * exception was caught. + * + * @warning If Mach exception information is available, the legacy signalInfo property will also be provided; this + * s required to maintain backwards compatibility with the established API. Note, however, that the signal info may be derived from the + * Mach exception info by the encoding crash reporter, and thus may not exactly match the kernel exception-to-signal + * mappings implemented in xnu. As such, when Mach exception info is available, its use should be preferred. + */ +@property(nonatomic, readonly) PLCrashReportMachExceptionInfo *machExceptionInfo; + /** * Thread information. Returns a list of PLCrashReportThreadInfo instances. */ diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportMachExceptionInfo.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportMachExceptionInfo.h new file mode 100644 index 0000000000..a9d7f7c245 --- /dev/null +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportMachExceptionInfo.h @@ -0,0 +1,48 @@ +/* + * Author: Landon Fuller + * + * Copyright (c) 2013 Plausible Labs Cooperative, Inc. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface PLCrashReportMachExceptionInfo : NSObject { +@private + /** The Mach exception type. */ + uint64_t _type; + + /** The Mach exception codes, represented as an ordered array of NSNumber instances. */ + NSArray *_codes; +} + +- (id) initWithType: (uint64_t) type codes: (NSArray *) codes; + +/** The Mach exception type. */ +@property(nonatomic, readonly) uint64_t type; + +/** The Mach exception codes, represented as an ordered array of 64-bit unsigned NSNumber instances. */ +@property(nonatomic, readonly) NSArray *codes; + +@end diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h index 8f1bd444a8..88824a6883 100644 --- a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h @@ -29,6 +29,11 @@ #import #import +#import "PLCrashReporterConfig.h" + +@class PLCrashMachExceptionServer; +@class PLCrashMachExceptionPortSet; + /** * @ingroup functions * @@ -60,15 +65,37 @@ typedef struct PLCrashReporterCallbacks { /** An arbitrary user-supplied context value. This value may be NULL. */ void *context; - /** The callback used to report caught signal information. In version 0 of this structure, all crashes will be - * reported via this function. */ + /** + * The callback used to report caught signal information. In version 0 of this structure, all crashes will be + * reported via this function. + * + * @warning When using PLCrashReporterSignalHandlerTypeMach, the siginfo_t argument to this function will be derived + * from the Mach exception data, and may be incorrect, or may otherwise not match the expected data as provided via + * PLCrashReporterSignalHandlerTypeBSD. In addition, the provided ucontext_t value will be zero-initialized, and will + * not provide valid thread state. + * + * This callback will be deprecated in favor of a Mach-compatible replacement in a future release; support is maintained + * here to allow clients that rely on post-crash callbacks without thread state to make use of Mach exceptions. + */ PLCrashReporterPostCrashSignalCallback handleSignal; } PLCrashReporterCallbacks; @interface PLCrashReporter : NSObject { @private + /** Reporter configuration */ + PLCrashReporterConfig *_config; + /** YES if the crash reporter has been enabled */ BOOL _enabled; + +#if PLCRASH_FEATURE_MACH_EXCEPTIONS + /** The backing Mach exception server, if any. Nil if the reporter has not been enabled, or if + * the configured signal handler type is not PLCrashReporterSignalHandlerTypeMach. */ + PLCrashMachExceptionServer *_machServer; + + /** Previously registered Mach exception ports, if any. */ + PLCrashMachExceptionPortSet *_previousMachPorts; +#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */ /** Application identifier */ NSString *_applicationIdentifier; @@ -82,6 +109,8 @@ typedef struct PLCrashReporterCallbacks { + (PLCrashReporter *) sharedReporter; +- (instancetype) initWithConfiguration: (PLCrashReporterConfig *) config; + - (BOOL) hasPendingCrashReport; - (NSData *) loadPendingCrashReportData; diff --git a/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterConfig.h b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterConfig.h new file mode 100644 index 0000000000..82f0bd9cf2 --- /dev/null +++ b/Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporterConfig.h @@ -0,0 +1,165 @@ +/* + * Author: Landon Fuller + * + * Copyright (c) 2013 Plausible Labs Cooperative, Inc. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import "PLCrashFeatureConfig.h" + +/** + * @ingroup enums + * Supported mechanisms for trapping and handling crashes. + */ +typedef NS_ENUM(NSUInteger, PLCrashReporterSignalHandlerType) { + /** + * Trap fatal signals via a sigaction(2)-registered BSD signal handler. + * + * PLCrashReporter's signal handler will supersede previously registered handlers; existing + * handlers will not be called. This behavior may be modified in a future release, and should + * not be relied upon as a mechanism to prevent existing signal handlers from being called. + * + * There are some limitations to signal-based crash handling on Mac OS X and iOS; specifically: + * + * - On Mac OS X, stack overflows will only be handled on the thread on which + * the crash reporter was initialized. This should generally be the main thread. + * - On iOS 6.0 and later, any stack overflows will not be handled due to sigaltstack() being + * non-functional on the device. (see rdar://13002712 - SA_ONSTACK/sigaltstack() ignored on iOS). + * - Some exit paths in Apple's Libc will deregister a signal handler before firing SIGABRT, resulting + * in the signal handler never being called (see rdar://14313497 - ___abort() disables SIGABRT signal + * handlers prior to raising SIGABRT). These __abort()-based checks are: + * - Implemented for unsafe memcpy/strcpy/snprintf C functions. + * - Only enabled when operating on a fixed-width target buffer (in which case the + * compiler rewrites the function calls to the built-in variants, and provides the fixed-width length as an argument). + * - Only trigger in the case that the source data exceeds the size of the fixed width target + * buffer, and the maximum length argument either isn't supplied by the caller (eg, when using strcpy), + * or a too-long argument is supplied (eg, strncpy with a length argument longer than the target buffer), + * AND that argument can't be checked at compile-time. + */ + PLCrashReporterSignalHandlerTypeBSD = 0, + +#if PLCRASH_FEATURE_MACH_EXCEPTIONS + /** + * Trap fatal signals via a Mach exception server. + * + * If any existing Mach exception server has been registered for the task, exceptions will be forwarded to that + * exception handler. Should the exceptions be handled by an existing handler, no report will be generated + * by PLCrashReporter. + * + * @par Mac OS X + * + * On Mac OS X, the Mach exception implementation is fully supported, using publicly available API -- note, + * however, that some kernel-internal constants, as well as architecture-specific trap information, + * may be required to fully interpret a Mach exception's root cause. + * + * @par iOS + * + * On iOS, the APIs required for a complete implementation are not fully public. + * + * The exposed surface of undocumented API usage is relatively low, and there has been strong user demand to + * implement Mach exception handling regardless of concerns over API visiblity. Given this, we've included + * Mach exception handling as an optional feature, with both build-time and runtime configuration + * to disable its inclusion or use, respectively. + * + * @par Debugger Incompatibility + * + * The Mach exception handler executes in-process, and will interfere with debuggers when they attempt to + * suspend all active threads (which will include the Mach exception handler). Mach-based handling + * should not be used when a debugger is attached. + * + * @par More Details + * + * For more information, refer to @ref mach_exceptions. + */ + PLCrashReporterSignalHandlerTypeMach = 1 +#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */ +}; + +/** + * @ingroup enums + * Supported mechanisms for performing local symbolication. + * + * Local symbolication is performed using inexact heuristics and symbol data available at runtime; it may + * return information that is incorrect. This may still be useful in the case where DWARF data is unavailable + * for a given build; in that case, it can provide function and method names (though not line numbers) for a + * crash report that may otherwise be unusable. + * + * Note, however, this comes at the cost of a significant increase in code that must run within the critical + * crash reporting section, where failures may result in crash reports being corrupted or left unwritten. In + * addition, some of the provided symbolication strategies rely on knowledge of runtime internals that may + * change in future iOS releases. Given that DWARF symbolication data will always be more accurate, and + * the risks inherent in executing considerably more code at crash time, it is strongly recommended that local + * symbolication only be enabled for non-release builds. + * + * Multiple symbolication strategies may be enabled, in which case a best-match heuristic will be applied to the + * results. + */ +typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) { + /** No symbolication. */ + PLCrashReporterSymbolicationStrategyNone = 0, + + /** + * Use the standard binary symbol table. On iOS, this alone will return + * incomplete results, as most symbols are rewritten to the common '\' string. + */ + PLCrashReporterSymbolicationStrategySymbolTable = 1 << 0, + + /** + * Use Objective-C metadata to find method and class names. This relies on detailed parsing + * of the Objective-C runtime data, including undefined flags and other runtime internals. As such, + * it may return incorrect data should the runtime be changed incompatibly. + */ + PLCrashReporterSymbolicationStrategyObjC = 1 << 1, + + /** + * Enable all available symbolication strategies. + */ + PLCrashReporterSymbolicationStrategyAll = (PLCrashReporterSymbolicationStrategySymbolTable|PLCrashReporterSymbolicationStrategyObjC) +}; + +@interface PLCrashReporterConfig : NSObject { +@private + /** The configured signal handler type. */ + PLCrashReporterSignalHandlerType _signalHandlerType; + + /** The configured symbolication strategy. */ + PLCrashReporterSymbolicationStrategy _symbolicationStrategy; +} + ++ (instancetype) defaultConfiguration; + +- (instancetype) init; +- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType + symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy; + +/** The configured signal handler type. */ +@property(nonatomic, readonly) PLCrashReporterSignalHandlerType signalHandlerType; + +/** The configured symbolication strategy. */ +@property(nonatomic, readonly) PLCrashReporterSymbolicationStrategy symbolicationStrategy; + + +@end +