Skip to content

Commit

Permalink
Merge pull request #342 from simaoseica-dd/feature/dynamic_size
Browse files Browse the repository at this point in the history
Make crash report size configurable
  • Loading branch information
simaoseica-dd authored Feb 20, 2025
1 parent c071db4 commit 58ddf9e
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* **[Improvement]** Update target iOS and tvOS version to 12.0.
* **[Improvement]** Support Xcode 16 build.
* **[Fix]** Fix endless loop when PLCrashReporter was set up during `load`.
* **[Improvement]** Make crash report size configurable.

___

Expand Down
7 changes: 4 additions & 3 deletions Source/PLCrashReport.m
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,11 @@ - (Plcrash__CrashReport *) decodeCrashData: (NSData *) data error: (NSError **)
return NULL;
}

Plcrash__CrashReport *crashReport = plcrash__crash_report__unpack(NULL, [data length] - sizeof(struct PLCrashReportFileHeader), header->data);
NSUInteger stackTraceSize = [data length] - sizeof(struct PLCrashReportFileHeader);
Plcrash__CrashReport *crashReport = plcrash__crash_report__unpack(NULL, stackTraceSize, header->data);
if (crashReport == NULL) {
populate_nserror(outError, PLCrashReporterErrorCrashReportInvalid, NSLocalizedString(@"An unknown error occured decoding the crash report",
@"Crash log decoding error message"));
populate_nserror(outError, PLCrashReporterErrorCrashReportInvalid, [NSString stringWithFormat: NSLocalizedString(@"Could not decode crash report with size of %lu bytes.",
@"Crash log decoding error message"), stackTraceSize]);
return NULL;
}

Expand Down
20 changes: 6 additions & 14 deletions Source/PLCrashReporter.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,6 @@
* Directory containing crash reports queued for sending. */
static NSString *PLCRASH_QUEUED_DIR = @"queued_reports";

/** @internal
* Maximum number of bytes that will be written to the crash report.
* Used as a safety measure in case of implementation malfunction.
*
* We provide for a generous 1m here. Most crash reports
* are approximately 7k, however, we've seen 283k report
* generated by an app that loads a pathologically
* large number (~1080) of shared libraries.
*/
#define MAX_REPORT_BYTES (1024 * 1024)

/**
* @internal
* Fatal signals to be monitored.
Expand Down Expand Up @@ -103,6 +92,9 @@
/** Path to the output file */
const char *path;

/** Maximum number of bytes that will be written to the crash report. */
NSUInteger max_report_size;

#if PLCRASH_FEATURE_MACH_EXCEPTIONS
/* Previously registered Mach exception ports, if any. Will be left uninitialized if PLCrashReporterSignalHandlerTypeMach
* is not enabled. */
Expand Down Expand Up @@ -159,8 +151,8 @@ static plcrash_error_t plcrash_write_report (plcrashreporter_handler_ctx_t *sigc
}

/* Initialize the output context */
plcrash_async_file_init(&file, fd, MAX_REPORT_BYTES);
plcrash_async_file_init(&file, fd, sigctx->max_report_size);

/* Write the crash log using the already-initialized writer */
err = plcrash_log_writer_write(&sigctx->writer, crashed_thread, &shared_image_list, &file, siginfo, thread_state);

Expand Down Expand Up @@ -754,7 +746,7 @@ - (NSData *) generateLiveReportWithThread: (thread_t) thread exception: (NSExcep

/* Initialize the output context */
plcrash_log_writer_init(&writer, _applicationIdentifier, _applicationVersion, _applicationMarketingVersion, [self mapToAsyncSymbolicationStrategy: _config.symbolicationStrategy], true);
plcrash_async_file_init(&file, fd, MAX_REPORT_BYTES);
plcrash_async_file_init(&file, fd, _config.maxReportBytes);

/* Set custom data, if already set before enabling */
if (self.customData != nil) {
Expand Down
11 changes: 10 additions & 1 deletion Source/PLCrashReporterConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) {
shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler
basePath: (NSString *) basePath;

- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler
basePath: (NSString *) basePath
maxReportBytes: (NSUInteger) maxReportByte;

/** The base path to save the crash data. */
@property(nonatomic, readonly) NSString *basePath;

Expand All @@ -177,8 +183,11 @@ typedef NS_OPTIONS(NSUInteger, PLCrashReporterSymbolicationStrategy) {
/** The configured symbolication strategy. */
@property(nonatomic, readonly) PLCrashReporterSymbolicationStrategy symbolicationStrategy;

/** Should PLCrashReporter regiser an uncaught exception handler? This is entended to be used in Xamarin apps */
/** Should PLCrashReporter register an uncaught exception handler? This is intended to be used in Xamarin apps */
@property(nonatomic, readonly) BOOL shouldRegisterUncaughtExceptionHandler;

/** Maximum number of bytes that will be written to the crash report */
@property(nonatomic, readonly) NSUInteger maxReportBytes;

@end

66 changes: 57 additions & 9 deletions Source/PLCrashReporterConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@
#import "PLCrashReporterConfig.h"
#endif

/** @internal
* Maximum number of bytes that will be written to the crash report
* when not explicitly defined in PLCrashReporterConfig.
*
* Default: 1MB.
*
* For reference, most crash reports
* are approximately 7k, however, we've seen 283k report
* generated by an app that loads a pathologically
* large number (~1080) of shared libraries.
*/
#define MAX_REPORT_BYTES (1024 * 1024)

/**
* Crash Reporter Configuration.
*
Expand All @@ -41,20 +54,28 @@ @implementation PLCrashReporterConfig {

/** The configured signal handler type. */
PLCrashReporterSignalHandlerType _signalHandlerType;

/** The configured symbolication strategy. */
PLCrashReporterSymbolicationStrategy _symbolicationStrategy;

/**
* Flag indicating if the uncaughtExceptionHandler should be initialized or not. It usually is, except in a
* Xamarin environment.
*/
BOOL _shouldRegisterUncaughtExceptionHandler;

/**
* Flag indicating if the uncaughtExceptionHandler should be initialized or not. It usually is, except in a
* Xamarin environment.
*/
BOOL _shouldRegisterUncaughtExceptionHandler;

/**
* Maximum number of bytes that will be written to the crash report.
*
* If not provided, the default value will be MAX_REPORT_BYTES (1MB).
*/
NSUInteger _maxReportBytes;
}

@synthesize signalHandlerType = _signalHandlerType;
@synthesize symbolicationStrategy = _symbolicationStrategy;
@synthesize shouldRegisterUncaughtExceptionHandler = _shouldRegisterUncaughtExceptionHandler;
@synthesize maxReportBytes = _maxReportBytes;

/**
* Return the default local configuration.
Expand Down Expand Up @@ -119,7 +140,11 @@ - (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) s
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler
{
return [self initWithSignalHandlerType: signalHandlerType symbolicationStrategy: symbolicationStrategy shouldRegisterUncaughtExceptionHandler: shouldRegisterUncaughtExceptionHandler basePath: nil];
return [self initWithSignalHandlerType: signalHandlerType
symbolicationStrategy: symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: shouldRegisterUncaughtExceptionHandler
basePath: nil
maxReportBytes: MAX_REPORT_BYTES];
}

/**
Expand All @@ -134,6 +159,28 @@ - (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) s
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler
basePath: (NSString *) basePath
{
return [self initWithSignalHandlerType: signalHandlerType
symbolicationStrategy: symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: shouldRegisterUncaughtExceptionHandler
basePath: basePath
maxReportBytes: MAX_REPORT_BYTES];
}

/**
* Initialize a new PLCrashReporterConfig instance.
*
* @param signalHandlerType The requested signal handler type.
* @param symbolicationStrategy A local symbolication strategy.
* @param shouldRegisterUncaughtExceptionHandler Flag indicating if an uncaught exception handler should be set.
* @param basePath The base path to save the crash data. May be nil.
* @param maxReportBytes Maximum number of bytes that will be written to the crash report.
*/
- (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) signalHandlerType
symbolicationStrategy: (PLCrashReporterSymbolicationStrategy) symbolicationStrategy
shouldRegisterUncaughtExceptionHandler: (BOOL) shouldRegisterUncaughtExceptionHandler
basePath: (NSString *) basePath
maxReportBytes: (NSUInteger) maxReportBytes
{
if ((self = [super init]) == nil)
return nil;
Expand All @@ -142,7 +189,8 @@ - (instancetype) initWithSignalHandlerType: (PLCrashReporterSignalHandlerType) s
_symbolicationStrategy = symbolicationStrategy;
_shouldRegisterUncaughtExceptionHandler = shouldRegisterUncaughtExceptionHandler;
_basePath = basePath;

_maxReportBytes = maxReportBytes;

return self;
}

Expand Down
38 changes: 38 additions & 0 deletions Tests/PLCrashReporterTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,44 @@ - (void) testGenerateLiveReportWithException {
STAssertEqualStrings(report.exceptionInfo.exceptionReason, exc.reason, @"Incorrect exception reason");
}

/**
* Test generation of a 'live' crash report that exceeds maxReportBytes.
*/
- (void) testGenerateLiveReportExceedingMaxReportBytes {
NSError *error;
NSData *reportData;
plcrash_test_thread_t thr;
NSUInteger maxReportBytes = 1024;

/* Spawn a thread and generate a report for it */
plcrash_test_thread_spawn(&thr);

NSException *exc = [NSException exceptionWithName: NSInvalidArgumentException reason: @"Testing" userInfo: nil];

/* CrashReporter config with maximum 1024 bytes to write the crash report */
PLCrashReporterConfig * config = [[PLCrashReporterConfig alloc] initWithSignalHandlerType: PLCrashReporterSignalHandlerTypeBSD
symbolicationStrategy: PLCrashReporterSymbolicationStrategyNone
shouldRegisterUncaughtExceptionHandler: YES
basePath: nil
maxReportBytes: maxReportBytes];

PLCrashReporter *reporter = [[PLCrashReporter alloc] initWithConfiguration: config];

/* The report exceeds the 1024 bytes defined in the config. */
reportData = [reporter generateLiveReportWithThread: pthread_mach_thread_np(thr.thread)
exception: exc
error: &error];
plcrash_test_thread_stop(&thr);
STAssertNotNil(reportData, @"Failed to generate live report: %@", error);

/* Try parsing the result */
PLCrashReport *report = [[PLCrashReport alloc] initWithData: reportData error: &error];
STAssertNil(report, @"Could not parse generated live report: %@", error);

NSString* errorDescription = [NSString stringWithFormat: @"Could not decode crash report with size of %lu bytes.", (maxReportBytes - sizeof(struct PLCrashReportFileHeader))];
STAssertEqualStrings([error localizedDescription], errorDescription, @"Error is not about exceeding MaxReportBytes");
}

/**
* Test generation of a 'live' crash report for a specific thread.
*/
Expand Down

0 comments on commit 58ddf9e

Please sign in to comment.