Update to custom PLCrashReporter v1.2 Beta 1 build with BIT namespace

- Adjust to new PLCR initialization scheme
- Don't enable PLCR if the app is starting with a debugger attached
- Add option to enable Mach exception handler. It is strongly discouraged to use this in release builds!
This commit is contained in:
Andreas Linde
2013-08-31 13:19:29 +02:00
parent 43fbe3bf65
commit 53d164d25d
10 changed files with 578 additions and 87 deletions

View File

@@ -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

View File

@@ -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];

View File

@@ -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:

View File

@@ -0,0 +1,105 @@
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* 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 <TargetConditionals.h>
/**
* @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 */

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -0,0 +1,48 @@
/*
* Author: Landon Fuller <landonf@plausiblelabs.com>
*
* 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 <Foundation/Foundation.h>
@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

View File

@@ -29,6 +29,11 @@
#import <Foundation/Foundation.h>
#import <mach/mach.h>
#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;

View File

@@ -0,0 +1,165 @@
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* 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 <Foundation/Foundation.h>
#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 <em>always</em> 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 '\<redacted>' 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