diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index 5c7faa06c..26b109220 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -16,7 +16,8 @@ steps: plugins: artifacts#v1.3.0: download: ["features/fixtures/ios/output/iOSTestApp.ipa"] - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=/app/build/iOSTestApp.ipa" @@ -42,7 +43,8 @@ steps: plugins: artifacts#v1.3.0: download: ["features/fixtures/ios/output/iOSTestApp.ipa"] - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=/app/build/iOSTestApp.ipa" @@ -69,7 +71,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -97,7 +100,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -125,7 +129,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -153,7 +158,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -183,7 +189,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -213,7 +220,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" diff --git a/.buildkite/pipeline.quick.yml b/.buildkite/pipeline.quick.yml index f938e0592..e26f222f0 100644 --- a/.buildkite/pipeline.quick.yml +++ b/.buildkite/pipeline.quick.yml @@ -144,7 +144,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -172,7 +173,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -200,7 +202,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" @@ -228,7 +231,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "--app=@build/ipa_url.txt" diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index c4b0496e8..a4bbbae57 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -136,7 +136,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "features/barebone_tests.feature" @@ -163,7 +164,8 @@ steps: artifacts#v1.3.0: download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" - docker-compose#v3.3.0: + docker-compose#v3.7.0: + pull: cocoa-maze-runner run: cocoa-maze-runner command: - "features/barebone_tests.feature" diff --git a/.jazzy.yaml b/.jazzy.yaml index 58c2ba066..ece90f9d5 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -2,11 +2,11 @@ author_url: "https://www.bugsnag.com" author: "Bugsnag Inc" clean: false # avoid deleting docs/.git framework_root: "Bugsnag" -github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.15.1/Bugsnag" +github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.15.2/Bugsnag" github_url: "https://github.com/bugsnag/bugsnag-cocoa" hide_documentation_coverage: true module: "Bugsnag" -module_version: "6.15.1" +module_version: "6.15.2" objc: true output: "docs" readme: "README.md" diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index c5d000204..5b7e4daaf 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.15.1", + "version": "6.15.2", "summary": "The Bugsnag crash reporting framework for Apple platforms.", "homepage": "https://bugsnag.com", "license": "MIT", @@ -9,7 +9,7 @@ }, "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.15.1" + "tag": "v6.15.2" }, "frameworks": [ "Foundation", diff --git a/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m index c0f1b4f16..1ad20d1c8 100644 --- a/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m +++ b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m @@ -62,6 +62,15 @@ - (BugsnagEvent *)loadEventAndReturnError:(NSError * __autoreleasing *)errorPtr NSMutableDictionary *diagnostics = [NSMutableDictionary dictionary]; diagnostics[@"data"] = [data base64EncodedStringWithOptions:0]; diagnostics[@"file"] = self.file; + NSDictionary *fileAttributes = [NSFileManager.defaultManager attributesOfItemAtPath:self.file error:nil]; + if (fileAttributes) { + NSDate *creationDate = fileAttributes[NSFileCreationDate]; + NSDate *modificationDate = fileAttributes[NSFileModificationDate]; + if (creationDate && modificationDate) { + // The amount of time spent writing the file could indicate why the process never completed + diagnostics[@"modificationInterval"] = @([modificationDate timeIntervalSinceDate:creationDate]); + } + } ReportInternalError(@"JSON parsing error", BSGErrorDescription(error), diagnostics); if (errorPtr) { *errorPtr = error; diff --git a/Bugsnag/Delivery/BSGEventUploadOperation.m b/Bugsnag/Delivery/BSGEventUploadOperation.m index 7fc9c0fd2..6d73a1120 100644 --- a/Bugsnag/Delivery/BSGEventUploadOperation.m +++ b/Bugsnag/Delivery/BSGEventUploadOperation.m @@ -76,8 +76,13 @@ - (void)runWithDelegate:(id)delegate completion return; } + NSDictionary *_Nullable originalPayload = nil; for (BugsnagOnSendErrorBlock block in configuration.onSendBlocks) { @try { + if (!originalPayload) { + // If OnSendError modifies the event and delivery fails, we need to persist the original state of the event. + originalPayload = [event toJsonWithRedactedKeys:configuration.redactedKeys]; + } if (!block(event)) { [self deleteEvent]; completionHandler(); @@ -98,6 +103,11 @@ - (void)runWithDelegate:(id)delegate completion return; } + if ([originalPayload isEqual:eventPayload]) { + // Save memory if payload has not changed + originalPayload = nil; + } + NSString *apiKey = event.apiKey ?: configuration.apiKey; NSMutableDictionary *requestPayload = [NSMutableDictionary dictionary]; @@ -131,7 +141,7 @@ - (void)runWithDelegate:(id)delegate completion case BugsnagApiClientDeliveryStatusFailed: bsg_log_debug(@"Upload failed; will retry event %@", self.name); if (self.shouldStoreEventPayloadForRetry) { - [delegate storeEventPayload:eventPayload]; + [delegate storeEventPayload:originalPayload ?: eventPayload]; } break; diff --git a/Bugsnag/Delivery/BSGEventUploader.m b/Bugsnag/Delivery/BSGEventUploader.m index 095ee2a3d..2b40b3347 100644 --- a/Bugsnag/Delivery/BSGEventUploader.m +++ b/Bugsnag/Delivery/BSGEventUploader.m @@ -11,6 +11,7 @@ #import "BSGEventUploadKSCrashReportOperation.h" #import "BSGEventUploadObjectOperation.h" #import "BSGFileLocations.h" +#import "BSGInternalErrorReporter.h" #import "BSGJSONSerialization.h" #import "BSGUtils.h" #import "BugsnagConfiguration.h" @@ -18,6 +19,10 @@ #import "BugsnagLogger.h" +static NSString * const CrashReportPrefix = @"CrashReport-"; +static NSString * const RecrashReportPrefix = @"RecrashReport-"; + + @interface BSGEventUploader () @property (readonly, nonatomic) NSString *eventsDirectory; @@ -89,6 +94,7 @@ - (void)uploadStoredEvents { } bsg_log_debug(@"Will scan stored events"); [self.scanQueue addOperationWithBlock:^{ + [self processRecrashReports]; NSMutableArray *sortedFiles = [self sortedEventFiles]; [self deleteExcessFiles:sortedFiles]; NSArray *operations = [self uploadOperationsWithFiles:sortedFiles]; @@ -105,6 +111,7 @@ - (void)uploadStoredEventsAfterDelay:(NSTimeInterval)delay { } - (void)uploadLatestStoredEvent:(void (^)(void))completionHandler { + [self processRecrashReports]; NSString *latestFile = [self sortedEventFiles].lastObject; BSGEventUploadFileOperation *operation = latestFile ? [self uploadOperationsWithFiles:@[latestFile]].lastObject : nil; if (!operation) { @@ -118,6 +125,52 @@ - (void)uploadLatestStoredEvent:(void (^)(void))completionHandler { // MARK: - Implementation +- (void)processRecrashReports { + NSError *error = nil; + NSString *directory = self.kscrashReportsDirectory; + NSFileManager *fileManager = [[NSFileManager alloc] init]; + + NSArray *entries = [fileManager contentsOfDirectoryAtPath:directory error:&error]; + if (!entries) { + bsg_log_err(@"%@", error); + return; + } + + // Limit to reporting a single recrash to prevent potential for consuming too many resources + BOOL didReportRecrash = NO; + + for (NSString *filename in entries) { + if (![filename hasPrefix:RecrashReportPrefix] || + ![filename.pathExtension isEqual:@"json"]) { + continue; + } + + NSString *path = [directory stringByAppendingPathComponent:filename]; + if (!didReportRecrash) { + id recrashReport = [BSGJSONSerialization JSONObjectWithContentsOfFile:path options:0 error:&error]; + if ([recrashReport isKindOfClass:[NSDictionary class]]) { + bsg_log_debug(@"Reporting %@", filename); + [BSGInternalErrorReporter.sharedInstance reportRecrash:recrashReport]; + didReportRecrash = YES; + } + } + bsg_log_debug(@"Deleting %@", filename); + if (![fileManager removeItemAtPath:path error:&error]) { + bsg_log_err(@"%@", error); + } + + // Delete the report to prevent reporting a "JSON parsing error" + NSString *crashReportFilename = [filename stringByReplacingOccurrencesOfString:RecrashReportPrefix withString:CrashReportPrefix]; + NSString *crashReportPath = [directory stringByAppendingPathComponent:crashReportFilename]; + if (![BSGJSONSerialization JSONObjectWithContentsOfFile:crashReportPath options:0 error:nil]) { + bsg_log_info(@"Deleting unparsable %@", crashReportFilename); + if (![fileManager removeItemAtPath:crashReportPath error:&error]) { + bsg_log_err(@"%@", error); + } + } + } +} + /// Returns the stored event files sorted from oldest to most recent. - (NSMutableArray *)sortedEventFiles { NSMutableArray *files = [NSMutableArray array]; diff --git a/Bugsnag/Helpers/BSGAppHangDetector.m b/Bugsnag/Helpers/BSGAppHangDetector.m index fa35ad37c..beec79ea0 100644 --- a/Bugsnag/Helpers/BSGAppHangDetector.m +++ b/Bugsnag/Helpers/BSGAppHangDetector.m @@ -22,6 +22,7 @@ @interface BSGAppHangDetector () @property (weak, nonatomic) id delegate; +@property (nonatomic) BOOL runLoopIsRunning; @property (nonatomic) BOOL recordAllThreads; @property (nonatomic) CFRunLoopObserverRef observer; @property (atomic) dispatch_time_t processingDeadline; @@ -81,6 +82,7 @@ - (void)startWithDelegate:(id)delegate { // Each iteration indicates a separate unit of work so the hang detection should be reset accordingly. dispatch_semaphore_signal(self.processingFinished); } + self.runLoopIsRunning = YES; self.processingDeadline = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(threshold * NSEC_PER_SEC)); dispatch_semaphore_signal(self.processingStarted); isProcessing = YES; @@ -104,9 +106,9 @@ - (void)startWithDelegate:(id)delegate { self.observer = CFRunLoopObserverCreateWithHandler(NULL, activities, true, order, observerBlock); // Start monitoring immediately so that app hangs during launch can be detected. - // Note that because scene-based apps start in UIApplicationStateBackground, hangs in - // -[AppDelegate application:didFinishLaunchingWithOptions:] will be ignored. - observerBlock(self.observer, kCFRunLoopAfterWaiting); + self.processingDeadline = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(threshold * NSEC_PER_SEC)); + dispatch_semaphore_signal(self.processingStarted); + isProcessing = YES; CFRunLoopAddObserver(CFRunLoopGetMain(), self.observer, kCFRunLoopCommonModes); @@ -146,7 +148,9 @@ - (void)detectAppHangs { } #endif - if (shouldReportAppHang && !bsg_kscrashstate_currentState()->applicationIsInForeground) { + // Ignore background state if the runloop has not yet ticked so that hangs in `didFinishLaunching` in UIScene-based + // apps are detected. UIScene-based apps always start in UIApplicationStateBackground, unlike those without scenes. + if (shouldReportAppHang && !bsg_kscrashstate_currentState()->applicationIsInForeground && self.runLoopIsRunning) { bsg_log_debug(@"Ignoring app hang because app is in the background"); shouldReportAppHang = NO; } diff --git a/Bugsnag/Helpers/BSGInternalErrorReporter.h b/Bugsnag/Helpers/BSGInternalErrorReporter.h index 912af1d6c..7fe456523 100644 --- a/Bugsnag/Helpers/BSGInternalErrorReporter.h +++ b/Bugsnag/Helpers/BSGInternalErrorReporter.h @@ -25,8 +25,6 @@ FOUNDATION_EXPORT NSString *BSGErrorDescription(NSError *error); @property (readonly, nonatomic) BugsnagConfiguration *configuration; -@property (readonly, nonatomic) BugsnagNotifier *notifier; - - (BugsnagAppWithState *)generateAppWithState:(NSDictionary *)systemInfo; - (BugsnagDeviceWithState *)generateDeviceWithState:(NSDictionary *)systemInfo; @@ -62,6 +60,8 @@ FOUNDATION_EXPORT NSString *BSGErrorDescription(NSError *error); diagnostics:(nullable NSDictionary *)diagnostics groupingHash:(nullable NSString *)groupingHash; +- (void)reportRecrash:(NSDictionary *)recrashReport; + // Private - (nullable BugsnagEvent *)eventWithErrorClass:(NSString *)errorClass @@ -73,6 +73,8 @@ FOUNDATION_EXPORT NSString *BSGErrorDescription(NSError *error); diagnostics:(nullable NSDictionary *)diagnostics groupingHash:(nullable NSString *)groupingHash; +- (nullable BugsnagEvent *)eventWithRecrashReport:(NSDictionary *)recrashReport; + - (nullable NSURLRequest *)requestForEvent:(BugsnagEvent *)event error:(NSError * __autoreleasing *)errorPtr; @end diff --git a/Bugsnag/Helpers/BSGInternalErrorReporter.m b/Bugsnag/Helpers/BSGInternalErrorReporter.m index 1188e744c..6297daf2d 100644 --- a/Bugsnag/Helpers/BSGInternalErrorReporter.m +++ b/Bugsnag/Helpers/BSGInternalErrorReporter.m @@ -8,6 +8,7 @@ #import "BSGInternalErrorReporter.h" +#import "BSG_KSCrashReportFields.h" #import "BSG_KSSystemInfo.h" #import "BSG_RFC3339DateTool.h" #import "BugsnagApiClient.h" @@ -110,6 +111,17 @@ - (void)reportException:(NSException *)exception } } +- (void)reportRecrash:(NSDictionary *)recrashReport { + @try { + BugsnagEvent *event = [self eventWithRecrashReport:recrashReport]; + if (event) { + [self sendEvent:event]; + } + } @catch (NSException *exception) { + bsg_log_err(@"%@", exception); + } +} + // MARK: Private API - (nullable BugsnagEvent *)eventWithErrorClass:(NSString *)errorClass @@ -117,14 +129,11 @@ - (nullable BugsnagEvent *)eventWithErrorClass:(NSString *)errorClass diagnostics:(nullable NSDictionary *)diagnostics groupingHash:(nullable NSString *)groupingHash { - NSArray *stacktrace = [BugsnagStackframe stackframesWithCallStackReturnAddresses: - BSGArraySubarrayFromIndex(NSThread.callStackReturnAddresses, 2)]; - BugsnagError *error = [[BugsnagError alloc] initWithErrorClass:errorClass errorMessage:message errorType:BSGErrorTypeCocoa - stacktrace:stacktrace]; + stacktrace:nil]; return [self eventWithError:error diagnostics:diagnostics groupingHash:groupingHash]; } @@ -144,6 +153,33 @@ - (nullable BugsnagEvent *)eventWithException:(NSException *)exception return [self eventWithError:error diagnostics:diagnostics groupingHash:groupingHash]; } +- (nullable BugsnagEvent *)eventWithRecrashReport:(NSDictionary *)recrashReport { + NSString *reportType = recrashReport[@ BSG_KSCrashField_Report][@ BSG_KSCrashField_Type]; + if (![reportType isEqualToString:@ BSG_KSCrashReportType_Minimal]) { + return nil; + } + + NSDictionary *crash = recrashReport[@ BSG_KSCrashField_Crash]; + NSDictionary *crashedThread = crash[@ BSG_KSCrashField_CrashedThread]; + + NSArray *backtrace = crashedThread[@ BSG_KSCrashField_Backtrace][@ BSG_KSCrashField_Contents]; + NSArray *binaryImages = recrashReport[@ BSG_KSCrashField_BinaryImages]; + NSArray *stacktrace = BSGDeserializeArrayOfObjects(backtrace, ^BugsnagStackframe *(NSDictionary *dict) { + return [BugsnagStackframe frameFromDict:dict withImages:binaryImages]; + }); + + NSDictionary *errorDict = crash[@ BSG_KSCrashField_Error]; + BugsnagError *error = + [[BugsnagError alloc] initWithErrorClass:@"Crash handler crashed" + errorMessage:BSGParseErrorClass(errorDict, (id)errorDict[@ BSG_KSCrashField_Type]) + errorType:BSGErrorTypeCocoa + stacktrace:stacktrace]; + + BugsnagEvent *event = [self eventWithError:error diagnostics:recrashReport groupingHash:nil]; + event.handledState = [BugsnagHandledState handledStateWithSeverityReason:Signal]; + return event; +} + - (nullable BugsnagEvent *)eventWithError:(BugsnagError *)error diagnostics:(nullable NSDictionary *)diagnostics groupingHash:(nullable NSString *)groupingHash { @@ -196,7 +232,7 @@ - (NSURLRequest *)requestForEvent:(nonnull BugsnagEvent *)event error:(NSError * NSMutableDictionary *requestPayload = [NSMutableDictionary dictionary]; requestPayload[BSGKeyEvents] = @[[event toJsonWithRedactedKeys:nil]]; - requestPayload[BSGKeyNotifier] = [dataSource.notifier toDict]; + requestPayload[BSGKeyNotifier] = [[[BugsnagNotifier alloc] init] toDict]; requestPayload[BSGKeyPayloadVersion] = EventPayloadVersion; NSData *data = [NSJSONSerialization dataWithJSONObject:requestPayload options:0 error:errorPtr]; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 63a29e2ab..6cad601c9 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -622,7 +622,9 @@ void bsg_kscrw_i_writeTraceInfo(const BSG_KSCrash_Context *crashContext, bool bsg_kscrw_i_exceedsBufferLen(const size_t length); -void bsg_kscrashreport_writeKSCrashFields(BSG_KSCrash_Context *crashContext, BSG_KSCrashReportWriter *writer); +void bsg_kscrashreport_writeKSCrashFields(BSG_KSCrash_Context *crashContext, + BSG_KSCrashReportWriter *writer, + const char *const path); /** Write the contents of a memory location. * Also writes meta information about the data. @@ -1164,6 +1166,21 @@ void bsg_kscrw_i_writeMemoryInfo(const BSG_KSCrashReportWriter *const writer, writer->endContainer(writer); } +void bsg_kscrw_i_writeDiskInfo(const BSG_KSCrashReportWriter *const writer, + const char *const key, + const char *const path) { + uint64_t freeDisk, size; + if (!bsg_ksfuStatfs(path, &freeDisk, &size)) { + return; + } + writer->beginObject(writer, key); + { + bsg_kscrw_i_addUIntegerElement(writer, BSG_KSCrashField_Free, freeDisk); + bsg_kscrw_i_addUIntegerElement(writer, BSG_KSCrashField_Size, size); + } + writer->endContainer(writer); +} + /** Write information about the error leading to the crash to the report. * * @param writer The writer. @@ -1511,6 +1528,13 @@ void bsg_kscrashreport_writeMinimalReport( &crashContext->crash); } writer->endContainer(writer); + + BSG_Mach_Header_Info *image = bsg_mach_headers_get_self_image(); + if (image) { + writer->beginArray(writer, BSG_KSCrashField_BinaryImages); + bsg_kscrw_i_writeBinaryImage(writer, NULL, image); + writer->endContainer(writer); + } } writer->endContainer(writer); @@ -1547,7 +1571,7 @@ void bsg_kscrashreport_writeStandardReport( writer, BSG_KSCrashField_Report, BSG_KSCrashReportType_Standard, crashContext->config.crashID, crashContext->config.processName); - bsg_kscrashreport_writeKSCrashFields(crashContext, writer); + bsg_kscrashreport_writeKSCrashFields(crashContext, writer, path); if (crashContext->config.onCrashNotify != NULL) { // NOTE: The deny list for BSG_KSCrashField_UserAtCrash children in BugsnagEvent.m @@ -1566,7 +1590,10 @@ void bsg_kscrashreport_writeStandardReport( close(fd); } -void bsg_kscrashreport_writeKSCrashFields(BSG_KSCrash_Context *crashContext, BSG_KSCrashReportWriter *writer) { +void bsg_kscrashreport_writeKSCrashFields(BSG_KSCrash_Context *crashContext, + BSG_KSCrashReportWriter *writer, + const char *const path) { + bsg_kscrw_i_writeProcessState(writer, BSG_KSCrashField_ProcessState); if (crashContext->config.systemInfoJSON != NULL) { @@ -1579,6 +1606,7 @@ void bsg_kscrashreport_writeKSCrashFields(BSG_KSCrash_Context *crashContext, BSG bsg_kscrw_i_writeMemoryInfo(writer, BSG_KSCrashField_Memory); bsg_kscrw_i_writeAppStats(writer, BSG_KSCrashField_AppStats, &crashContext->state); + bsg_kscrw_i_writeDiskInfo(writer, BSG_KSCrashField_Disk, path); } writer->endContainer(writer); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportFields.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportFields.h index 516fc30af..33fe865ee 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportFields.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportFields.h @@ -114,6 +114,7 @@ #pragma mark - Memory - #define BSG_KSCrashField_Free "free" +#define BSG_KSCrashField_Size "size" #define BSG_KSCrashField_Usable "usable" #pragma mark - Error - @@ -170,6 +171,7 @@ #pragma mark Standard #define BSG_KSCrashField_AppStats "application_stats" #define BSG_KSCrashField_BinaryImages "binary_images" +#define BSG_KSCrashField_Disk "disk" #define BSG_KSCrashField_SystemAtCrash "system_atcrash" #define BSG_KSCrashField_System "system" #define BSG_KSCrashField_Memory "memory" diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.h index 275b898f4..b1ad0b7ec 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.h @@ -34,6 +34,7 @@ #define BSG_KSSystemField_BundleVersion "CFBundleVersion" #define BSG_KSSystemField_CPUArch "cpu_arch" #define BSG_KSSystemField_DeviceAppHash "device_app_hash" +#define BSG_KSSystemField_Disk "disk" #define BSG_KSSystemField_Jailbroken "jailbroken" #define BSG_KSSystemField_Machine "machine" #define BSG_KSSystemField_Memory "memory" diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index 1aa4f4bc5..17117fa61 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -29,6 +29,7 @@ #import "BSG_KSSystemInfo.h" #import "BSG_KSSystemInfoC.h" #import "BSG_KSMachHeaders.h" +#import "BSG_KSFileUtils.h" #import "BSG_KSJSONCodecObjC.h" #import "BSG_KSMach.h" #import "BSG_KSSysCtl.h" @@ -432,6 +433,19 @@ + (NSDictionary *)systemInfo { @BSG_KSSystemField_Size: [self int64Sysctl:@"hw.memsize"] }; + NSString *dir = NSSearchPathForDirectoriesInDomains( + NSCachesDirectory, NSUserDomainMask, YES).firstObject; + const char *path = dir.fileSystemRepresentation; + if (path) { + uint64_t dfree, size; + if (bsg_ksfuStatfs(path, &dfree, &size)) { + sysInfo[@BSG_KSSystemField_Disk] = @{ + @ BSG_KSCrashField_Free: @(dfree), + @ BSG_KSCrashField_Size: @(size) + }; + } + } + NSDictionary *statsInfo = [[BSG_KSCrash sharedInstance] captureAppStats]; sysInfo[@BSG_KSCrashField_AppStats] = statsInfo; return sysInfo; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c index ea17b0516..e721c4625 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c @@ -150,15 +150,13 @@ void bsg_kscrashsentry_suspendThreads(void) { if (bsg_g_context != NULL) { bsg_g_context->allThreads = bsg_ksmachgetAllThreads(&bsg_g_context->allThreadsCount); bsg_ksmachgetThreadStates(bsg_g_context->allThreads, bsg_g_context->allThreadRunStates, bsg_g_context->allThreadsCount); - BSG_KSLOG_DEBUG( - "Suspending all threads except for %d reserved threads.", - BSG_KSCrashReservedThreadTypeCount); bsg_g_context->threadsToResumeCount = bsg_ksmachremoveThreadsFromList(bsg_g_context->allThreads, bsg_g_context->allThreadsCount, bsg_g_context->reservedThreads, BSG_KSCrashReservedThreadTypeCount, bsg_g_context->threadsToResume, MAX_CAPTURED_THREADS); + BSG_KSLOG_DEBUG("Suspending %d of %d threads.", bsg_g_context->threadsToResumeCount, bsg_g_context->allThreadsCount); bsg_ksmachsuspendThreads(bsg_g_context->threadsToResume, bsg_g_context->threadsToResumeCount); } else { BSG_KSLOG_DEBUG("Suspending all threads."); @@ -179,8 +177,7 @@ void bsg_kscrashsentry_resumeThreads(void) { } if (bsg_g_context != NULL) { - BSG_KSLOG_DEBUG("Resuming all threads except for %d reserved threads.", - BSG_KSCrashReservedThreadTypeCount); + BSG_KSLOG_DEBUG("Resuming %d of %d threads.", bsg_g_context->threadsToResumeCount, bsg_g_context->allThreadsCount); bsg_ksmachresumeThreads(bsg_g_context->threadsToResume, bsg_g_context->threadsToResumeCount); bsg_g_context->threadsToResumeCount = 0; if (bsg_g_context->allThreads != NULL) { @@ -203,12 +200,15 @@ void bsg_kscrashsentry_clearContext(BSG_KSCrash_SentryContext *context) { void (*onCrash)(void *) = context->onCrash; bool threadTracingEnabled = context->threadTracingEnabled; bool reportWhenDebuggerIsAttached = context->reportWhenDebuggerIsAttached; + thread_t reservedThreads[BSG_KSCrashReservedThreadTypeCount]; + memcpy(reservedThreads, context->reservedThreads, sizeof(reservedThreads)); memset(context, 0, sizeof(*context)); context->onCrash = onCrash; context->threadTracingEnabled = threadTracingEnabled; context->reportWhenDebuggerIsAttached = reportWhenDebuggerIsAttached; + memcpy(context->reservedThreads, reservedThreads, sizeof(reservedThreads)); } void bsg_kscrashsentry_beginHandlingCrash(BSG_KSCrash_SentryContext *context) { diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm index 00644a341..fa87e321b 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm @@ -126,7 +126,10 @@ static void CPPExceptionTerminate(void) { BSG_KSLOG_DEBUG("Detected NSException. Recording details and letting " "the current NSException handler deal with it."); isNSException = true; + // recordException() doesn't call beginHandlingCrash() + bsg_kscrashsentry_beginHandlingCrash(bsg_g_context); bsg_recordException(exception); + bsg_g_context->handlingCrash = false; } catch (std::exception &exc) { strlcpy(descriptionBuff, exc.what(), sizeof(descriptionBuff)); } catch (std::exception *exc) { diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c index 4e0e7cd3e..ee62044d6 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c @@ -221,19 +221,19 @@ void *ksmachexc_i_handleExceptions(void *const userData) { thread_suspend(bsg_ksmachthread_self()); } - for (;;) { + while (bsg_g_installed) { BSG_KSLOG_DEBUG("Waiting for mach exception"); // Wait for a message. - kern_return_t kr = mach_msg( + mach_msg_return_t result = mach_msg( &exceptionMessage.header, MACH_RCV_MSG, 0, sizeof(exceptionMessage), bsg_g_exceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if (kr == KERN_SUCCESS) { + if (result == MACH_MSG_SUCCESS) { break; } // Loop and try again on failure. - BSG_KSLOG_ERROR("mach_msg: %s", mach_error_string(kr)); + BSG_KSLOG_ERROR("mach_msg: %d", result); } BSG_KSLOG_DEBUG("Trapped mach exception code 0x%llx, subcode 0x%llx", @@ -442,6 +442,15 @@ void bsg_kscrashsentry_uninstallMachHandler(void) { bsg_ksmachexc_i_restoreExceptionPorts(); + bsg_g_installed = 0; + + if (bsg_g_context->handlingCrash) { + // Terminating a thread that is currently handling an exception message + // can cause a deadlock, so let's not do that! + BSG_KSLOG_DEBUG("Not cancelling exception threads."); + return; + } + thread_t thread_self = bsg_ksmachthread_self(); if (bsg_g_primaryPThread != 0 && bsg_g_primaryMachThread != thread_self) { @@ -464,9 +473,6 @@ void bsg_kscrashsentry_uninstallMachHandler(void) { bsg_g_secondaryMachThread = 0; bsg_g_secondaryPThread = 0; } - - BSG_KSLOG_DEBUG("Mach exception handlers uninstalled."); - bsg_g_installed = 0; } #else diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c index 812e17b10..224a1046a 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c @@ -31,6 +31,7 @@ #include #include +#include #include const char *bsg_ksfulastPathEntry(const char *const path) { @@ -81,3 +82,13 @@ bool bsg_ksfuwriteBytesToFD(const int fd, const char *const bytes, return true; } + +bool bsg_ksfuStatfs(const char *path, uint64_t *free, uint64_t *total) { + struct statfs st; + if (statfs(path, &st) != 0) { + return false; + } + *free = st.f_bsize * st.f_bavail; + *total = st.f_bsize * st.f_blocks; + return true; +} diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h index 2c64c0a35..2992d09ce 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h @@ -64,6 +64,18 @@ bool bsg_ksfuwriteBytesToFD(const int fd, const char *bytes, ssize_t length); */ bool bsg_ksfuflushWriteBuffer(const int fd); +/** + * Get file system statistics. + * + * @param path The path name of any file or directory within the file system. + * @param free A pointer to where the number of free bytes availalable to a + * non-superuser (corresponding to NSFileSystemFreeSize) should be + * written. + * @param total A pointer to where the total number of bytes in the file system + * should be written. + */ +bool bsg_ksfuStatfs(const char *path, uint64_t *free, uint64_t *total); + #ifdef __cplusplus } #endif diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c index 00ed92829..e63f80221 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c @@ -36,6 +36,7 @@ struct crashreporter_annotations_t { static void bsg_mach_headers_register_dyld_images(void); static void bsg_mach_headers_register_for_changes(void); static intptr_t bsg_mach_headers_compute_slide(const struct mach_header *header); +static bool bsg_mach_headers_contains_address(BSG_Mach_Header_Info *image, vm_address_t address); static const char * bsg_mach_headers_get_path(const struct mach_header *header); static const struct dyld_all_image_infos *g_all_image_infos; @@ -46,6 +47,8 @@ static BSG_Mach_Header_Info *bsg_g_mach_headers_images_head; static BSG_Mach_Header_Info *bsg_g_mach_headers_images_tail; static dispatch_queue_t bsg_g_serial_queue; +static BSG_Mach_Header_Info *g_self_image; + BSG_Mach_Header_Info *bsg_mach_headers_get_images() { if (!bsg_g_mach_headers_images_head) { bsg_mach_headers_initialize(); @@ -64,6 +67,10 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_main_image() { return NULL; } +BSG_Mach_Header_Info *bsg_mach_headers_get_self_image(void) { + return g_self_image; +} + void bsg_mach_headers_initialize() { // Clear any existing headers to reset the head/tail pointers @@ -76,6 +83,7 @@ void bsg_mach_headers_initialize() { bsg_g_mach_headers_images_head = NULL; bsg_g_mach_headers_images_tail = NULL; bsg_g_serial_queue = dispatch_queue_create("com.bugsnag.mach-headers", DISPATCH_QUEUE_SERIAL); + g_self_image = NULL; } static void bsg_mach_headers_register_dyld_images() { @@ -201,6 +209,9 @@ void bsg_mach_headers_add_image(const struct mach_header *header, intptr_t slide bsg_g_mach_headers_images_tail->next = newImage; } bsg_g_mach_headers_images_tail = newImage; + if (bsg_mach_headers_contains_address(newImage, (vm_address_t)bsg_mach_headers_add_image)) { + g_self_image = newImage; + } }); } else { free(newImage); @@ -249,12 +260,7 @@ BSG_Mach_Header_Info *bsg_mach_headers_image_named(const char *const imageName, BSG_Mach_Header_Info *bsg_mach_headers_image_at_address(const uintptr_t address) { for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img; img = img->next) { - if (img->unloaded == true) { - continue; - } - uintptr_t imageAddress = (uintptr_t)img->header; - if (address >= imageAddress && - address < (imageAddress + img->imageSize)) { + if (bsg_mach_headers_contains_address(img, address)) { return img; } } @@ -368,6 +374,14 @@ static intptr_t bsg_mach_headers_compute_slide(const struct mach_header *header) return 0; } +static bool bsg_mach_headers_contains_address(BSG_Mach_Header_Info *img, vm_address_t address) { + if (img->unloaded) { + return false; + } + vm_address_t imageStart = (vm_address_t)img->header; + return address >= imageStart && address < (imageStart + img->imageSize); +} + static const char * bsg_mach_headers_get_path(const struct mach_header *header) { Dl_info DlInfo = {0}; dladdr(header, &DlInfo); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h index b34e29643..4f252f92c 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h @@ -69,6 +69,11 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_images(void); */ BSG_Mach_Header_Info *bsg_mach_headers_get_main_image(void); +/** + * Returns the image that contains KSCrash. + */ +BSG_Mach_Header_Info *bsg_mach_headers_get_self_image(void); + /** * Called when a binary image is loaded. */ diff --git a/Bugsnag/Payload/BugsnagDeviceWithState+Private.h b/Bugsnag/Payload/BugsnagDeviceWithState+Private.h index 519f836c4..b4c3d586b 100644 --- a/Bugsnag/Payload/BugsnagDeviceWithState+Private.h +++ b/Bugsnag/Payload/BugsnagDeviceWithState+Private.h @@ -26,8 +26,6 @@ NS_ASSUME_NONNULL_BEGIN @end -NSNumber *BSGDeviceFreeSpace(NSSearchPathDirectory directory); - NSMutableDictionary *BSGParseDeviceMetadata(NSDictionary *event); NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Payload/BugsnagDeviceWithState.m b/Bugsnag/Payload/BugsnagDeviceWithState.m index d0ba99165..3b380563a 100644 --- a/Bugsnag/Payload/BugsnagDeviceWithState.m +++ b/Bugsnag/Payload/BugsnagDeviceWithState.m @@ -35,26 +35,6 @@ return device; } -/** - * Calculates the amount of free disk space on the device in bytes, for a given directory. - * @param directory the directory whose disk space should be queried - * @return free space in the number of bytes, or nil if this information could not be found - */ -NSNumber *BSGDeviceFreeSpace(NSSearchPathDirectory directory) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, true); - NSString *path = [searchPaths lastObject]; - - NSError *error; - NSDictionary *fileSystemAttrs = - [fileManager attributesOfFileSystemForPath:path error:&error]; - - if (!fileSystemAttrs) { - bsg_log_warn(@"Failed to read free disk space: %@", error); - } - return fileSystemAttrs[NSFileSystemFreeSize]; -} - @implementation BugsnagDeviceWithState + (BugsnagDeviceWithState *) deviceFromJson:(NSDictionary *)json { @@ -89,7 +69,7 @@ + (BugsnagDeviceWithState *)deviceWithKSCrashReport:(NSDictionary *)event { [self populateFields:device dictionary:event]; device.orientation = [event valueForKeyPath:@"user.state.deviceState.orientation"]; device.freeMemory = [event valueForKeyPath:@"system." BSG_KSSystemField_Memory "." BSG_KSCrashField_Free]; - device.freeDisk = BSGDeviceFreeSpace(NSCachesDirectory); + device.freeDisk = [event valueForKeyPath:@"system." BSG_KSSystemField_Disk "." BSG_KSCrashField_Free]; NSString *val = [event valueForKeyPath:@"report.timestamp"]; diff --git a/Bugsnag/Payload/BugsnagError.m b/Bugsnag/Payload/BugsnagError.m index be23ed91c..b622ae969 100644 --- a/Bugsnag/Payload/BugsnagError.m +++ b/Bugsnag/Payload/BugsnagError.m @@ -106,7 +106,7 @@ - (instancetype)initWithErrorClass:(NSString *)errorClass _errorClass = errorClass; _errorMessage = errorMessage; _typeString = BSGSerializeErrorType(errorType); - _stacktrace = stacktrace; + _stacktrace = stacktrace ?: @[]; } return self; } diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index 6f483767a..abe70811b 100644 --- a/Bugsnag/Payload/BugsnagNotifier.m +++ b/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else _name = @"Bugsnag Objective-C"; #endif - _version = @"6.15.1"; + _version = @"6.15.2"; _url = @"https://github.com/bugsnag/bugsnag-cocoa"; _dependencies = @[]; } diff --git a/Bugsnag/Payload/BugsnagStackframe.m b/Bugsnag/Payload/BugsnagStackframe.m index 4f6497646..e5c364bbe 100644 --- a/Bugsnag/Payload/BugsnagStackframe.m +++ b/Bugsnag/Payload/BugsnagStackframe.m @@ -27,7 +27,7 @@ @implementation BugsnagStackframe -+ (NSDictionary *_Nullable)findImageAddr:(unsigned long)addr inImages:(NSArray *)images { +static NSDictionary * _Nullable FindImage(NSArray *images, uintptr_t addr) { for (NSDictionary *image in images) { if ([(NSNumber *)image[BSGKeyImageAddress] unsignedLongValue] == addr) { return image; @@ -80,7 +80,7 @@ + (instancetype)frameFromDict:(NSDictionary *)dict withImages:(N frame.isPc = [dict[BSGKeyIsPC] boolValue]; frame.isLr = [dict[BSGKeyIsLR] boolValue]; - NSDictionary *image = [self findImageAddr:[frame.machoLoadAddress unsignedLongValue] inImages:binaryImages]; + NSDictionary *image = FindImage(binaryImages, (uintptr_t)frame.machoLoadAddress.unsignedLongLongValue); if (image != nil) { frame.machoUuid = image[BSGKeyUuid]; frame.machoVmAddress = image[BSGKeyImageVmAddress]; @@ -93,7 +93,7 @@ + (instancetype)frameFromDict:(NSDictionary *)dict withImages:(N // Ignore invalid link register frames. // For EXC_BREAKPOINT mach exceptions the link register does not contain an instruction address. return nil; - } else { + } else if (/* Don't warn for recrash reports */ binaryImages.count > 1) { bsg_log_warn(@"BugsnagStackframe: no image found for address %@", FormatMemoryAddress(frame.machoLoadAddress)); } diff --git a/BugsnagNetworkRequestPlugin.podspec.json b/BugsnagNetworkRequestPlugin.podspec.json index fc401268b..2b607d760 100644 --- a/BugsnagNetworkRequestPlugin.podspec.json +++ b/BugsnagNetworkRequestPlugin.podspec.json @@ -1,16 +1,16 @@ { "name": "BugsnagNetworkRequestPlugin", - "version": "6.15.1", + "version": "6.15.2", "summary": "Network request monitoring support for Bugsnag.", "homepage": "https://bugsnag.com", "license": "MIT", "authors": { "Bugsnag": "notifiers@bugsnag.com" }, - "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.15.1/BugsnagNetworkRequestPlugin/README.md", + "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.15.2/BugsnagNetworkRequestPlugin/README.md", "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.15.1" + "tag": "v6.15.2" }, "dependencies": { "Bugsnag": "~> 6.13" diff --git a/CHANGELOG.md b/CHANGELOG.md index 025979850..dec8f08d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ Changelog ========= +## 6.15.2 (2022-01-05) + +### Bug fixes + +* Detect hangs during launch of UIScene based apps. + [#1263](https://github.com/bugsnag/bugsnag-cocoa/pull/1263) + +* Stop persisting changes made by `OnSendError` callbacks if delivery needs to be retried. + [#1262](https://github.com/bugsnag/bugsnag-cocoa/pull/1262) + +* Fix incorrect `device.freeDisk` in crash errors. + [#1256](https://github.com/bugsnag/bugsnag-cocoa/pull/1256) + +* Fix some potential deadlocks that could occur if a crash handler crashes. + [#1252](https://github.com/bugsnag/bugsnag-cocoa/pull/1252) + ## 6.15.1 (2021-12-08) ### Bug fixes diff --git a/Framework/Info.plist b/Framework/Info.plist index 02c4cc05d..b65668a80 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.15.1 + 6.15.2 CFBundleVersion 1 diff --git a/Gemfile b/Gemfile index 32433b6a6..233eee75a 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'xcpretty' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v6.6.2' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v6.8.0' # Use a specific branch #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'master' diff --git a/Gemfile.lock b/Gemfile.lock index 9ac803d38..66943fc47 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,28 +1,28 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: 9efc4f0c876f050d34e0bef54be2599598076818 - tag: v6.6.2 + revision: fe12189f83aad154f54221ee0fcd41b483d3c0d1 + tag: v6.8.0 specs: - bugsnag-maze-runner (6.6.2) + bugsnag-maze-runner (6.8.0) appium_lib (~> 11.2.0) bugsnag (~> 6.24) cucumber (~> 7.1) cucumber-expressions (~> 6.0.0) curb (~> 0.9.6) - minitest (~> 5.0) optimist (~> 3.0.1) os (~> 1.0.0) rake (~> 12.3.3) rubyzip (~> 2.3.2) selenium-webdriver (~> 3.11) - test-unit (~> 3.3.0) + test-unit (~> 3.5.2) webrick (~> 1.7.0) GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (6.1.4.1) + CFPropertyList (3.0.5) + rexml + activesupport (6.1.4.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -124,7 +124,7 @@ GEM cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) cucumber-messages (~> 17.1, >= 17.1.1) curb (0.9.11) - danger (8.3.1) + danger (8.4.2) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) @@ -139,7 +139,7 @@ GEM terminal-table (>= 1, < 4) diff-lcs (1.4.4) escape (0.0.4) - ethon (0.14.0) + ethon (0.15.0) ffi (>= 1.15.0) eventmachine (1.2.7) faraday (1.8.0) @@ -170,12 +170,12 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - git (1.9.1) + git (1.10.0) rchardet (~> 1.8) httpclient (2.8.3) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) - jazzy (0.14.0) + jazzy (0.14.1) cocoapods (~> 1.5) mustache (~> 1.1) open4 (~> 1.3) @@ -185,7 +185,7 @@ GEM sassc (~> 2.1) sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) - json (2.5.1) + json (2.6.1) kramdown (2.3.1) rexml kramdown-parser-gfm (1.1.0) @@ -195,7 +195,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2021.1115) mini_portile2 (2.6.1) - minitest (5.14.4) + minitest (5.15.0) molinillo (0.8.0) multi_test (0.1.2) multipart-post (2.1.1) @@ -207,8 +207,6 @@ GEM nokogiri (1.12.5) mini_portile2 (~> 2.6.1) racc (~> 1.4) - nokogiri (1.12.5-x86_64-darwin) - racc (~> 1.4) octokit (4.21.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) @@ -239,7 +237,7 @@ GEM ffi (~> 1.1) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - test-unit (3.3.9) + test-unit (3.5.3) power_assert tomlrb (1.3.0) typhoeus (1.4.0) @@ -262,11 +260,10 @@ GEM rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) - zeitwerk (2.4.2) + zeitwerk (2.5.1) PLATFORMS ruby - universal-darwin-20 DEPENDENCIES bugsnag-maze-runner! @@ -276,4 +273,4 @@ DEPENDENCIES xcpretty BUNDLED WITH - 2.2.20 + 2.3.0 diff --git a/TESTING.md b/TESTING.md index 292b73088..39bfa8481 100644 --- a/TESTING.md +++ b/TESTING.md @@ -25,55 +25,77 @@ run. The tests are located in the ['features'](/features/) directory. For testing against a real device, maze-runner's CLI and the test fixtures are containerized so you'll need Docker (and Docker Compose) to run them. -__Note: only Bugsnag employees can run the end-to-end tests.__ We have dedicated test infrastructure and private -BrowserStack credentials that cannot be shared outside of the organization. - -#### Requirements +### Requirements - Xcode - Make -- Docker -- Docker-compose -- AWS `opensource` profile credentials -- BrowserStack credentials - -#### Authenticating with the private container registry - -You'll need to set the credentials for the aws profile in order to access the private docker registry: - -``` -aws configure --profile=opensource -``` +- BrowserStack credentials or device running a modern version of iOS. -Subsequently you'll need to run the following commmand to authenticate with the registry: +### Building the test fixture app -``` -$(aws ecr get-login --profile=opensource --no-include-email) -``` +Build the test iOS fixture: + ```shell script + make test-fixtures + ``` -__Your session will periodically expire__, so you'll need to run this command to re-authenticate when that happens. - -#### Steps +### Running tests on BrowserStack (typically Bugsnag employees only) 1. Ensure the following environment variables are set: - `MAZE_DEVICE_FARM_USERNAME` - your BrowserStack App Automate Username - `MAZE_DEVICE_FARM_ACCESS_KEY` - your BrowserStack App Automate Access Key - `MAZE_BS_LOCAL` - location of the `BrowserStackLocal` executable on your local file system -1. Build the test fixtures: - ```shell script - make test-fixtures - ``` -1. Check the contents of `Gemfile` to select the version of `maze-runner` to use -1. See https://www.browserstack.com/local-testing/app-automate for details of the required local testing binary. -1. To run a single feature: +2. See https://www.browserstack.com/local-testing/app-automate for details of the required local testing binary. +3. Check the contents of `Gemfile` to select the version of `maze-runner` to use +4. To run a single feature: ```shell script bundle exec maze-runner --app=features/fixtures/ios/output/iOSTestApp.ipa \ --farm=bs \ --device=IOS_14 \ features/app_and_device_attributes.feature ``` -1. To run all features, omit the final argument. -1. Maze Runner supports various other option, as well as all those that Cucumber does. For full details run: +5. To run all features, omit the final argument. + +### Running tests on your own device + +#### Prerequisites + +1. Install a proxy server such as `mitmproxy`: + ```shell script + brew install mitmproxy + ``` +2. Install Appium + ``` + npm install -g appium@1.21 + ``` +3. Set `MAZE_APPLE_TEAM_ID` to your Apple Developer Team Id. +4. The test fixture is hard-coded to send requests to `bs-local.com:9339` (BrowserStack's approach to local testing). + Add an entry for bs-local.com to `/etc/hosts`: + ``` + 127.0.0.1 bs-local.com + ``` +5. Install and run a proxy, such as `mitmproxy` + ```shell script + mitmproxy + ``` +6. Set a manual proxy on your device's network connection to the IP of your Mac and port of the proxy + (8080 by default for `mitmproxy`). + +#### Running tests + +1. Run Maze Runner as follows, adjusting for your specific device: + ```shell script + bundle exec maze-runner --app=features/fixtures/ios/output/iOSTestApp.ipa \ + --farm=local \ + --udid= \ + --os=ios \ + --os-version=14 \ + features/app_and_device_attributes.feature + ``` + `` is the device Identifier found under Devices and Simulators in Xcode. + +### Notes + +1. Maze Runner supports various other options, as well as all those that Cucumber does. For full details run: ```shell script `bundle exec maze-runner --help` ``` diff --git a/Tests/BugsnagTests/BSGInternalErrorReporterTests.m b/Tests/BugsnagTests/BSGInternalErrorReporterTests.m index 01d0a3df8..51b7fce89 100644 --- a/Tests/BugsnagTests/BSGInternalErrorReporterTests.m +++ b/Tests/BugsnagTests/BSGInternalErrorReporterTests.m @@ -17,7 +17,6 @@ @interface BSGInternalErrorReporterTests : XCTestCase @property (nonatomic) BugsnagConfiguration *configuration; -@property (nonatomic) BugsnagNotifier *notifier; @end @@ -29,7 +28,6 @@ - (void)setUp { [BSGInternalErrorReporter setSharedInstance:nil]; #pragma clang diagnostic pop self.configuration = [[BugsnagConfiguration alloc] initWithApiKey:@"0192837465afbecd0192837465afbecd"]; - self.notifier = [[BugsnagNotifier alloc] init]; } - (void)testEventWithErrorClass { @@ -41,7 +39,7 @@ - (void)testEventWithErrorClass { XCTAssertEqualObjects(event.errors[0].errorMessage, @"Something went wrong"); XCTAssertEqualObjects(event.groupingHash, @"test"); XCTAssertEqualObjects(event.threads, @[]); - XCTAssertGreaterThan(event.errors[0].stacktrace.count, 0); + XCTAssertEqual(event.errors[0].stacktrace.count, 0); XCTAssertNil(event.apiKey); NSDictionary *diagnostics = [event.metadata getMetadataFromSection:@"BugsnagDiagnostics"]; @@ -71,6 +69,26 @@ - (void)testEventWithException { XCTAssertEqualObjects(diagnostics[@"apiKey"], configuration.apiKey); } +- (void)testEventWithRecrashReport { + BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:@"0192837465afbecd0192837465afbecd"]; + BSGInternalErrorReporter *reporter = [[BSGInternalErrorReporter alloc] initWithDataSource:self]; + + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"RecrashReport" ofType:@"json" inDirectory:@"Data"]; + id recrashReport = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:path] options:0 error:nil]; + BugsnagEvent *event = [reporter eventWithRecrashReport:recrashReport]; + XCTAssertEqualObjects(event.errors[0].errorClass, @"Crash handler crashed"); + XCTAssertEqualObjects(event.errors[0].errorMessage, @"EXC_BAD_ACCESS"); + XCTAssertEqualObjects(event.errors[0].stacktrace[1].machoUuid, @"77B20495-B09E-3585-AE2C-1475AD41A48A"); + XCTAssertEqualObjects(event.errors[0].stacktrace[1].method, @"BSSerializeDataCrashHandler"); + XCTAssertEqualObjects(event.threads, @[]); + XCTAssertNil(event.apiKey); + + NSDictionary *diagnostics = [event.metadata getMetadataFromSection:@"BugsnagDiagnostics"]; + XCTAssertEqualObjects(diagnostics[@"apiKey"], configuration.apiKey); + XCTAssert([diagnostics[@"crash"] isKindOfClass:[NSDictionary class]]); + XCTAssert([diagnostics[@"binary_images"] isKindOfClass:[NSArray class]]); +} + - (void)testRequestForEvent { self.configuration.endpoints.notify = @"https://notify.example.com"; diff --git a/Tests/BugsnagTests/BugsnagDeviceTest.m b/Tests/BugsnagTests/BugsnagDeviceTest.m index 82b58e67e..ca985dd31 100644 --- a/Tests/BugsnagTests/BugsnagDeviceTest.m +++ b/Tests/BugsnagTests/BugsnagDeviceTest.m @@ -33,6 +33,9 @@ - (void)setUp { @"usable": @15065522176, @"free": @742920192 }, + @"disk": @{ + @"free": @1234567 + }, @"device_app_hash": @"123" }, @"report": @{ @@ -159,18 +162,6 @@ - (void)testDeviceWithStateToDict { XCTAssertEqualObjects([formatter dateFromString:@"2014-12-02T01:56:13Z"], device.time); } -- (void)testDeviceFreeSpaceShouldBeLargeNumber { - NSNumber *freeBytes = BSGDeviceFreeSpace(NSCachesDirectory); - XCTAssertNotNil(freeBytes, @"expect a valid number for successful call to retrieve free space"); - XCTAssertGreaterThan([freeBytes integerValue], 1000, @"expect at least 1k of free space on test device"); -} - -- (void)testDeviceFreeSpaceShouldBeNilWhenFailsToRetrieveIt { - NSSearchPathDirectory notAccessibleDirectory = NSAdminApplicationDirectory; - NSNumber *freeBytes = BSGDeviceFreeSpace(notAccessibleDirectory); - XCTAssertNil(freeBytes, @"expect nil when fails to retrieve free space for the directory"); -} - - (void)testDeviceRuntimeInfoAppended { BugsnagDevice *device = [BugsnagDevice deviceWithKSCrashReport:self.data]; XCTAssertEqual(2, [device.runtimeVersions count]); diff --git a/Tests/BugsnagTests/Data/RecrashReport.json b/Tests/BugsnagTests/Data/RecrashReport.json new file mode 100644 index 000000000..ce3180897 --- /dev/null +++ b/Tests/BugsnagTests/Data/RecrashReport.json @@ -0,0 +1 @@ +{"report":{"version":"3.1.0","id":"A6115AE2-3894-4566-9382-605441DA08EE","process_name":"macOSTestApp","timestamp":1639063461,"timestamp_millis":1639063461408,"type":"minimal"},"crash":{"crashed_thread":{"backtrace":{"contents":[{"object_addr":4330020864,"object_name":"macOSTestApp","symbol_addr":4330060000,"symbol_name":"OnCrashBadAccess","instruction_addr":4330060020},{"object_addr":4331298816,"object_name":"Bugsnag","symbol_addr":4331514880,"symbol_name":"BSSerializeDataCrashHandler","instruction_addr":4331515261},{"object_addr":4331298816,"object_name":"Bugsnag","symbol_addr":4331342352,"symbol_name":"bsg_kscrw_i_callUserCrashHandler","instruction_addr":4331342398},{"object_addr":4331298816,"object_name":"Bugsnag","symbol_addr":4331343120,"symbol_name":"bsg_kscrashreport_writeStandardReport","instruction_addr":4331343511},{"object_addr":4331298816,"object_name":"Bugsnag","symbol_addr":4331318128,"symbol_name":"bsg_kscrash_i_onCrash","instruction_addr":4331318264},{"object_addr":4331298816,"object_name":"Bugsnag","symbol_addr":4331346240,"symbol_name":"_ZL21CPPExceptionTerminatev","instruction_addr":4331349022},{"object_addr":140733735362560,"object_name":"libc++abi.dylib","symbol_addr":140733735424767,"symbol_name":"_ZSt11__terminatePFvvE","instruction_addr":140733735424775},{"object_addr":140733735362560,"object_name":"libc++abi.dylib","symbol_addr":140733735424640,"symbol_name":"_ZSt9terminatev","instruction_addr":140733735424681},{"object_addr":140733733916672,"object_name":"libdispatch.dylib","symbol_addr":140733733931006,"symbol_name":"_dispatch_client_callout","instruction_addr":140733733931034},{"object_addr":140733733916672,"object_name":"libdispatch.dylib","symbol_addr":140733733941257,"symbol_name":"_dispatch_continuation_pop","instruction_addr":140733733941680},{"object_addr":140733733916672,"object_name":"libdispatch.dylib","symbol_addr":140733734006103,"symbol_name":"_dispatch_source_invoke","instruction_addr":140733734008164},{"object_addr":140733733916672,"object_name":"libdispatch.dylib","symbol_addr":140733733980067,"symbol_name":"_dispatch_main_queue_callback_4CF","instruction_addr":140733733980680},{"object_addr":140733736165376,"object_name":"CoreFoundation","symbol_addr":140733736939279,"symbol_name":"__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__","instruction_addr":140733736939288},{"object_addr":140733736165376,"object_name":"CoreFoundation","symbol_addr":140733736683087,"symbol_name":"__CFRunLoopRun","instruction_addr":140733736685842},{"object_addr":140733736165376,"object_name":"CoreFoundation","symbol_addr":140733736680793,"symbol_name":"CFRunLoopRunSpecific","instruction_addr":140733736681356},{"object_addr":140733873090560,"object_name":"HIToolbox","symbol_addr":140733873289567,"symbol_name":"RunCurrentEventLoopInMode","instruction_addr":140733873289859},{"object_addr":140733873090560,"object_name":"HIToolbox","symbol_addr":140733873288602,"symbol_name":"ReceiveNextEventCommon","instruction_addr":140733873289189},{"object_addr":140733873090560,"object_name":"HIToolbox","symbol_addr":140733873288509,"symbol_name":"_BlockUntilNextEventMatchingListInModeWithFilter","instruction_addr":140733873288579},{"object_addr":140733778403328,"object_name":"AppKit","symbol_addr":140733778660882,"symbol_name":"_DPSNextEvent","instruction_addr":140733778661746},{"object_addr":140733778403328,"object_name":"AppKit","symbol_addr":140733778654193,"symbol_name":"-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]","instruction_addr":140733778655557},{"object_addr":140733778403328,"object_name":"AppKit","symbol_addr":140733778598431,"symbol_name":"-[NSApplication run]","instruction_addr":140733778599017},{"object_addr":140733778403328,"object_name":"AppKit","symbol_addr":140733778418492,"symbol_name":"NSApplicationMain","instruction_addr":140733778419308},{"object_addr":4330020864,"object_name":"macOSTestApp","symbol_addr":4330056800,"symbol_name":"main","instruction_addr":4330057133},{"object_addr":140733735698432,"object_name":"libdyld.dylib","symbol_addr":140733735788348,"symbol_name":"start","instruction_addr":140733735788349}],"skipped":0},"registers":{"basic":{"rax":0,"rbx":140603611497760,"rcx":140733735460022,"rdx":0,"rdi":140732885694120,"rsi":514,"rbp":140732885694000,"rsp":140732885694000,"r8":2,"r9":0,"r10":0,"r11":534,"r12":0,"r13":105553148027392,"r14":0,"r15":272,"rip":4330060020,"rflags":66050,"cs":43,"fs":0,"gs":0},"exception":{"trapno":14,"err":6,"faultvaddr":0}},"index":0,"crashed":true,"current_thread":false,"stack":{"overflow":false}},"error":{"address":0,"mach":{"exception":1,"exception_name":"EXC_BAD_ACCESS","code":"0x1","code_name":"KERN_INVALID_ADDRESS","subcode":"0x0"},"type":"mach"}},"binary_images":[{"image_addr":4331298816,"image_vmaddr":0,"image_size":491520,"name":"/Users/nick/Library/Developer/Xcode/DerivedData/macOSTestApp-ffunpkxyeczwoccascsrmsggolbp/Build/Products/Debug/macOSTestApp.app/Contents/Frameworks/Bugsnag.framework/Versions/A/Bugsnag","uuid":"77B20495-B09E-3585-AE2C-1475AD41A48A","cpu_type":16777223,"cpu_subtype":3}]} \ No newline at end of file diff --git a/Tests/BugsnagTests/Info.plist b/Tests/BugsnagTests/Info.plist index 82d9552c8..8c3e48244 100644 --- a/Tests/BugsnagTests/Info.plist +++ b/Tests/BugsnagTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.15.1 + 6.15.2 CFBundleVersion 1 diff --git a/Tests/TestHost-iOS/Info.plist b/Tests/TestHost-iOS/Info.plist index b0e9bf1f5..8960906a4 100644 --- a/Tests/TestHost-iOS/Info.plist +++ b/Tests/TestHost-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.15.1 + 6.15.2 CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/VERSION b/VERSION index d36e8d82f..0bffdbe7a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.15.1 +6.15.2 diff --git a/examples/objective-c-ios/objective-c-ios.xcodeproj/project.pbxproj b/examples/objective-c-ios/objective-c-ios.xcodeproj/project.pbxproj index e41ef66ab..68801342f 100644 --- a/examples/objective-c-ios/objective-c-ios.xcodeproj/project.pbxproj +++ b/examples/objective-c-ios/objective-c-ios.xcodeproj/project.pbxproj @@ -235,7 +235,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env ruby"; - shellScript = "fork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"].each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; + shellScript = "dsyms = Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"]\n\nfork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n dsyms.each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; showEnvVarsInLog = 0; }; 6D3E90BD4495FE724F7FF283 /* [CP] Check Pods Manifest.lock */ = { diff --git a/examples/objective-c-osx/objective-c-osx.xcodeproj/project.pbxproj b/examples/objective-c-osx/objective-c-osx.xcodeproj/project.pbxproj index 60c4e7125..3cdad9dc2 100644 --- a/examples/objective-c-osx/objective-c-osx.xcodeproj/project.pbxproj +++ b/examples/objective-c-osx/objective-c-osx.xcodeproj/project.pbxproj @@ -216,7 +216,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env ruby"; - shellScript = "fork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"].each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; + shellScript = "dsyms = Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"]\n\nfork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n dsyms.each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/examples/swift-ios/swift-ios.xcodeproj/project.pbxproj b/examples/swift-ios/swift-ios.xcodeproj/project.pbxproj index f9af8cd55..d77c1ebe4 100644 --- a/examples/swift-ios/swift-ios.xcodeproj/project.pbxproj +++ b/examples/swift-ios/swift-ios.xcodeproj/project.pbxproj @@ -225,7 +225,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env ruby"; - shellScript = "fork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"].each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; + shellScript = "dsyms = Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"]\n\nfork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n dsyms.each do |dsym|\n system(\"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} https://upload.bugsnag.com/\")\n end\nend\n"; showEnvVarsInLog = 0; }; B2D2057DB5069AFE4AD142D4 /* [CP] Embed Pods Frameworks */ = { diff --git a/features/app_and_device_attributes.feature b/features/app_and_device_attributes.feature index 6f340b37f..fb7178fe4 100644 --- a/features/app_and_device_attributes.feature +++ b/features/app_and_device_attributes.feature @@ -69,7 +69,7 @@ Feature: App and Device attributes present And the event "app.isLaunching" is false Scenario: App and Device info is as expected when overridden via config - When I run "AppAndDeviceAttributesScenarioConfigOverride" + When I run "AppAndDeviceAttributesConfigOverrideScenario" And I wait to receive an error Then the error is valid for the error reporting API And the error "Bugsnag-API-Key" header equals "12312312312312312312312312312312" @@ -80,7 +80,7 @@ Feature: App and Device attributes present And the error payload field "events.0.app.releaseStage" equals "secondStage" Scenario: App and Device info is as expected when overridden via callback - When I run "AppAndDeviceAttributesScenarioCallbackOverride" + When I run "AppAndDeviceAttributesCallbackOverrideScenario" And I wait to receive an error Then the error is valid for the error reporting API And the error "Bugsnag-API-Key" header equals "12312312312312312312312312312312" @@ -93,7 +93,7 @@ Feature: App and Device attributes present And the error payload field "events.0.device.modelNumber" equals "0898" Scenario: Info.plist settings are used when calling startWithApiKey - When I run "AppAndDeviceAttributesScenarioStartWithApiKey" + When I run "AppAndDeviceAttributesStartWithApiKeyScenario" And I wait to receive an error Then the error is valid for the error reporting API And the error "Bugsnag-API-Key" header equals "12312312312312312312312312312312" @@ -115,13 +115,13 @@ Feature: App and Device attributes present And the event "app.isLaunching" is true Scenario: isLaunching is true for unhandled exception during launch - When I run "AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario" and relaunch the app + When I run "AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario" and relaunch the crashed app And I configure Bugsnag for "AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario" And I wait to receive an error And the event "app.isLaunching" is true Scenario: isLaunching is false for unhandled exception after launch - When I run "AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario" and relaunch the app + When I run "AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario" and relaunch the crashed app And I configure Bugsnag for "AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario" And I wait to receive an error And the event "app.isLaunching" is false diff --git a/features/app_hangs.feature b/features/app_hangs.feature index 1210a6151..2b0a358be 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -134,7 +134,7 @@ Feature: App hangs @skip_macos Scenario: Fatal app hangs should be reported if the app hangs before going to the background When I run "AppHangFatalOnlyScenario" - And I background the app for 3 seconds + And I send the app to the background for 3 seconds And I relaunch the app And I configure Bugsnag for "AppHangFatalOnlyScenario" And I wait to receive an error @@ -143,7 +143,7 @@ Feature: App hangs @skip_macos Scenario: Fatal app hangs should not be reported if they occur once the app is in the background When I run "AppHangDidEnterBackgroundScenario" - And I background the app for 3 seconds + And I send the app to the background for 3 seconds And I relaunch the app And I configure Bugsnag for "AppHangDidEnterBackgroundScenario" Then I should receive no errors @@ -151,14 +151,12 @@ Feature: App hangs @skip_macos Scenario: App hangs should be reported if the app hangs after resuming from the background When I run "AppHangDidBecomeActiveScenario" - And I background the app for 3 seconds + And I send the app to the background for 3 seconds And I wait to receive an error And the exception "message" equals "The app's main thread failed to respond to an event within 2000 milliseconds" Scenario: App hangs that occur during app termination should be non-fatal - Given I run "AppHangInTerminationScenario" - And the app is not running - And I relaunch the app + Given I run "AppHangInTerminationScenario" and relaunch the crashed app And I configure Bugsnag for "AppHangInTerminationScenario" Then I wait to receive an error And the event "severity" equals "warning" diff --git a/features/auto_detect_errors.feature b/features/auto_detect_errors.feature index ac372b253..d0598028b 100644 --- a/features/auto_detect_errors.feature +++ b/features/auto_detect_errors.feature @@ -13,7 +13,7 @@ Feature: autoDetectErrors flag controls whether errors are captured automaticall And the event "unhandled" is false And I discard the oldest error And I relaunch the app - When I run "AutoDetectFalseNSExceptionScenario" and relaunch the app + When I run "AutoDetectFalseNSExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "AutoDetectFalseHandledScenario" Then I should receive no requests @@ -25,6 +25,6 @@ Feature: autoDetectErrors flag controls whether errors are captured automaticall And the event "unhandled" is false And I discard the oldest error And I relaunch the app - When I run "AutoDetectFalseAbortScenario" and relaunch the app + When I run "AutoDetectFalseAbortScenario" and relaunch the crashed app And I configure Bugsnag for "AutoDetectFalseHandledScenario" Then I should receive no requests diff --git a/features/auto_notify.feature b/features/auto_notify.feature index 5c124f93b..e184c686f 100644 --- a/features/auto_notify.feature +++ b/features/auto_notify.feature @@ -11,7 +11,7 @@ Feature: autoNotify flag allows disabling error detection after Bugsnag is initi And the event "unhandled" is false And I discard the oldest error And I relaunch the app - When I run "AutoNotifyFalseNSExceptionScenario" and relaunch the app + When I run "AutoNotifyFalseNSExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "AutoNotifyFalseHandledScenario" Then I should receive no requests @@ -23,7 +23,7 @@ Feature: autoNotify flag allows disabling error detection after Bugsnag is initi And the event "unhandled" is false And I discard the oldest error And I relaunch the app - When I run "AutoNotifyFalseAbortScenario" and relaunch the app + When I run "AutoNotifyFalseAbortScenario" and relaunch the crashed app And I configure Bugsnag for "AutoNotifyFalseHandledScenario" Then I should receive no requests @@ -35,7 +35,7 @@ Feature: autoNotify flag allows disabling error detection after Bugsnag is initi And the event "unhandled" is false And I discard the oldest error And I relaunch the app - When I run "AutoNotifyReenabledScenario" and relaunch the app + When I run "AutoNotifyReenabledScenario" and relaunch the crashed app And I configure Bugsnag for "AutoNotifyReenabledScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index bf83ff47d..381255e1d 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -108,7 +108,7 @@ Feature: Barebone tests And the stacktrace is valid for the event Scenario: Barebone test: unhandled error - When I run "BareboneTestUnhandledErrorScenario" and relaunch the app + When I run "BareboneTestUnhandledErrorScenario" and relaunch the crashed app And I set the app to "report" mode And I configure Bugsnag for "BareboneTestUnhandledErrorScenario" And I wait to receive an error diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 35634898a..d900937ee 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -25,7 +25,7 @@ Feature: Attaching a series of notable events leading up to errors Then the event has a "state" breadcrumb named "Bugsnag loaded" Scenario: An app lauches and subsequently crashes - When I run "BuiltinTrapScenario" and relaunch the app + When I run "BuiltinTrapScenario" and relaunch the crashed app And I configure Bugsnag for "BuiltinTrapScenario" And I wait to receive an error Then the event has a "state" breadcrumb named "Bugsnag loaded" @@ -36,7 +36,7 @@ Feature: Attaching a series of notable events leading up to errors Then the event has a "manual" breadcrumb named "Cache locked" Scenario: Modifying a breadcrumb name in callback - When I run "ModifyBreadcrumbInNotify" + When I run "ModifyBreadcrumbInNotifyScenario" And I wait to receive an error Then the event has a "manual" breadcrumb named "Cache locked" @@ -44,7 +44,7 @@ Feature: Attaching a series of notable events leading up to errors @skip_macos Scenario: State breadcrumbs When I configure Bugsnag for "HandledErrorScenario" - And I background the app for 2 seconds + And I send the app to the background for 2 seconds And I click the element "run_scenario" And I wait to receive an error Then the event has a "state" breadcrumb named "Bugsnag loaded" diff --git a/features/context.feature b/features/context.feature index 15c6526a1..7efa42bc4 100644 --- a/features/context.feature +++ b/features/context.feature @@ -16,7 +16,7 @@ Feature: The context can be automatically and manually set on errors And the event "context" is null Scenario: Automatic context from a C error - When I run "AbortScenario" and relaunch the app + When I run "AbortScenario" and relaunch the crashed app And I configure Bugsnag for "AbortScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/crashprobe.feature b/features/crashprobe.feature index bb67412fa..369d92d13 100644 --- a/features/crashprobe.feature +++ b/features/crashprobe.feature @@ -4,7 +4,7 @@ Feature: Reporting crash events Given I clear all persistent data Scenario: Executing privileged instruction - When I run "PrivilegedInstructionScenario" and relaunch the app + When I run "PrivilegedInstructionScenario" and relaunch the crashed app And I configure Bugsnag for "PrivilegedInstructionScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -15,7 +15,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[PrivilegedInstructionScenario run]" Scenario: Calling __builtin_trap() - When I run "BuiltinTrapScenario" and relaunch the app + When I run "BuiltinTrapScenario" and relaunch the crashed app And I configure Bugsnag for "BuiltinTrapScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -26,7 +26,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[BuiltinTrapScenario run]" Scenario: Calling non-existent method - When I run "NonExistentMethodScenario" and relaunch the app + When I run "NonExistentMethodScenario" and relaunch the crashed app And I configure Bugsnag for "NonExistentMethodScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -49,7 +49,7 @@ Feature: Reporting crash events And the "method" of stack frame 5 equals "-[NonExistentMethodScenario run]" Scenario: Trigger a crash after overwriting the link register - When I run "OverwriteLinkRegisterScenario" and relaunch the app + When I run "OverwriteLinkRegisterScenario" and relaunch the crashed app And I configure Bugsnag for "OverwriteLinkRegisterScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -58,7 +58,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[OverwriteLinkRegisterScenario run]" Scenario: Attempt to write into a read-only page - When I run "ReadOnlyPageScenario" and relaunch the app + When I run "ReadOnlyPageScenario" and relaunch the crashed app And I configure Bugsnag for "ReadOnlyPageScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -66,9 +66,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[ReadOnlyPageScenario run]" Scenario: Stack overflow - When I run "StackOverflowScenario" - And the app is not running - And I relaunch the app + When I run "StackOverflowScenario" and relaunch the crashed app And I configure Bugsnag for "StackOverflowScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -86,7 +84,7 @@ Feature: Reporting crash events And the "method" of stack frame 9 equals "-[StackOverflowScenario run]" Scenario: Crash inside objc_msgSend() - When I run "ObjCMsgSendScenario" and relaunch the app + When I run "ObjCMsgSendScenario" and relaunch the crashed app And I configure Bugsnag for "ObjCMsgSendScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -97,7 +95,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "objc_msgSend" Scenario: Attempt to execute an instruction undefined on the current architecture - When I run "UndefinedInstructionScenario" and relaunch the app + When I run "UndefinedInstructionScenario" and relaunch the crashed app And I configure Bugsnag for "UndefinedInstructionScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -105,7 +103,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[UndefinedInstructionScenario run]" Scenario: Send a message to an object whose memory has already been freed - When I run "ReleasedObjectScenario" and relaunch the app + When I run "ReleasedObjectScenario" and relaunch the crashed app And I configure Bugsnag for "ReleasedObjectScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -117,8 +115,8 @@ Feature: Reporting crash events | Intel | -[ReleasedObjectScenario run] | Scenario: Crash within Swift code - When I run "SwiftCrash" and relaunch the app - And I configure Bugsnag for "SwiftCrash" + When I run "SwiftCrashScenario" and relaunch the crashed app + And I configure Bugsnag for "SwiftCrashScenario" And I wait to receive an error Then the error is valid for the error reporting API And the exception "errorClass" equals "Fatal error" @@ -126,8 +124,8 @@ Feature: Reporting crash events And the event "metaData.error.crashInfo" matches "Fatal error: Unexpectedly found nil while unwrapping an Optional value" Scenario: Assertion failure in Swift code - When I run "SwiftAssertion" and relaunch the app - And I configure Bugsnag for "SwiftAssertion" + When I run "SwiftAssertionScenario" and relaunch the crashed app + And I configure Bugsnag for "SwiftAssertionScenario" And I wait to receive an error Then the error is valid for the error reporting API And the exception "errorClass" equals "Fatal error" @@ -135,7 +133,7 @@ Feature: Reporting crash events And the event "metaData.error.crashInfo" matches "Fatal error: several unfortunate things just happened" Scenario: Dereference a null pointer - When I run "NullPointerScenario" and relaunch the app + When I run "NullPointerScenario" and relaunch the crashed app And I configure Bugsnag for "NullPointerScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -144,7 +142,13 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[NullPointerScenario run]" Scenario: Trigger a crash with libsystem_pthread's _pthread_list_lock held - When I run "AsyncSafeThreadScenario" and relaunch the app + When I run "AsyncSafeThreadScenario" + + # Sleep and relaunch here instead of checking the app state as this specific + # crash seems to inhibit Appium's ability to check the app state on iOS 10 + And I wait for 3 seconds + And I relaunch the app + And I configure Bugsnag for "AsyncSafeThreadScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -156,13 +160,13 @@ Feature: Reporting crash events | -[AsyncSafeThreadScenario run] | Scenario: Trigger a crash with simulated malloc() lock held - When I run "AsyncSafeMallocScenario" and relaunch the app + When I run "AsyncSafeMallocScenario" and relaunch the crashed app And I configure Bugsnag for "AsyncSafeMallocScenario" And I wait to receive an error And the exception "errorClass" equals "SIGABRT" Scenario: Read a garbage pointer - When I run "ReadGarbagePointerScenario" and relaunch the app + When I run "ReadGarbagePointerScenario" and relaunch the crashed app And I configure Bugsnag for "ReadGarbagePointerScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -171,7 +175,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "-[ReadGarbagePointerScenario run]" Scenario: Access a non-object as an object - When I run "AccessNonObjectScenario" and relaunch the app + When I run "AccessNonObjectScenario" and relaunch the crashed app And I configure Bugsnag for "AccessNonObjectScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -180,7 +184,7 @@ Feature: Reporting crash events And the "method" of stack frame 0 equals "objc_msgSend" Scenario: Misuse of libdispatch - When I run "DispatchCrashScenario" and relaunch the app + When I run "DispatchCrashScenario" and relaunch the crashed app And I configure Bugsnag for "DispatchCrashScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/delivery.feature b/features/delivery.feature index 870d1daa9..d1925546b 100644 --- a/features/delivery.feature +++ b/features/delivery.feature @@ -23,7 +23,7 @@ Feature: Delivery of errors Then I should receive no requests Scenario: Bugsnag.start() should block for 2 seconds after a launch crash - When I run "SendLaunchCrashesSynchronouslyScenario" and relaunch the app + When I run "SendLaunchCrashesSynchronouslyScenario" and relaunch the crashed app And I set the response delay for the next request to 5000 milliseconds And I set the app to "report" mode And I run "SendLaunchCrashesSynchronouslyScenario" @@ -32,7 +32,7 @@ Feature: Delivery of errors And the event "metaData.bugsnag.startDuration" is between 2.0 and 2.5 Scenario: Bugsnag.start() should not block if sendLaunchCrashesSynchronously is false - When I run "SendLaunchCrashesSynchronouslyFalseScenario" and relaunch the app + When I run "SendLaunchCrashesSynchronouslyFalseScenario" and relaunch the crashed app And I set the response delay for the next request to 5000 milliseconds And I set the app to "report" mode And I run "SendLaunchCrashesSynchronouslyFalseScenario" @@ -41,7 +41,7 @@ Feature: Delivery of errors And the event "metaData.bugsnag.startDuration" is between 0.0 and 0.5 Scenario: Bugsnag.start() should not block for non-launch crashes - When I run "SendLaunchCrashesSynchronouslyLaunchCompletedScenario" and relaunch the app + When I run "SendLaunchCrashesSynchronouslyLaunchCompletedScenario" and relaunch the crashed app And I set the response delay for the next request to 5000 milliseconds And I set the app to "report" mode And I run "SendLaunchCrashesSynchronouslyLaunchCompletedScenario" diff --git a/features/discard_classes.feature b/features/discard_classes.feature index 18403018e..f8b7716d9 100644 --- a/features/discard_classes.feature +++ b/features/discard_classes.feature @@ -9,13 +9,13 @@ Feature: Configuration discardClasses option And the exception "errorClass" equals "NotDiscarded" Scenario: Discard unhandled exception - When I run "DiscardClassesUnhandledExceptionScenario" and relaunch the app + When I run "DiscardClassesUnhandledExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "DiscardClassesUnhandledExceptionScenario" And I wait to receive an error And the exception "errorClass" equals "NotDiscarded" Scenario: Discard unhandled crash - When I run "DiscardClassesUnhandledCrashScenario" and relaunch the app + When I run "DiscardClassesUnhandledCrashScenario" and relaunch the crashed app And I configure Bugsnag for "DiscardClassesUnhandledCrashScenario" And I wait to receive an error And the exception "errorClass" equals "NotDiscarded" diff --git a/features/enabled_error_types.feature b/features/enabled_error_types.feature index 242554cbc..a7e6b0370 100644 --- a/features/enabled_error_types.feature +++ b/features/enabled_error_types.feature @@ -5,14 +5,14 @@ Feature: Enabled error types Scenario: All Crash reporting is disabled # enabledErrorTypes = None, Generate a manual notification, crash - When I run "DisableAllExceptManualExceptionsAndCrashScenario" and relaunch the app + When I run "DisableAllExceptManualExceptionsAndCrashScenario" and relaunch the crashed app And I configure Bugsnag for "DisableAllExceptManualExceptionsAndCrashScenario" And I wait to receive an error Then the error is valid for the error reporting API And the event "unhandled" is false Scenario: NSException Crash Reporting is disabled - When I run "DisableNSExceptionScenario" and relaunch the app + When I run "DisableNSExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "DisableNSExceptionScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -21,21 +21,21 @@ Feature: Enabled error types And the event "unhandled" is true Scenario: CPP Crash Reporting is disabled - When I run "EnabledErrorTypesCxxScenario" and relaunch the app + When I run "EnabledErrorTypesCxxScenario" and relaunch the crashed app And I configure Bugsnag for "EnabledErrorTypesCxxScenario" And I wait to receive an error Then the error is valid for the error reporting API And the event "unhandled" is false Scenario: Mach Crash Reporting is disabled - When I run "DisableMachExceptionScenario" and relaunch the app + When I run "DisableMachExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "DisableMachExceptionScenario" And I wait to receive an error Then the error is valid for the error reporting API And the event "unhandled" is false Scenario: Signals Crash Reporting is disabled - When I run "DisableSignalsExceptionScenario" and relaunch the app + When I run "DisableSignalsExceptionScenario" and relaunch the crashed app And I configure Bugsnag for "DisableSignalsExceptionScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/event_callbacks.feature b/features/event_callbacks.feature index 30a19df81..682794781 100644 --- a/features/event_callbacks.feature +++ b/features/event_callbacks.feature @@ -54,7 +54,7 @@ Feature: Callbacks can access and modify event information And the event "unhandled" is false Scenario: An OnSend callback can overwrite information for an unhandled error - When I run "SwiftAssertion" and relaunch the app + When I run "SwiftAssertionScenario" and relaunch the crashed app And I configure Bugsnag for "OnSendOverwriteScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -68,7 +68,7 @@ Feature: Callbacks can access and modify event information And the event "user.name" equals "customName" Scenario: Information set in OnCrashHandler is added to the final report - When I run "OnCrashHandlerScenario" and relaunch the app + When I run "OnCrashHandlerScenario" and relaunch the crashed app And I configure Bugsnag for "OnSendOverwriteScenario" And I wait to receive an error Then the error is valid for the error reporting API @@ -116,3 +116,13 @@ Feature: Callbacks can access and modify event information And the event "metaData.callbacks.beforeCrash" is true And the event "metaData.callbacks.afterCrash" is null And the event "metaData.callbacks.secondCallback" is true + + Scenario: Changes made in OnSendError should not be persisted + Given I set the HTTP status code for the next request to 500 + And I run "OnSendErrorPersistenceScenario" + And I wait to receive an error + And I clear the error queue + And I relaunch the app + And I configure Bugsnag for "OnSendErrorPersistenceScenario" + And I wait to receive an error + Then the event "metaData.unexpected.message" is null diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 23e3e61f6..a76f04c7d 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -18,8 +18,10 @@ 00CEB60D24080C690004793D /* EnabledErrorTypesScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 00CEB60C24080C690004793D /* EnabledErrorTypesScenario.m */; }; 01018BA025E40ADD000312C6 /* AsyncSafeMallocScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01018B9F25E40ADD000312C6 /* AsyncSafeMallocScenario.m */; }; 0104085F258CA0A100933C60 /* DispatchCrashScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0104085E258CA0A100933C60 /* DispatchCrashScenario.swift */; }; + 0104B47E275A7B3C003EBDF0 /* RecrashScenarios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0104B47D275A7B3C003EBDF0 /* RecrashScenarios.mm */; }; 0163BFA72583B3CF008DC28B /* DiscardClassesScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0163BFA62583B3CF008DC28B /* DiscardClassesScenarios.swift */; }; - 01847DD626453D4E00ADA4C7 /* InternalErrorReportingScenarios.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DD526453D4E00ADA4C7 /* InternalErrorReportingScenarios.m */; }; + 017B4134276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 017B4133276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m */; }; + 01847DD626453D4E00ADA4C7 /* InvalidCrashReportScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DD526453D4E00ADA4C7 /* InvalidCrashReportScenario.m */; }; 01AF6A53258A112F00FFC803 /* BareboneTestScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */; }; 01B6BB7525D5748800FC4DE6 /* LastRunInfoScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenarios.swift */; }; 01B6BBB625DA82B800FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; }; @@ -35,11 +37,11 @@ 8A32DB8222424E3000EDD92F /* NSExceptionShiftScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A32DB8122424E3000EDD92F /* NSExceptionShiftScenario.m */; }; 8A38C5D124094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A38C5D024094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift */; }; 8A3B5F292407F66700CE4A3A /* ModifyBreadcrumbScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3B5F282407F66700CE4A3A /* ModifyBreadcrumbScenario.swift */; }; - 8A3B5F2B240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift */; }; + 8A3B5F2B240807EE00CE4A3A /* ModifyBreadcrumbInNotifyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotifyScenario.swift */; }; 8A42FD35225DEE04007AE561 /* SessionOOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A42FD33225DEE04007AE561 /* SessionOOMScenario.m */; }; 8A530CCC22FDDBF000F0C108 /* ManyConcurrentNotifyScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A530CCB22FDDBF000F0C108 /* ManyConcurrentNotifyScenario.m */; }; 8A72A0382396574F00328051 /* CustomPluginNotifierDescriptionScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A72A0372396574F00328051 /* CustomPluginNotifierDescriptionScenario.m */; }; - 8A840FBA21AF5C450041DBFA /* SwiftAssertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A840FB921AF5C450041DBFA /* SwiftAssertion.swift */; }; + 8A840FBA21AF5C450041DBFA /* SwiftAssertionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A840FB921AF5C450041DBFA /* SwiftAssertionScenario.swift */; }; 8A98400320FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A98400220FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m */; }; 8AB1081923301FE600672818 /* ReleaseStageScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */; }; 8AB65FCC22DC77CB001200AB /* LoadConfigFromFileScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB65FCB22DC77CB001200AB /* LoadConfigFromFileScenario.swift */; }; @@ -136,7 +138,7 @@ F42955E0916B8851F074D9B3 /* UserEmailScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4295F595986A279FA3BDEA7 /* UserEmailScenario.swift */; }; F429565A951303E2C3136D0D /* UserIdScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42957C58F98EECD3ADD84B2 /* UserIdScenario.swift */; }; F4295836C8AF75547C675E8D /* ReleasedObjectScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = F4295E71D7B2DFE77057F3DA /* ReleasedObjectScenario.m */; }; - F42958881D3F34A76CADE4EC /* SwiftCrash.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4295EEDC00E5ED3C166DBF0 /* SwiftCrash.swift */; }; + F42958881D3F34A76CADE4EC /* SwiftCrashScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4295EEDC00E5ED3C166DBF0 /* SwiftCrashScenario.swift */; }; F42958BE5DDACDBF653CA926 /* ManualSessionScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = F429570EE7A751B53D011481 /* ManualSessionScenario.m */; }; F42959124DB949EEF1420957 /* ReadOnlyPageScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = F4295871D1BCF211398CAEBA /* ReadOnlyPageScenario.m */; }; F4295968571A4118D6A4606A /* UserEnabledScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42954E2B3FF0C5C0474DA74 /* UserEnabledScenario.swift */; }; @@ -180,8 +182,10 @@ 00CEB60C24080C690004793D /* EnabledErrorTypesScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EnabledErrorTypesScenario.m; sourceTree = ""; }; 01018B9F25E40ADD000312C6 /* AsyncSafeMallocScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AsyncSafeMallocScenario.m; sourceTree = ""; }; 0104085E258CA0A100933C60 /* DispatchCrashScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchCrashScenario.swift; sourceTree = ""; }; + 0104B47D275A7B3C003EBDF0 /* RecrashScenarios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RecrashScenarios.mm; sourceTree = ""; }; 0163BFA62583B3CF008DC28B /* DiscardClassesScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscardClassesScenarios.swift; sourceTree = ""; }; - 01847DD526453D4E00ADA4C7 /* InternalErrorReportingScenarios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InternalErrorReportingScenarios.m; sourceTree = ""; }; + 017B4133276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnSendErrorPersistenceScenario.m; sourceTree = ""; }; + 01847DD526453D4E00ADA4C7 /* InvalidCrashReportScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InvalidCrashReportScenario.m; sourceTree = ""; }; 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BareboneTestScenarios.swift; sourceTree = ""; }; 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LastRunInfoScenarios.swift; sourceTree = ""; }; 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendLaunchCrashesSynchronouslyScenario.swift; sourceTree = ""; }; @@ -198,14 +202,14 @@ 8A32DB8122424E3000EDD92F /* NSExceptionShiftScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSExceptionShiftScenario.m; sourceTree = ""; }; 8A38C5D024094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscardedBreadcrumbTypeScenario.swift; sourceTree = ""; }; 8A3B5F282407F66700CE4A3A /* ModifyBreadcrumbScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyBreadcrumbScenario.swift; sourceTree = ""; }; - 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyBreadcrumbInNotify.swift; sourceTree = ""; }; + 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotifyScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyBreadcrumbInNotifyScenario.swift; sourceTree = ""; }; 8A42FD33225DEE04007AE561 /* SessionOOMScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionOOMScenario.m; sourceTree = ""; }; 8A42FD34225DEE04007AE561 /* SessionOOMScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionOOMScenario.h; sourceTree = ""; }; 8A530CCA22FDDBF000F0C108 /* ManyConcurrentNotifyScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ManyConcurrentNotifyScenario.h; sourceTree = ""; }; 8A530CCB22FDDBF000F0C108 /* ManyConcurrentNotifyScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ManyConcurrentNotifyScenario.m; sourceTree = ""; }; 8A72A0362396574F00328051 /* CustomPluginNotifierDescriptionScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomPluginNotifierDescriptionScenario.h; sourceTree = ""; }; 8A72A0372396574F00328051 /* CustomPluginNotifierDescriptionScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomPluginNotifierDescriptionScenario.m; sourceTree = ""; }; - 8A840FB921AF5C450041DBFA /* SwiftAssertion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftAssertion.swift; sourceTree = ""; }; + 8A840FB921AF5C450041DBFA /* SwiftAssertionScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftAssertionScenario.swift; sourceTree = ""; }; 8A98400120FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoSessionCustomVersionScenario.h; sourceTree = ""; }; 8A98400220FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutoSessionCustomVersionScenario.m; sourceTree = ""; }; 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReleaseStageScenarios.swift; sourceTree = ""; }; @@ -357,7 +361,7 @@ F4295E71D7B2DFE77057F3DA /* ReleasedObjectScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReleasedObjectScenario.m; sourceTree = ""; }; F4295E86DC0BE9DC731B0D1C /* NullPointerScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NullPointerScenario.m; sourceTree = ""; }; F4295EE3B0BD05E5FE513B20 /* AutoSessionScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutoSessionScenario.m; sourceTree = ""; }; - F4295EEDC00E5ED3C166DBF0 /* SwiftCrash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCrash.swift; sourceTree = ""; }; + F4295EEDC00E5ED3C166DBF0 /* SwiftCrashScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCrashScenario.swift; sourceTree = ""; }; F4295F13EBCAC9CB0ABC4008 /* NonExistentMethodScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NonExistentMethodScenario.m; sourceTree = ""; }; F4295F22AA1863213F4A5F51 /* AutoSessionScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoSessionScenario.h; sourceTree = ""; }; F4295F595986A279FA3BDEA7 /* UserEmailScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserEmailScenario.swift; sourceTree = ""; }; @@ -534,7 +538,7 @@ isa = PBXGroup; children = ( 8A3B5F282407F66700CE4A3A /* ModifyBreadcrumbScenario.swift */, - 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift */, + 8A3B5F2A240807EE00CE4A3A /* ModifyBreadcrumbInNotifyScenario.swift */, 8A38C5D024094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift */, E7A324E5247E9D8D008B0052 /* BreadcrumbCallbackDiscardScenario.swift */, E7A324E7247E9D9A008B0052 /* BreadcrumbCallbackOrderScenario.swift */, @@ -599,9 +603,11 @@ 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */, 01DE903726CE99B800455213 /* CriticalThermalStateScenario.swift */, 0163BFA62583B3CF008DC28B /* DiscardClassesScenarios.swift */, - 01847DD526453D4E00ADA4C7 /* InternalErrorReportingScenarios.m */, + 01847DD526453D4E00ADA4C7 /* InvalidCrashReportScenario.m */, + 017B4133276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m */, 01E5EAD025B713990066EA8A /* OOMScenario.h */, 01E5EAD125B713990066EA8A /* OOMScenario.m */, + 0104B47D275A7B3C003EBDF0 /* RecrashScenarios.mm */, 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */, F4295ABA693D273D52AA9F6B /* Scenario.h */, F42954E8B66F3FB7F5333CF7 /* Scenario.m */, @@ -674,7 +680,7 @@ F4295E71D7B2DFE77057F3DA /* ReleasedObjectScenario.m */, F42952BCC8A05EFAAE503E3D /* StackOverflowScenario.h */, F4295493796EA93321E5CDDB /* StackOverflowScenario.m */, - F4295EEDC00E5ED3C166DBF0 /* SwiftCrash.swift */, + F4295EEDC00E5ED3C166DBF0 /* SwiftCrashScenario.swift */, F4295A4EF5288C60F978676F /* UndefinedInstructionScenario.h */, F429538D19421F28D8EB0446 /* UndefinedInstructionScenario.m */, ); @@ -745,7 +751,7 @@ isa = PBXGroup; children = ( 8A32DB8022424E3000EDD92F /* NSExceptionShiftScenario.h */, - 8A840FB921AF5C450041DBFA /* SwiftAssertion.swift */, + 8A840FB921AF5C450041DBFA /* SwiftAssertionScenario.swift */, F4295A364B3851D3811BC648 /* HandledErrorScenario.swift */, F4295FA8EBBA645EECF7B483 /* HandledErrorOverrideScenario.swift */, F429526319377A8848136413 /* HandledExceptionScenario.swift */, @@ -869,7 +875,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env ruby"; - shellScript = "# First, attempt to get the API key from an environment variable\napi_key = ENV[\"BUGSNAG_API_KEY\"]\n\n# If not present, attempt to lookup the value from the Info.plist\nif !api_key\n default_info_plist_location = Dir.glob(\"./{ios/,}*/Info.plist\").reject {|path| path =~ /build|test/i }\n plist_buddy_response = `/usr/libexec/PlistBuddy -c \"print :BugsnagAPIKey\" \"#{default_info_plist_location.first}\"`\n api_key = plist_buddy_response if $?.success?\nend\n\nfork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"].each do |dsym|\n curl_command = \"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} \"\n curl_command += \"-F apiKey=#{Shellwords.escape(api_key)} \" if api_key\n curl_command += \"https://upload.bugsnag.com/\"\n system(curl_command)\n end\nend\n"; + shellScript = "# First, attempt to get the API key from an environment variable\napi_key = ENV[\"BUGSNAG_API_KEY\"]\n\n# If not present, attempt to lookup the value from the Info.plist\nif !api_key\n default_info_plist_location = Dir.glob(\"./{ios/,}*/Info.plist\").reject {|path| path =~ /build|test/i }\n plist_buddy_response = `/usr/libexec/PlistBuddy -c \"print :BugsnagAPIKey\" \"#{default_info_plist_location.first}\"`\n api_key = plist_buddy_response if $?.success?\nend\n\ndsyms = Dir[\"#{ENV[\"DWARF_DSYM_FOLDER_PATH\"]}/*/Contents/Resources/DWARF/*\"]\n\nfork do\n Process.setsid\n STDIN.reopen(\"/dev/null\")\n STDOUT.reopen(\"/dev/null\", \"a\")\n STDERR.reopen(\"/dev/null\", \"a\")\n\n require 'shellwords'\n\n dsyms.each do |dsym|\n curl_command = \"curl -F dsym=@#{Shellwords.escape(dsym)} -F projectRoot=#{Shellwords.escape(ENV[\"PROJECT_DIR\"])} \"\n curl_command += \"-F apiKey=#{Shellwords.escape(api_key)} \" if api_key\n curl_command += \"https://upload.bugsnag.com/\"\n system(curl_command)\n end\nend\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -942,11 +948,12 @@ F4295836C8AF75547C675E8D /* ReleasedObjectScenario.m in Sources */, 01E5EAD225B713990066EA8A /* OOMScenario.m in Sources */, 8A530CCC22FDDBF000F0C108 /* ManyConcurrentNotifyScenario.m in Sources */, - F42958881D3F34A76CADE4EC /* SwiftCrash.swift in Sources */, + F42958881D3F34A76CADE4EC /* SwiftCrashScenario.swift in Sources */, E7FDB6C6264D5AD800BCF881 /* AutoNotifyFalseAbortScenario.swift in Sources */, E7A324EA247E9DA5008B0052 /* BreadcrumbCallbackOverrideScenario.swift in Sources */, F42955DB6D08642528917FAB /* CxxExceptionScenario.mm in Sources */, - 8A3B5F2B240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift in Sources */, + 017B4134276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m in Sources */, + 8A3B5F2B240807EE00CE4A3A /* ModifyBreadcrumbInNotifyScenario.swift in Sources */, CBB787912578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m in Sources */, 8A32DB8222424E3000EDD92F /* NSExceptionShiftScenario.m in Sources */, F42954B7318A02824C65C514 /* ObjCMsgSendScenario.m in Sources */, @@ -958,9 +965,9 @@ E700EE6C247D793A008CFFB6 /* SIGPIPEScenario.m in Sources */, E700EE50247D15DE008CFFB6 /* UserFromConfigSessionScenario.swift in Sources */, A1117E552535A59100014FDA /* OOMLoadScenario.swift in Sources */, - 8A840FBA21AF5C450041DBFA /* SwiftAssertion.swift in Sources */, + 8A840FBA21AF5C450041DBFA /* SwiftAssertionScenario.swift in Sources */, E753F24824927412001FB671 /* OnSendErrorCallbackCrashScenario.swift in Sources */, - 01847DD626453D4E00ADA4C7 /* InternalErrorReportingScenarios.m in Sources */, + 01847DD626453D4E00ADA4C7 /* InvalidCrashReportScenario.m in Sources */, 001E5502243B8FDA0009E31D /* AutoCaptureRunScenario.m in Sources */, 0104085F258CA0A100933C60 /* DispatchCrashScenario.swift in Sources */, E700EE55247D3204008CFFB6 /* OnSendOverwriteScenario.swift in Sources */, @@ -985,6 +992,7 @@ E7A324DA247E70C4008B0052 /* SessionCallbackCrashScenario.swift in Sources */, E7DD40452473D980000EDC14 /* UserDefaultInfoScenario.swift in Sources */, 01F1474425F282E600C2DC65 /* AppHangScenarios.swift in Sources */, + 0104B47E275A7B3C003EBDF0 /* RecrashScenarios.mm in Sources */, E7A324E3247E7C17008B0052 /* SessionCallbackRemovalScenario.m in Sources */, AAFEF9EA26EB533800980A10 /* NetworkBreadcrumbsScenario.swift in Sources */, E7767F15221C223C0006648C /* NewSessionScenario.swift in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard b/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard index 65bc73c97..646db4529 100644 --- a/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard +++ b/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -35,7 +35,7 @@ - - -