diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a35de87a..4e5150cde0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ - (plugin-console-breadcrumbs): Ensure console breadcrumbs do not run in Expo's dev environment and obscure log line numbers [#1453](https://github.com/bugsnag/bugsnag-js/pull/1453) +### Changed + +- (react-native): Update bugsnag-cocoa to v6.10.0 + - Fix an issue that could cause C++ exceptions with very long descriptions to not be reported. [bugsnag-cocoa#1137](https://github.com/bugsnag/bugsnag-cocoa/pull/1137) + - Improve performance of adding metadata by using async file I/O. [bugsnag-cocoa#1133](https://github.com/bugsnag/bugsnag-cocoa/pull/1133) + - Improve performance of leaving breadcrumbs by using async file I/O. [bugsnag-cocoa#1124](https://github.com/bugsnag/bugsnag-cocoa/pull/1124) + ## 7.10.4 (2021-06-28) ### Fixed diff --git a/packages/react-native/ios/.bugsnag-cocoa-version b/packages/react-native/ios/.bugsnag-cocoa-version index e8cab31868..d233d8e660 100644 --- a/packages/react-native/ios/.bugsnag-cocoa-version +++ b/packages/react-native/ios/.bugsnag-cocoa-version @@ -1 +1 @@ -b75032fbd1e77d5fadc30c339c3d78bec201ef04 +18b712d9ecba80b24728d96db55d089ca8953028 diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json index 850933e74b..88ec764116 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.9.7", + "version": "6.10.0", "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.9.7" + "tag": "v6.10.0" }, "frameworks": [ "Foundation", diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/project.pbxproj b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/project.pbxproj index f3c87fab90..42f57fe0a0 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/project.pbxproj +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/project.pbxproj @@ -675,6 +675,13 @@ 01B14C57251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B14C58251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B6BB7E25D5777F00FC4DE6 /* BugsnagSwiftPublicAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008966B02486D43500DC48C2 /* BugsnagSwiftPublicAPITests.swift */; }; + 01B79DA9267CC4A000C8CC5E /* BSGGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGGlobals.h */; }; + 01B79DAA267CC4A000C8CC5E /* BSGGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGGlobals.h */; }; + 01B79DAB267CC4A000C8CC5E /* BSGGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGGlobals.h */; }; + 01B79DAC267CC4A000C8CC5E /* BSGGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */; }; + 01B79DAD267CC4A000C8CC5E /* BSGGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */; }; + 01B79DAE267CC4A000C8CC5E /* BSGGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */; }; + 01B79DAF267CC4A000C8CC5E /* BSGGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */; }; 01BDB1F525DEBFB200A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; 01BDB1FD25DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; 01BDB20525DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; @@ -1316,6 +1323,8 @@ 0195FC3B256BC81400DE6646 /* BugsnagEvent+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagEvent+Private.h"; sourceTree = ""; }; 0198762E2567D5AB000A7AF3 /* BugsnagStackframe+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagStackframe+Private.h"; sourceTree = ""; }; 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "report-react-native-promise-rejection.json"; sourceTree = ""; }; + 01B79DA7267CC4A000C8CC5E /* BSGGlobals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGGlobals.h; sourceTree = ""; }; + 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGGlobals.m; sourceTree = ""; }; 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadKSCrashReportOperationTests.m; sourceTree = ""; }; 01BDB21425DEC02900A91FAF /* Data */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Data; sourceTree = ""; }; 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSCrashReportWriterTests.m; sourceTree = ""; }; @@ -1820,6 +1829,8 @@ 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */, 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */, 019480C42625EE9800E833ED /* BSGAppKit.h */, + 01B79DA7267CC4A000C8CC5E /* BSGGlobals.h */, + 01B79DA8267CC4A000C8CC5E /* BSGGlobals.m */, 01847D942644140F00ADA4C7 /* BSGInternalErrorReporter.h */, 01847D952644140F00ADA4C7 /* BSGInternalErrorReporter.m */, CBCF77A125010648004AF22A /* BSGJSONSerialization.h */, @@ -2031,6 +2042,7 @@ 008969F02486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, 00896A2F2486DAD100DC48C2 /* BSG_KSCrashIdentifier.h in Headers */, 008969FC2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */, + 01B79DA9267CC4A000C8CC5E /* BSGGlobals.h in Headers */, 008967B82486D9D800DC48C2 /* BugsnagBreadcrumbs.h in Headers */, 008969DE2486DAD100DC48C2 /* BSG_KSCrashC.h in Headers */, 0089699F2486DAD100DC48C2 /* BSG_KSJSONCodecObjC.h in Headers */, @@ -2133,6 +2145,7 @@ 008969F12486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, 00896A302486DAD100DC48C2 /* BSG_KSCrashIdentifier.h in Headers */, 008969FD2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */, + 01B79DAA267CC4A000C8CC5E /* BSGGlobals.h in Headers */, 008967B92486D9D800DC48C2 /* BugsnagBreadcrumbs.h in Headers */, 008969DF2486DAD100DC48C2 /* BSG_KSCrashC.h in Headers */, 008969A02486DAD100DC48C2 /* BSG_KSJSONCodecObjC.h in Headers */, @@ -2235,6 +2248,7 @@ 008969F22486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, 00896A312486DAD100DC48C2 /* BSG_KSCrashIdentifier.h in Headers */, 008969FE2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */, + 01B79DAB267CC4A000C8CC5E /* BSGGlobals.h in Headers */, 008967BA2486D9D800DC48C2 /* BugsnagBreadcrumbs.h in Headers */, 008969E02486DAD100DC48C2 /* BSG_KSCrashC.h in Headers */, 008969A12486DAD100DC48C2 /* BSG_KSJSONCodecObjC.h in Headers */, @@ -2622,6 +2636,7 @@ CBE9062D25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, 010FF28725ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A352486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, + 01B79DAC267CC4A000C8CC5E /* BSGGlobals.m in Sources */, 008969E42486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DA2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, 008969A52486DAD100DC48C2 /* BSG_KSBacktrace.c in Sources */, @@ -2790,6 +2805,7 @@ CBE9062E25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, 010FF28825ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A362486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, + 01B79DAD267CC4A000C8CC5E /* BSGGlobals.m in Sources */, 008969E52486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DB2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, 008969A62486DAD100DC48C2 /* BSG_KSBacktrace.c in Sources */, @@ -2957,6 +2973,7 @@ CBE9062F25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, 010FF28925ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A372486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, + 01B79DAE267CC4A000C8CC5E /* BSGGlobals.m in Sources */, 008969E62486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DC2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, 008969A72486DAD100DC48C2 /* BSG_KSBacktrace.c in Sources */, @@ -3123,6 +3140,7 @@ 00AD1F262486A17900A27979 /* Bugsnag.m in Sources */, 008968CE2486DA9600DC48C2 /* BugsnagThread.m in Sources */, 00AD1F312486A17900A27979 /* BugsnagSessionTracker.m in Sources */, + 01B79DAF267CC4A000C8CC5E /* BSGGlobals.m in Sources */, 008968C62486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBB0928F2519F891007698BC /* BugsnagSystemState.m in Sources */, 008968AE2486DA9600DC48C2 /* BugsnagStateEvent.m in Sources */, diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist new file mode 100644 index 0000000000..9b1774a117 --- /dev/null +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist @@ -0,0 +1,22 @@ + + + + + classNames + + BugsnagBreadcrumbsTest + + testPerformance + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.00675 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/Info.plist b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/Info.plist new file mode 100644 index 0000000000..97eb23796c --- /dev/null +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/Info.plist @@ -0,0 +1,33 @@ + + + + + runDestinationsByUUID + + 7E1A1286-DD8C-4E45-9E88-4335CD976176 + + localComputer + + busSpeedInMHz + 400 + cpuCount + 1 + cpuKind + 8-Core Intel Core i9 + cpuSpeedInMHz + 2300 + logicalCPUCoresPerPackage + 16 + modelCode + MacBookPro16,1 + physicalCPUCoresPerPackage + 8 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + x86_64 + + + + diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h index 7053a2b01a..1657d4b642 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(BugsnagConfiguration *)config; /** - * The current breadcrumbs, loaded from disk. + * The breadcrumbs stored in memory. */ @property (readonly, nonatomic) NSArray *breadcrumbs; @@ -39,12 +39,19 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)addBreadcrumbWithBlock:(BSGBreadcrumbConfiguration)block; +/** + * Store a new serialized breadcrumb. + * + * This method is not intended to be used from other classes, it is exposed to facilitate unit testing. + */ +- (void)addBreadcrumbWithData:(NSData *)data writeToDisk:(BOOL)writeToDisk; + - (NSArray *)breadcrumbsBeforeDate:(NSDate *)date; /** - * Returns the breadcrumb JSON dictionaries stored on disk. + * The breadcrumb stored on disk. */ -- (nullable NSArray *)cachedBreadcrumbs; +- (NSArray *)cachedBreadcrumbs; /** * Removes breadcrumbs from disk. @@ -60,6 +67,7 @@ NS_ASSUME_NONNULL_END /** * Inserts the current breadcrumbs into a crash report. * - * This function is async-signal-safe. + * This function is async-signal-safe, but requires that any threads that could be adding + * breadcrumbs are suspended. */ void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter * _Nonnull writer); diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m index 993222e42b..e1ade02779 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m @@ -6,9 +6,9 @@ // Copyright © 2020 Bugsnag. All rights reserved. // - #import "BugsnagBreadcrumbs.h" +#import "BSGGlobals.h" #import "BSGFileLocations.h" #import "BSGJSONSerialization.h" #import "BSG_KSCrashReportWriter.h" @@ -18,16 +18,18 @@ #import "BugsnagConfiguration+Private.h" #import "BugsnagLogger.h" -/** - * Information that can be accessed in an async-safe manner from the crash handler. - */ -typedef struct { - char directoryPath[PATH_MAX]; - unsigned int firstFileNumber; - unsigned int nextFileNumber; -} BugsnagBreadcrumbsContext; -static BugsnagBreadcrumbsContext g_context; +// +// Breadcrumbs are stored as a linked list of JSON encoded C strings +// so that they are accessible at crash time. +// + +struct bsg_breadcrumb_list_item { + struct bsg_breadcrumb_list_item *next; + char jsonData[]; // MUST be null terminated +}; + +static struct bsg_breadcrumb_list_item *g_breadcrumbs_head; #pragma mark - @@ -55,13 +57,31 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)config { _maxBreadcrumbs = (unsigned int)config.maxBreadcrumbs; _breadcrumbsPath = [BSGFileLocations current].breadcrumbs; - [_breadcrumbsPath getFileSystemRepresentation:g_context.directoryPath maxLength:sizeof(g_context.directoryPath)]; return self; } - (NSArray *)breadcrumbs { - return [self loadBreadcrumbsAsDictionaries:NO] ?: @[]; + NSMutableArray *breadcrumbs = [NSMutableArray array]; + @synchronized (self) { + for (struct bsg_breadcrumb_list_item *item = g_breadcrumbs_head; item != NULL; item = item->next) { + NSError *error = nil; + NSData *data = [NSData dataWithBytesNoCopy:item->jsonData length:strlen(item->jsonData) freeWhenDone:NO]; + id JSONObject = [BSGJSONSerialization JSONObjectWithData:data options:0 error:&error]; + if (!JSONObject) { + bsg_log_err(@"Unable to parse breadcrumb: %@", error); + continue; + } + BugsnagBreadcrumb *breadcrumb = nil; + if (![JSONObject isKindOfClass:[NSDictionary class]] || + !(breadcrumb = [BugsnagBreadcrumb breadcrumbFromDict:JSONObject])) { + bsg_log_err(@"Unexpected breadcrumb payload in buffer"); + continue; + } + [breadcrumbs addObject:breadcrumb]; + } + } + return breadcrumbs; } - (NSArray *)breadcrumbsBeforeDate:(nonnull NSDate *)date { @@ -94,16 +114,71 @@ - (void)addBreadcrumbWithBlock:(BSGBreadcrumbConfiguration)block { if (!data) { return; } - unsigned int fileNumber; + [self addBreadcrumbWithData:data writeToDisk:YES]; +} + +- (void)addBreadcrumbWithData:(NSData *)data writeToDisk:(BOOL)writeToDisk { + struct bsg_breadcrumb_list_item *newItem = calloc(1, sizeof(struct bsg_breadcrumb_list_item) + data.length + 1); + if (!newItem) { + return; + } + [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, __unused BOOL *stop) { + memcpy(newItem->jsonData + byteRange.location, bytes, byteRange.length); + }]; + @synchronized (self) { - fileNumber = self.nextFileNumber; + const unsigned int fileNumber = self.nextFileNumber; + const BOOL deleteOld = fileNumber >= self.maxBreadcrumbs; self.nextFileNumber = fileNumber + 1; - if (fileNumber + 1 > self.maxBreadcrumbs) { - g_context.firstFileNumber = fileNumber + 1 - self.maxBreadcrumbs; + + if (g_breadcrumbs_head) { + struct bsg_breadcrumb_list_item *tail = g_breadcrumbs_head; + while (tail->next) { + tail = tail->next; + } + tail->next = newItem; + if (deleteOld) { + struct bsg_breadcrumb_list_item *head = g_breadcrumbs_head; + g_breadcrumbs_head = head->next; + head->next = NULL; + free(head); + } + } else { + g_breadcrumbs_head = newItem; + } + + if (!writeToDisk) { + return; } - g_context.nextFileNumber = fileNumber + 1; + // + // Breadcrumbs are also stored on disk so that they are accessible at next + // launch if an OOM is detected. + // + dispatch_async(BSGGlobalsFileSystemQueue(), ^{ + // Avoid writing breadcrumbs that have already been deleted from the in-memory store. + // This can occur when breadcrumbs are being added faster than they can be written. + unsigned int nextFileNumber = self.nextFileNumber; + BOOL isStale = (self.maxBreadcrumbs < nextFileNumber) && (fileNumber < (nextFileNumber - self.maxBreadcrumbs)); + + NSError *error = nil; + + if (!isStale) { + NSString *file = [self pathForFileNumber:fileNumber]; + // NSDataWritingAtomic not required because we no longer read the files without checking for validity + if (![data writeToFile:file options:0 error:&error]) { + bsg_log_err(@"Unable to write breadcrumb: %@", error); + } + } + + if (deleteOld) { + NSString *fileToDelete = [self pathForFileNumber:fileNumber - self.maxBreadcrumbs]; + if (![[[NSFileManager alloc] init] removeItemAtPath:fileToDelete error:&error] && + !([error.domain isEqual:NSCocoaErrorDomain] && error.code == NSFileNoSuchFileError)) { + bsg_log_err(@"Unable to delete old breadcrumb: %@", error); + } + } + }); } - [self writeBreadcrumbData:(NSData *)data toFileNumber:fileNumber]; } - (BOOL)shouldSendBreadcrumb:(BugsnagBreadcrumb *)crumb { @@ -121,9 +196,14 @@ - (BOOL)shouldSendBreadcrumb:(BugsnagBreadcrumb *)crumb { - (void)removeAllBreadcrumbs { @synchronized (self) { + struct bsg_breadcrumb_list_item *item = g_breadcrumbs_head; + g_breadcrumbs_head = NULL; + while (item) { + struct bsg_breadcrumb_list_item *next = item->next; + free(item); + item = next; + } self.nextFileNumber = 0; - g_context.firstFileNumber = 0; - g_context.nextFileNumber = 0; } [self deleteBreadcrumbFiles]; } @@ -148,34 +228,13 @@ - (NSString *)pathForFileNumber:(unsigned int)fileNumber { return [self.breadcrumbsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%u.json", fileNumber]]; } -- (void)writeBreadcrumbData:(NSData *)data toFileNumber:(unsigned int)fileNumber { - NSString *path = [self pathForFileNumber:fileNumber]; - - NSError *error = nil; - if (![data writeToFile:path options:NSDataWritingAtomic error:&error]) { - bsg_log_err(@"Unable to write breadcrumb: %@", error); - return; - } - - if (fileNumber >= self.maxBreadcrumbs) { - NSString *oldPath = [self pathForFileNumber:fileNumber - self.maxBreadcrumbs]; - if (![[NSFileManager defaultManager] removeItemAtPath:oldPath error:&error]) { - bsg_log_err(@"Unable to delete old breadcrumb: %@", error); - } - } -} - -- (nullable NSArray *)cachedBreadcrumbs { - return [self loadBreadcrumbsAsDictionaries:YES]; -} - -- (nullable NSArray *)loadBreadcrumbsAsDictionaries:(BOOL)asDictionaries { +- (NSArray *)cachedBreadcrumbs { NSError *error = nil; NSArray *filenames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.breadcrumbsPath error:&error]; if (!filenames) { bsg_log_err(@"Unable to read breadcrumbs: %@", error); - return nil; + return @[]; } // We cannot use NSString's -localizedStandardCompare: because its sorting may vary by locale. @@ -187,7 +246,7 @@ - (nullable NSArray *)loadBreadcrumbsAsDictionaries:(BOOL)asDictionaries { return NSOrderedSame; }]; - NSMutableArray *breadcrumbs = [NSMutableArray array]; + NSMutableArray *breadcrumbs = [NSMutableArray array]; for (NSString *file in filenames) { if ([file hasPrefix:@"."] || ![file.pathExtension isEqual:@"json"]) { @@ -214,7 +273,7 @@ - (nullable NSArray *)loadBreadcrumbsAsDictionaries:(BOOL)asDictionaries { bsg_log_err(@"Unexpected breadcrumb payload in file %@", file); continue; } - [breadcrumbs addObject:asDictionaries ? JSONObject : breadcrumb]; + [breadcrumbs addObject:breadcrumb]; } return breadcrumbs; @@ -234,15 +293,11 @@ - (void)deleteBreadcrumbFiles { #pragma mark - void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter *writer) { - char path[PATH_MAX]; writer->beginArray(writer, "breadcrumbs"); - for (unsigned int i = g_context.firstFileNumber; i < g_context.nextFileNumber; i++) { - int result = snprintf(path, sizeof(path), "%s/%u.json", g_context.directoryPath, i); - if (result < 0 || result >= (int)sizeof(path)) { - bsg_log_err(@"Breadcrumb path is too long"); - continue; - } - writer->addJSONFileElement(writer, NULL, path); + + for (struct bsg_breadcrumb_list_item *item = g_breadcrumbs_head; item != NULL; item = item->next) { + writer->addJSONElement(writer, NULL, item->jsonData); } + writer->endContainer(writer); } diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.h index caafe6a5b8..bb4d5b23a1 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.h @@ -9,6 +9,7 @@ #import #import "BugsnagConfiguration.h" +#import "BugsnagKeys.h" #define SYSTEMSTATE_KEY_APP @"app" #define SYSTEMSTATE_KEY_DEVICE @"device" @@ -16,6 +17,7 @@ #define SYSTEMSTATE_APP_WAS_TERMINATED @"wasTerminated" #define SYSTEMSTATE_APP_IS_ACTIVE @"isActive" #define SYSTEMSTATE_APP_IS_IN_FOREGROUND @"inForeground" +#define SYSTEMSTATE_APP_IS_LAUNCHING BSGKeyIsLaunching #define SYSTEMSTATE_APP_VERSION @"version" #define SYSTEMSTATE_APP_BUNDLE_VERSION @"bundleVersion" #define SYSTEMSTATE_APP_DEBUGGER_IS_ACTIVE @"debuggerIsActive" @@ -38,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) NSUInteger consecutiveLaunchCrashes; +- (void)markLaunchCompleted; + /** * Purge all stored system state. */ diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.m index 94cbf49930..89123aaabb 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/BugsnagSystemState.m @@ -19,12 +19,12 @@ #import #import "BSGFileLocations.h" +#import "BSGGlobals.h" #import "BSGJSONSerialization.h" #import "BSG_KSMach.h" #import "BSG_KSSystemInfo.h" #import "BSG_RFC3339DateTool.h" #import "BugsnagKVStoreObjC.h" -#import "BugsnagKeys.h" #import "BugsnagLogger.h" #import "BugsnagSessionTracker.h" #import "BugsnagSystemState.h" @@ -56,10 +56,16 @@ NSMutableDictionary *app = state[SYSTEMSTATE_KEY_APP]; // KV-store versions of these are authoritative - app[SYSTEMSTATE_APP_WAS_TERMINATED] = [kvstore NSBooleanForKey:SYSTEMSTATE_APP_WAS_TERMINATED defaultValue:false]; - app[SYSTEMSTATE_APP_IS_ACTIVE] = [kvstore NSBooleanForKey:SYSTEMSTATE_APP_IS_ACTIVE defaultValue:false]; - app[SYSTEMSTATE_APP_IS_IN_FOREGROUND] = [kvstore NSBooleanForKey:SYSTEMSTATE_APP_IS_IN_FOREGROUND defaultValue:false]; - app[SYSTEMSTATE_APP_DEBUGGER_IS_ACTIVE] = [kvstore NSBooleanForKey:SYSTEMSTATE_APP_DEBUGGER_IS_ACTIVE defaultValue:false]; + for (NSString *key in @[SYSTEMSTATE_APP_DEBUGGER_IS_ACTIVE, + SYSTEMSTATE_APP_IS_ACTIVE, + SYSTEMSTATE_APP_IS_IN_FOREGROUND, + SYSTEMSTATE_APP_IS_LAUNCHING, + SYSTEMSTATE_APP_WAS_TERMINATED]) { + NSNumber *value = [kvstore NSBooleanForKey:key defaultValue:nil]; + if (value != nil) { + app[key] = value; + } + } return state; } @@ -89,6 +95,7 @@ id blankIfNil(id value) { [kvstore deleteKey:SYSTEMSTATE_APP_WAS_TERMINATED]; [kvstore setBoolean:isActive forKey:SYSTEMSTATE_APP_IS_ACTIVE]; [kvstore setBoolean:isInForeground forKey:SYSTEMSTATE_APP_IS_IN_FOREGROUND]; + [kvstore setBoolean:true forKey:SYSTEMSTATE_APP_IS_LAUNCHING]; [kvstore setBoolean:isBeingDebugged forKey:SYSTEMSTATE_APP_DEBUGGER_IS_ACTIVE]; NSMutableDictionary *app = [NSMutableDictionary new]; @@ -98,6 +105,7 @@ id blankIfNil(id value) { app[BSGKeyVersion] = blankIfNil(systemInfo[@BSG_KSSystemField_BundleShortVersion]); app[BSGKeyBundleVersion] = blankIfNil(systemInfo[@BSG_KSSystemField_BundleVersion]); app[BSGKeyMachoUUID] = [BSG_KSSystemInfo appUUID]; + app[@"binaryArch"] = systemInfo[@BSG_KSSystemField_BinaryArch]; app[@"inForeground"] = @(isInForeground); app[@"isActive"] = @(isActive); #if BSG_PLATFORM_TVOS @@ -167,7 +175,7 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)config { _currentLaunchStateRW = initCurrentState(_kvStore, config); _currentLaunchState = [_currentLaunchStateRW copy]; _consecutiveLaunchCrashes = [_lastLaunchState[InternalKey][ConsecutiveLaunchCrashesKey] unsignedIntegerValue]; - [self sync]; + [self syncState:_currentLaunchState]; __weak __typeof__(self) weakSelf = self; NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; @@ -246,6 +254,10 @@ - (void)setConsecutiveLaunchCrashes:(NSUInteger)consecutiveLaunchCrashes { [self setValue:@(_consecutiveLaunchCrashes = consecutiveLaunchCrashes) forKey:ConsecutiveLaunchCrashesKey inSection:InternalKey]; } +- (void)markLaunchCompleted { + [self.kvStore setBoolean:false forKey:SYSTEMSTATE_APP_IS_LAUNCHING]; +} + - (void)setValue:(id)value forAppKey:(NSString *)key { [self setValue:value forKey:key inSection:SYSTEMSTATE_KEY_APP]; } @@ -260,32 +272,26 @@ - (void)setValue:(id)value forKey:(NSString *)key inSection:(NSString *)section }]; } -- (void)mutateLaunchState:(void (^)(NSMutableDictionary *state))block { +- (void)mutateLaunchState:(nonnull void (^)(NSMutableDictionary *state))block { + NSDictionary *state = nil; + @synchronized (self) { + block(self.currentLaunchStateRW); + // User-facing state should never mutate from under them. + self.currentLaunchState = copyDictionary(self.currentLaunchStateRW); + state = self.currentLaunchState; + } // Run on a BG thread so we don't monopolize the notification queue. - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ - @synchronized (self) { - block(self.currentLaunchStateRW); - // User-facing state should never mutate from under them. - self.currentLaunchState = copyDictionary(self.currentLaunchStateRW); - } - [self sync]; + dispatch_async(BSGGlobalsFileSystemQueue(), ^(void){ + [self syncState:state]; }); } -- (void)sync { - NSDictionary *state = self.currentLaunchState; - NSError *error = nil; +- (void)syncState:(NSDictionary *)state { NSAssert([BSGJSONSerialization isValidJSONObject:state], @"BugsnagSystemState cannot be converted to JSON data"); - if (![BSGJSONSerialization isValidJSONObject:state]) { - bsg_log_err(@"System state cannot be written as JSON"); - return; - } - NSData *data = [BSGJSONSerialization dataWithJSONObject:state options:0 error:&error]; - if (error) { + NSError *error = nil; + if (![BSGJSONSerialization writeJSONObject:state toFile:self.persistenceFilePath options:0 error:&error]) { bsg_log_err(@"System state cannot be written as JSON: %@", error); - return; } - [data writeToFile:self.persistenceFilePath atomically:YES]; } - (void)purge { diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m index 914ea447e2..ec5e240bd6 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Client/BugsnagClient.m @@ -32,6 +32,7 @@ #import "BSGConnectivity.h" #import "BSGEventUploader.h" #import "BSGFileLocations.h" +#import "BSGGlobals.h" #import "BSGInternalErrorReporter.h" #import "BSGJSONSerialization.h" #import "BSGNotificationBreadcrumbs.h" @@ -92,12 +93,12 @@ // Contains the state of the event (handled/unhandled) char *handledState; // Contains the user-specified metadata, including the user tab from config. - char *metadataPath; + char *metadataJSON; // Contains the Bugsnag configuration, all under the "config" tab. char *configPath; - // Contains notifier state, under "deviceState" and crash-specific + // Contains notifier state under "deviceState", and crash-specific // information under "crash". - char *statePath; + char *stateJSON; // User onCrash handler void (*onCrash)(const BSG_KSCrashReportWriter *writer); } bsg_g_bugsnag_data; @@ -128,8 +129,8 @@ void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer, __attrib } if (isCrash) { writer->addJSONFileElement(writer, "config", bsg_g_bugsnag_data.configPath); - writer->addJSONFileElement(writer, "metaData", bsg_g_bugsnag_data.metadataPath); - writer->addJSONFileElement(writer, "state", bsg_g_bugsnag_data.statePath); + writer->addJSONElement(writer, "metaData", bsg_g_bugsnag_data.metadataJSON); + writer->addJSONElement(writer, "state", bsg_g_bugsnag_data.stateJSON); BugsnagBreadcrumbsWriteCrashReport(writer); if (watchdogSentinelPath != NULL) { // Delete the file to indicate a handled termination @@ -249,11 +250,9 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration { _configMetadataFromLastLaunch = [BSGJSONSerialization JSONObjectWithContentsOfFile:_configMetadataFile options:0 error:nil]; _metadataFile = fileLocations.metadata; - bsg_g_bugsnag_data.metadataPath = strdup(_metadataFile.fileSystemRepresentation); _metadataFromLastLaunch = [BSGJSONSerialization JSONObjectWithContentsOfFile:_metadataFile options:0 error:nil]; _stateMetadataFile = fileLocations.state; - bsg_g_bugsnag_data.statePath = strdup(_stateMetadataFile.fileSystemRepresentation); _stateMetadataFromLastLaunch = [BSGJSONSerialization JSONObjectWithContentsOfFile:_stateMetadataFile options:0 error:nil]; self.stateEventBlocks = [NSMutableArray new]; @@ -284,20 +283,8 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration { _lastOrientation = BSGOrientationNameFromEnum([UIDEVICE currentDevice].orientation); [self.state addMetadata:_lastOrientation withKey:BSGKeyOrientation toSection:BSGKeyDeviceState]; #endif - // sync initial state - [self metadataChanged:self.metadata]; - [self metadataChanged:self.state]; - - // add observers for future metadata changes - // weakSelf is used as the BugsnagClient will always be instantiated - // for the entire lifecycle of an application, and there is therefore - // no need to check for strong self - __weak __typeof__(self) weakSelf = self; - void (^observer)(BugsnagStateEvent *) = ^(BugsnagStateEvent *event) { - [weakSelf metadataChanged:event.data]; - }; - [self.metadata addObserverWithBlock:observer]; - [self.state addObserverWithBlock:observer]; + [self.metadata setStorageBuffer:&bsg_g_bugsnag_data.metadataJSON file:_metadataFile]; + [self.state setStorageBuffer:&bsg_g_bugsnag_data.stateJSON file:_stateMetadataFile]; self.pluginClient = [[BugsnagPluginClient alloc] initWithPlugins:self.configuration.plugins client:self]; @@ -443,6 +430,7 @@ - (void)markLaunchCompleted { bsg_log_debug(@"App has finished launching"); [self.appLaunchTimer invalidate]; [self.state addMetadata:@NO withKey:BSGKeyIsLaunching toSection:BSGKeyApp]; + [self.systemState markLaunchCompleted]; } - (void)sendLaunchCrashSynchronously { @@ -575,8 +563,14 @@ - (void)computeDidCrashLastLaunch { self.appDidCrashLastLaunch = didCrash; - BOOL wasLaunching = [self.stateMetadataFromLastLaunch[BSGKeyApp][BSGKeyIsLaunching] boolValue]; - BOOL didCrashDuringLaunch = didCrash && wasLaunching; + NSNumber *wasLaunching = ({ + // BugsnagSystemState's KV-store is now the reliable source of the isLaunching status. + self.systemState.lastLaunchState[SYSTEMSTATE_KEY_APP][SYSTEMSTATE_APP_IS_LAUNCHING] ?: + // Earlier notifier versions stored it only in state.json - but due to async I/O this is no longer accurate. + self.stateMetadataFromLastLaunch[BSGKeyApp][BSGKeyIsLaunching]; + }); + + BOOL didCrashDuringLaunch = didCrash && wasLaunching.boolValue; if (didCrashDuringLaunch) { self.systemState.consecutiveLaunchCrashes++; } else { @@ -964,16 +958,6 @@ - (void)addBreadcrumbWithBlock:(void (^)(BugsnagBreadcrumb *))block { [self.breadcrumbs addBreadcrumbWithBlock:block]; } -- (void)metadataChanged:(BugsnagMetadata *)metadata { - @synchronized(metadata) { - if (metadata == self.metadata) { - [BSGJSONSerialization writeJSONObject:[metadata toDictionary] toFile:self.metadataFile options:0 error:nil]; - } else if (metadata == self.state) { - [BSGJSONSerialization writeJSONObject:[metadata toDictionary] toFile:self.stateMetadataFile options:0 error:nil]; - } - } -} - /** * Update the device status in response to a battery change notification * @@ -1347,7 +1331,7 @@ - (BugsnagEvent *)generateOutOfMemoryEvent { handledState:[BugsnagHandledState handledStateWithSeverityReason:LikelyOutOfMemory] user:session.user ?: [[BugsnagUser alloc] init] metadata:metadata - breadcrumbs:self.breadcrumbs.breadcrumbs ?: @[] + breadcrumbs:[self.breadcrumbs cachedBreadcrumbs] ?: @[] errors:@[error] threads:@[] session:session]; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploadFileOperation.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploadFileOperation.m index 8cd99cd9f7..5a87867c64 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploadFileOperation.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploadFileOperation.m @@ -9,6 +9,7 @@ #import "BSGEventUploadFileOperation.h" #import "BSGFileLocations.h" +#import "BSGGlobals.h" #import "BSGJSONSerialization.h" #import "BugsnagEvent+Private.h" #import "BugsnagLogger.h" @@ -32,12 +33,14 @@ - (BugsnagEvent *)loadEventAndReturnError:(NSError * __autoreleasing *)errorPtr } - (void)deleteEvent { - NSError *error = nil; - if ([NSFileManager.defaultManager removeItemAtPath:self.file error:&error]) { - bsg_log_debug(@"Deleted event %@", self.name); - } else { - bsg_log_err(@"%@", error); - } + dispatch_sync(BSGGlobalsFileSystemQueue(), ^{ + NSError *error = nil; + if ([NSFileManager.defaultManager removeItemAtPath:self.file error:&error]) { + bsg_log_debug(@"Deleted event %@", self.name); + } else { + bsg_log_err(@"%@", error); + } + }); } - (void)storeEventPayload:(__attribute__((unused)) NSDictionary *)eventPayload { diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploader.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploader.m index 66ec201997..534d43bed4 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploader.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Delivery/BSGEventUploader.m @@ -11,6 +11,7 @@ #import "BSGEventUploadKSCrashReportOperation.h" #import "BSGEventUploadObjectOperation.h" #import "BSGFileLocations.h" +#import "BSGGlobals.h" #import "BSGJSONSerialization.h" #import "BugsnagConfiguration.h" #import "BugsnagEvent+Private.h" @@ -197,13 +198,15 @@ - (void)deleteExcessFiles:(NSMutableArray *)sortedEventFiles { // MARK: - BSGEventUploadOperationDelegate - (void)storeEventPayload:(NSDictionary *)eventPayload { - NSString *file = [[self.eventsDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"json"]; - NSError *error = nil; - if (![BSGJSONSerialization writeJSONObject:eventPayload toFile:file options:0 error:&error]) { - bsg_log_err(@"Error encountered while saving event payload for retry: %@", error); - return; - } - [self deleteExcessFiles:[self sortedEventFiles]]; + dispatch_sync(BSGGlobalsFileSystemQueue(), ^{ + NSString *file = [[self.eventsDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"json"]; + NSError *error = nil; + if (![BSGJSONSerialization writeJSONObject:eventPayload toFile:file options:0 error:&error]) { + bsg_log_err(@"Error encountered while saving event payload for retry: %@", error); + return; + } + [self deleteExcessFiles:[self sortedEventFiles]]; + }); } @end diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.h new file mode 100644 index 0000000000..77a5eae525 --- /dev/null +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.h @@ -0,0 +1,15 @@ +// +// BSGGlobals.h +// Bugsnag +// +// Created by Nick Dowell on 18/06/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import + +__BEGIN_DECLS + +dispatch_queue_t BSGGlobalsFileSystemQueue(void); + +__END_DECLS diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.m new file mode 100644 index 0000000000..ecc90c8450 --- /dev/null +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BSGGlobals.m @@ -0,0 +1,23 @@ +// +// BSGGlobals.m +// Bugsnag +// +// Created by Nick Dowell on 18/06/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGGlobals.h" + +static dispatch_queue_t bsg_g_fileSystemQueue; + +static void BSGGlobalsInit(void) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bsg_g_fileSystemQueue = dispatch_queue_create("com.bugsnag.filesystem", DISPATCH_QUEUE_SERIAL); + }); +} + +dispatch_queue_t BSGGlobalsFileSystemQueue(void) { + BSGGlobalsInit(); + return bsg_g_fileSystemQueue; +} diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.h index 45a4db8ae6..bb70af8df0 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN - (bool)booleanForKey:(NSString*)key defaultValue:(bool)defaultValue; -- (NSNumber*)NSBooleanForKey:(NSString*)key defaultValue:(bool)defaultValue; +- (nullable NSNumber*)NSBooleanForKey:(NSString*)key defaultValue:(nullable NSNumber*)defaultValue; - (void)setString:(NSString*)value forKey:(NSString*)key; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.m index 80f1da594f..c51b3e8598 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Helpers/BugsnagKVStoreObjC.m @@ -63,8 +63,13 @@ - (bool)booleanForKey:(NSString*)key defaultValue:(bool)defaultValue { return value; } -- (NSNumber*)NSBooleanForKey:(NSString*)key defaultValue:(bool)defaultValue { - return [NSNumber numberWithBool:[self booleanForKey:key defaultValue:defaultValue]]; +- (nullable NSNumber*)NSBooleanForKey:(NSString*)key defaultValue:(nullable NSNumber*)defaultValue { + int err = 0; + bool boolValue = bsgkv_getBoolean([key UTF8String], &err); + if (err != 0) { + return defaultValue; + } + return [NSNumber numberWithBool:boolValue]; } - (void)setString:(NSString*)value forKey:(NSString*)key { diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm index c6ff49e2d1..8e7ced8bd8 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm @@ -129,7 +129,9 @@ static void CPPExceptionTerminate(void) { isNSException = true; bsg_recordException(exception); } catch (std::exception &exc) { - strncpy(descriptionBuff, exc.what(), sizeof(descriptionBuff)); + strlcpy(descriptionBuff, exc.what(), sizeof(descriptionBuff)); + } catch (std::exception *exc) { + strlcpy(descriptionBuff, exc->what(), sizeof(descriptionBuff)); } #define CATCH_VALUE(TYPE, PRINTFTYPE) \ catch (TYPE value) { \ diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Metadata/BugsnagMetadata.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Metadata/BugsnagMetadata.m index 62d201d955..49b5102862 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Metadata/BugsnagMetadata.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Metadata/BugsnagMetadata.m @@ -26,14 +26,26 @@ #import "BugsnagMetadata+Private.h" +#import "BSGGlobals.h" +#import "BSGJSONSerialization.h" #import "BSGSerialization.h" #import "BugsnagLogger.h" #import "BugsnagStateEvent.h" + @interface BugsnagMetadata () + @property(atomic, readwrite, strong) NSMutableArray *stateEventBlocks; + +@property (assign, nonatomic) char **buffer; +@property (copy, nonatomic) NSString *file; +@property (copy, nonatomic) NSData *pendingWrite; + @end + +// MARK: - + @implementation BugsnagMetadata - (instancetype)init { @@ -91,7 +103,9 @@ - (NSMutableArray *)sanitizeArray:(NSArray *)obj { } - (NSDictionary *)toDictionary { - return [self.dictionary mutableCopy]; + @synchronized (self) { + return [self.dictionary mutableCopy]; + } } - (void)notifyObservers { @@ -190,7 +204,7 @@ - (void)addMetadata:(NSDictionary *)metadataValues } if (![oldValue isEqual:metadata]) { self.dictionary[sectionName] = metadata.count ? metadata : nil; - [self notifyObservers]; + [self didChangeValue]; } } } @@ -214,8 +228,8 @@ - (void)clearMetadataFromSection:(NSString *)sectionName { @synchronized(self) { [self.dictionary removeObjectForKey:sectionName]; + [self didChangeValue]; } - [self notifyObservers]; } - (void)clearMetadataFromSection:(NSString *)section @@ -223,8 +237,84 @@ - (void)clearMetadataFromSection:(NSString *)section { @synchronized(self) { [(NSMutableDictionary *)self.dictionary[section] removeObjectForKey:key]; + [self didChangeValue]; + } +} + +// MARK: - + +- (void)didChangeValue { + if (self.buffer || self.file) { + [self serialize]; } [self notifyObservers]; } +- (void)setStorageBuffer:(char * _Nullable *)buffer file:(NSString *)file { + self.buffer = buffer; + self.file = file; + [self serialize]; +} + +- (void)serialize { + NSError *error = nil; + NSData *data = [BSGJSONSerialization dataWithJSONObject:[self toDictionary] options:0 error:&error]; + if (!data) { + bsg_log_err(@"%s: %@", __FUNCTION__, error); + return; + } + if (self.buffer) { + [self writeData:data toBuffer:self.buffer]; + } + if (self.file) { + [self writeData:data toFile:self.file]; + } +} + +// +// Metadata is stored in memory as a JSON encoded C string so that it is accessible at crash time. +// +- (void)writeData:(NSData *)data toBuffer:(char **)buffer { + char *newbuffer = calloc(1, data.length + 1); + if (!newbuffer) { + return; + } + [data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, __unused BOOL * _Nonnull stop) { + memcpy(newbuffer + byteRange.location, bytes, byteRange.length); + }]; + char *oldbuffer = *buffer; + *buffer = newbuffer; + free(oldbuffer); +} + +// +// Metadata is also stored on disk so that it is accessible at next launch if an OOM is detected. +// +- (void)writeData:(NSData *)data toFile:(NSString *)file { + self.pendingWrite = data; + + dispatch_async(BSGGlobalsFileSystemQueue(), ^{ + NSData *pendingWrite; + + @synchronized (self) { + if (!self.pendingWrite) { + // The latest data has already been written to disk. + return; + } + pendingWrite = self.pendingWrite; + } + + NSError *error = nil; + if (![pendingWrite writeToFile:(NSString *_Nonnull)file options:NSDataWritingAtomic error:&error]) { + bsg_log_err(@"%s: %@", __FUNCTION__, error); + } + + @synchronized (self) { + if (self.pendingWrite == pendingWrite) { + self.pendingWrite = nil; + } + } + }); +} + @end diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagApp.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagApp.m index ea0d97f13d..c4777503dd 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagApp.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagApp.m @@ -21,7 +21,6 @@ NSDictionary *BSGParseAppMetadata(NSDictionary *event) { NSMutableDictionary *app = [NSMutableDictionary new]; app[@"name"] = [event valueForKeyPath:@"system." BSG_KSSystemField_BundleExecutable]; - app[@"binaryArch"] = [event valueForKeyPath:@"system." BSG_KSSystemField_BinaryArch]; app[@"runningOnRosetta"] = [event valueForKeyPath:@"system." BSG_KSSystemField_Translated]; return app; } @@ -31,6 +30,7 @@ @implementation BugsnagApp + (BugsnagApp *)deserializeFromJson:(NSDictionary *)json { BugsnagApp *app = [BugsnagApp new]; if (json != nil) { + app.binaryArch = json[@"binaryArch"]; app.bundleVersion = json[@"bundleVersion"]; app.codeBundleId = json[@"codeBundleId"]; app.id = json[@"id"]; @@ -61,6 +61,7 @@ + (void)populateFields:(BugsnagApp *)app { NSDictionary *system = event[BSGKeySystem]; app.id = system[@BSG_KSSystemField_BundleID]; + app.binaryArch = system[@BSG_KSSystemField_BinaryArch]; app.bundleVersion = system[@BSG_KSSystemField_BundleVersion]; app.dsymUuid = system[@BSG_KSSystemField_AppUUID]; app.version = system[@BSG_KSSystemField_BundleShortVersion]; @@ -87,6 +88,7 @@ - (void)setValuesFromConfiguration:(BugsnagConfiguration *)configuration - (NSDictionary *)toDict { NSMutableDictionary *dict = [NSMutableDictionary new]; + dict[@"binaryArch"] = self.binaryArch; dict[@"bundleVersion"] = self.bundleVersion; dict[@"codeBundleId"] = self.codeBundleId; dict[@"dsymUUIDs"] = BSGArrayWithObject(self.dsymUuid); diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagAppWithState.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagAppWithState.m index 6f5a5959dd..5f2c563af7 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagAppWithState.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagAppWithState.m @@ -38,6 +38,7 @@ + (BugsnagAppWithState *)appFromJson:(NSDictionary *)json { app.dsymUuid = dsyms[0]; } + app.binaryArch = json[@"binaryArch"]; app.bundleVersion = json[@"bundleVersion"]; app.codeBundleId = json[@"codeBundleId"]; app.id = json[@"id"]; diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagBreadcrumb.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagBreadcrumb.m index c8f252f67a..b178656643 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagBreadcrumb.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagBreadcrumb.m @@ -98,93 +98,39 @@ - (BOOL)isValid { } - (NSDictionary *)objectValue { - @synchronized (self) { - NSString *timestamp = self.timestampString ?: [BSG_RFC3339DateTool stringFromDate:self.timestamp]; - if (timestamp && self.message.length > 0) { - NSMutableDictionary *metadata = [NSMutableDictionary new]; - for (NSString *key in self.metadata) { - metadata[[key copy]] = [self.metadata[key] copy]; - } - return @{ - // Note: The Bugsnag Error Reporting API specifies that the breadcrumb "message" - // field should be delivered in as a "name" field. This comment notes that variance. - BSGKeyName : [self.message copy], - BSGKeyTimestamp : timestamp, - BSGKeyType : BSGBreadcrumbTypeValue(self.type), - BSGKeyMetadata : metadata - }; + NSString *timestamp = self.timestampString ?: [BSG_RFC3339DateTool stringFromDate:self.timestamp]; + if (timestamp && self.message.length > 0) { + NSMutableDictionary *metadata = [NSMutableDictionary new]; + for (NSString *key in self.metadata) { + metadata[[key copy]] = [self.metadata[key] copy]; } - return nil; + return @{ + // Note: The Bugsnag Error Reporting API specifies that the breadcrumb "message" + // field should be delivered in as a "name" field. This comment notes that variance. + BSGKeyName : [self.message copy], + BSGKeyTimestamp : timestamp, + BSGKeyType : BSGBreadcrumbTypeValue(self.type), + BSGKeyMetadata : metadata + }; } + return nil; } // The timestamp is lazily computed from the timestampString to avoid unnecessary // calls to -dateFromString: (which is expensive) when loading breadcrumbs from disk. - (NSDate *)timestamp { - @synchronized (self) { - if (!_timestamp) { - _timestamp = [BSG_RFC3339DateTool dateFromString:self.timestampString]; - } - return _timestamp; + if (!_timestamp) { + _timestamp = [BSG_RFC3339DateTool dateFromString:self.timestampString]; } + return _timestamp; } @synthesize timestampString = _timestampString; - (void)setTimestampString:(NSString *)timestampString { - @synchronized (self) { - _timestampString = [timestampString copy]; - self.timestamp = nil; - } -} - -@synthesize message = _message; - -- (NSString *)message { - @synchronized (self) { - return _message; - } -} - -@synthesize type = _type; - -- (BSGBreadcrumbType)type { - @synchronized (self) { - return _type; - } -} - -- (void)setType:(BSGBreadcrumbType)type { - @synchronized (self) { - [self willChangeValueForKey:NSStringFromSelector(@selector(type))]; - _type = type; - [self didChangeValueForKey:NSStringFromSelector(@selector(type))]; - } -} - -- (void)setMessage:(NSString *)message { - @synchronized (self) { - [self willChangeValueForKey:NSStringFromSelector(@selector(message))]; - _message = message; - [self didChangeValueForKey:NSStringFromSelector(@selector(message))]; - } -} - -@synthesize metadata = _metadata; - -- (NSDictionary *)metadata { - @synchronized (self) { - return _metadata; - } -} - -- (void)setMetadata:(NSDictionary *)metadata { - @synchronized (self) { - [self willChangeValueForKey:NSStringFromSelector(@selector(metadata))]; - _metadata = metadata; - [self didChangeValueForKey:NSStringFromSelector(@selector(metadata))]; - } + _timestampString = [timestampString copy]; + self.timestamp = nil; } + (instancetype)breadcrumbWithBlock:(BSGBreadcrumbConfiguration)block { diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m index 8d6230c7b3..d069a10765 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else self.name = @"Bugsnag Objective-C"; #endif - self.version = @"6.9.7"; + self.version = @"6.10.0"; self.url = @"https://github.com/bugsnag/bugsnag-cocoa"; self.dependencies = [NSMutableArray new]; } diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagApp.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagApp.h index 3bdba32858..04d6dc1d2a 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagApp.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagApp.h @@ -14,6 +14,11 @@ */ @interface BugsnagApp : NSObject +/** + * The architecture of the running binary + */ +@property (copy, nullable, nonatomic) NSString *binaryArch; + /** * The bundle version used by the application */ diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagMetadata.h b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagMetadata.h index e34cc98cff..a19ab95cc3 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagMetadata.h +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Bugsnag/include/Bugsnag/BugsnagMetadata.h @@ -28,7 +28,19 @@ #import +NS_ASSUME_NONNULL_BEGIN + /// :nodoc: @interface BugsnagMetadata : NSObject -- (instancetype _Nonnull)initWithDictionary:(NSDictionary *_Nonnull)dict; + +- (instancetype)initWithDictionary:(NSDictionary *)dict; + +/// Configures the metadata object to serialize itself to the provided buffer and file immediately, and upon each change. +- (void)setStorageBuffer:(char *_Nullable *_Nullable)buffer file:(nullable NSString *)file; + +/// Exposed to facilitate unit testing. +- (void)writeData:(NSData *)data toBuffer:(char *_Nullable *_Nonnull)buffer; + @end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md b/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md index 45c0f40e91..319cabc8cc 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/CHANGELOG.md @@ -1,6 +1,19 @@ Changelog ========= +## 6.10.0 (2021-06-30) + +### Bug fixes + +* Fix an issue that could cause C++ exceptions with very long descriptions to not be reported. + [#1137](https://github.com/bugsnag/bugsnag-cocoa/pull/1137) + +* Improve performance of adding metadata by using async file I/O. + [#1133](https://github.com/bugsnag/bugsnag-cocoa/pull/1133) + +* Improve performance of leaving breadcrumbs by using async file I/O. + [#1124](https://github.com/bugsnag/bugsnag-cocoa/pull/1124) + ## 6.9.7 (2021-06-23) ### Bug fixes diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist b/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist index cfa68af1c8..868b0a1a9e 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.9.7 + 6.10.0 CFBundleVersion 1 diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION b/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION index aa902ca825..cf79bf90ee 100644 --- a/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION +++ b/packages/react-native/ios/vendor/bugsnag-cocoa/VERSION @@ -1 +1 @@ -6.9.7 +6.10.0