diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0ad6b784c..e3dc8823d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Crash in Client when reading integrations (#2398) - Don't update session for dropped events (#2374) ## 7.31.1 diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 75695df6b91..63668aff7f3 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -40,8 +40,8 @@ 0A1B497328E597DD00D7BFA3 /* TestLogOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1B497228E597DD00D7BFA3 /* TestLogOutput.swift */; }; 0A1C3592287D7107007D01E3 /* SentryMetaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1C3591287D7107007D01E3 /* SentryMetaTests.swift */; }; 0A2690B72885C2E000E4432D /* TestSentryPermissionsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AABE2EF2885C2120057ED69 /* TestSentryPermissionsObserver.swift */; }; - 0A2D7BBA29152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D7BB929152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift */; }; 0A283E79291A67E000EF4126 /* SentryUIDeviceWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A283E78291A67E000EF4126 /* SentryUIDeviceWrapperTests.swift */; }; + 0A2D7BBA29152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D7BB929152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift */; }; 0A2D8D5B289815C0008720F6 /* SentryBaseIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D8D5A289815C0008720F6 /* SentryBaseIntegration.m */; }; 0A2D8D5D289815EB008720F6 /* SentryBaseIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */; }; 0A2D8D8728992260008720F6 /* SentryBaseIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D8D8628992260008720F6 /* SentryBaseIntegrationTests.swift */; }; @@ -466,6 +466,7 @@ 7BB654FB253DC14A00887E87 /* SentryUserFeedback.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BB654FA253DC14A00887E87 /* SentryUserFeedback.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7BB65501253DC1B500887E87 /* SentryUserFeedback.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BB65500253DC1B500887E87 /* SentryUserFeedback.m */; }; 7BB6550D253EEB3900887E87 /* SentryUserFeedbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB6550C253EEB3900887E87 /* SentryUserFeedbackTests.swift */; }; + 7BB7E7C729267A28004BF96B /* EmptyIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB7E7C629267A28004BF96B /* EmptyIntegration.swift */; }; 7BBC826D25DFCFDE005F1ED8 /* SentryInAppLogic.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BBC826C25DFCFDE005F1ED8 /* SentryInAppLogic.h */; }; 7BBC827125DFD039005F1ED8 /* SentryInAppLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BBC827025DFD039005F1ED8 /* SentryInAppLogic.m */; }; 7BBC827925DFD7D7005F1ED8 /* SentryInAppLogicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBC827825DFD7D7005F1ED8 /* SentryInAppLogicTests.swift */; }; @@ -773,8 +774,8 @@ 03F9D37B2819A65C00602916 /* SentryProfilerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerTests.mm; sourceTree = ""; }; 0A1B497228E597DD00D7BFA3 /* TestLogOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestLogOutput.swift; sourceTree = ""; }; 0A1C3591287D7107007D01E3 /* SentryMetaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryMetaTests.swift; sourceTree = ""; }; - 0A2D7BB929152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryOutOfMemoryScopeObserverTests.swift; sourceTree = ""; }; 0A283E78291A67E000EF4126 /* SentryUIDeviceWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIDeviceWrapperTests.swift; sourceTree = ""; }; + 0A2D7BB929152CBF008727AF /* SentryOutOfMemoryScopeObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryOutOfMemoryScopeObserverTests.swift; sourceTree = ""; }; 0A2D8D5A289815C0008720F6 /* SentryBaseIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBaseIntegration.m; sourceTree = ""; }; 0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryBaseIntegration.h; path = include/SentryBaseIntegration.h; sourceTree = ""; }; 0A2D8D8628992260008720F6 /* SentryBaseIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryBaseIntegrationTests.swift; sourceTree = ""; }; @@ -1225,6 +1226,7 @@ 7BB654FA253DC14A00887E87 /* SentryUserFeedback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryUserFeedback.h; path = Public/SentryUserFeedback.h; sourceTree = ""; }; 7BB65500253DC1B500887E87 /* SentryUserFeedback.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryUserFeedback.m; sourceTree = ""; }; 7BB6550C253EEB3900887E87 /* SentryUserFeedbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackTests.swift; sourceTree = ""; }; + 7BB7E7C629267A28004BF96B /* EmptyIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyIntegration.swift; sourceTree = ""; }; 7BBC826C25DFCFDE005F1ED8 /* SentryInAppLogic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInAppLogic.h; path = include/SentryInAppLogic.h; sourceTree = ""; }; 7BBC827025DFD039005F1ED8 /* SentryInAppLogic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryInAppLogic.m; sourceTree = ""; }; 7BBC827825DFD7D7005F1ED8 /* SentryInAppLogicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInAppLogicTests.swift; sourceTree = ""; }; @@ -2734,6 +2736,7 @@ 7BF1F6AC282A4FC6006BD6AB /* SentryTestObserver.h */, 7BF1F6AD282A4FE2006BD6AB /* SentryTestObserver.m */, 7B72D23928D074BC0014798A /* TestExtensions.swift */, + 7BB7E7C629267A28004BF96B /* EmptyIntegration.swift */, ); path = TestUtils; sourceTree = ""; @@ -3753,6 +3756,7 @@ 8E70B10125CB8695002B3155 /* SentrySpanIdTests.swift in Sources */, 7BFE7A0A27A1B6B000D2B66E /* SentryOutOfMemoryIntegrationTests.swift in Sources */, 7BA61CAF247BBF3C00C130A8 /* SentryDebugImageProviderTests.swift in Sources */, + 7BB7E7C729267A28004BF96B /* EmptyIntegration.swift in Sources */, 7B965728268321CD00C66E25 /* SentryCrashScopeObserverTests.swift in Sources */, 7BD86ECB264A6DB5005439DB /* TestSysctl.swift in Sources */, 035E73CA27D57398005EEB11 /* SentryThreadHandleTests.mm in Sources */, @@ -3852,6 +3856,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency -disable-availability-checking"; SDKROOT = iphoneos; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3912,6 +3917,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency -disable-availability-checking"; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; @@ -3952,6 +3958,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry; PRODUCT_NAME = Sentry; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3990,6 +3997,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = NO; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry; PRODUCT_NAME = Sentry; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4015,6 +4023,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4043,6 +4052,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4112,6 +4122,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency -disable-availability-checking"; SDKROOT = iphoneos; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4156,6 +4167,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry; PRODUCT_NAME = Sentry; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4182,6 +4194,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4251,6 +4264,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency -disable-availability-checking"; SDKROOT = iphoneos; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4289,6 +4303,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry; PRODUCT_NAME = Sentry; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4315,6 +4330,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry.tests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index 4eecbf78456..d2ad4e79ff5 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -652,6 +652,7 @@ - (void)setSdk:(SentryEvent *)event id integrations = event.extra[@"__sentry_sdk_integrations"]; if (!integrations) { integrations = [NSMutableArray new]; + for (NSString *integration in SentrySDK.currentHub.installedIntegrationNames) { // Every integration starts with "Sentry" and ends with "Integration". To keep the // payload of the event small we remove both. diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index 6448a99eed2..32982b9b8da 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -31,14 +31,14 @@ @property (nonatomic, strong) SentryTracesSampler *tracesSampler; @property (nonatomic, strong) SentryProfilesSampler *profilesSampler; @property (nonatomic, strong) id currentDateProvider; -@property (nonatomic, strong) - NSMutableArray *> *installedIntegrations; -@property (nonatomic, strong) NSMutableArray *installedIntegrationNames; +@property (nonatomic, strong) NSMutableArray> *installedIntegrations; +@property (nonatomic, strong) NSMutableSet *installedIntegrationNames; @end @implementation SentryHub { NSObject *_sessionLock; + NSObject *_integrationsLock; } - (instancetype)initWithClient:(nullable SentryClient *)client @@ -48,8 +48,9 @@ - (instancetype)initWithClient:(nullable SentryClient *)client _client = client; _scope = scope; _sessionLock = [[NSObject alloc] init]; + _integrationsLock = [[NSObject alloc] init]; _installedIntegrations = [[NSMutableArray alloc] init]; - _installedIntegrationNames = [[NSMutableArray alloc] init]; + _installedIntegrationNames = [[NSMutableSet alloc] init]; _crashWrapper = [SentryCrashWrapper sharedInstance]; _tracesSampler = [[SentryTracesSampler alloc] initWithOptions:client.options]; #if SENTRY_TARGET_PROFILING_SUPPORTED @@ -539,17 +540,53 @@ - (void)configureScope:(void (^)(SentryScope *scope))callback */ - (BOOL)isIntegrationInstalled:(Class)integrationClass { - for (id item in self.installedIntegrations) { - if ([item isKindOfClass:integrationClass]) { - return YES; + @synchronized(_integrationsLock) { + for (id item in _installedIntegrations) { + if ([item isKindOfClass:integrationClass]) { + return YES; + } } + return NO; } - return NO; } - (BOOL)hasIntegration:(NSString *)integrationName { - return [self.installedIntegrationNames containsObject:integrationName]; + // installedIntegrations and installedIntegrationNames share the same lock. + // Instead of creating an extra lock object, we use _installedIntegrations. + @synchronized(_integrationsLock) { + return [_installedIntegrationNames containsObject:integrationName]; + } +} + +- (void)addInstalledIntegration:(id)integration name:(NSString *)name +{ + @synchronized(_integrationsLock) { + [_installedIntegrations addObject:integration]; + [_installedIntegrationNames addObject:name]; + } +} + +- (void)removeAllIntegrations +{ + @synchronized(_integrationsLock) { + [_installedIntegrations removeAllObjects]; + [_installedIntegrationNames removeAllObjects]; + } +} + +- (NSArray> *)installedIntegrations +{ + @synchronized(_integrationsLock) { + return _installedIntegrations.copy; + } +} + +- (NSSet *)installedIntegrationNames +{ + @synchronized(_integrationsLock) { + return _installedIntegrationNames.copy; + } } - (void)setUser:(nullable SentryUser *)user diff --git a/Sources/Sentry/SentrySDK.m b/Sources/Sentry/SentrySDK.m index e08b87dac1a..df3fc7af53b 100644 --- a/Sources/Sentry/SentrySDK.m +++ b/Sources/Sentry/SentrySDK.m @@ -387,10 +387,10 @@ + (void)installIntegrations } id integrationInstance = [[integrationClass alloc] init]; BOOL shouldInstall = [integrationInstance installWithOptions:options]; + if (shouldInstall) { SENTRY_LOG_DEBUG(@"Integration installed: %@", integrationName); - [SentrySDK.currentHub.installedIntegrations addObject:integrationInstance]; - [SentrySDK.currentHub.installedIntegrationNames addObject:integrationName]; + [SentrySDK.currentHub addInstalledIntegration:integrationInstance name:integrationName]; } } } @@ -407,7 +407,6 @@ + (void)close { // pop the hub and unset SentryHub *hub = SentrySDK.currentHub; - [SentrySDK setCurrentHub:nil]; // uninstall all the integrations for (NSObject *integration in hub.installedIntegrations) { @@ -415,13 +414,15 @@ + (void)close [integration uninstall]; } } - [hub.installedIntegrations removeAllObjects]; + [hub removeAllIntegrations]; // close the client SentryClient *client = [hub getClient]; client.options.enabled = NO; [hub bindClient:nil]; + [SentrySDK setCurrentHub:nil]; + [SentryDependencyContainer reset]; SENTRY_LOG_DEBUG(@"SDK closed!"); diff --git a/Sources/Sentry/include/SentryHub+Private.h b/Sources/Sentry/include/SentryHub+Private.h index d02295caf82..31c69a62ad8 100644 --- a/Sources/Sentry/include/SentryHub+Private.h +++ b/Sources/Sentry/include/SentryHub+Private.h @@ -8,9 +8,11 @@ NS_ASSUME_NONNULL_BEGIN @interface SentryHub (Private) -@property (nonatomic, strong) - NSMutableArray *> *installedIntegrations; -@property (nonatomic, strong) NSMutableArray *installedIntegrationNames; +@property (nonatomic, strong) NSArray> *installedIntegrations; +@property (nonatomic, strong) NSSet *installedIntegrationNames; + +- (void)addInstalledIntegration:(id)integration name:(NSString *)name; +- (void)removeAllIntegrations; - (SentryClient *_Nullable)client; diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index 1a353a3697a..af09c75253e 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -32,6 +32,7 @@ class SentryClientTest: XCTestCase { let deviceWrapper = TestSentryUIDeviceWrapper() let locale = Locale(identifier: "en_US") let timezone = TimeZone(identifier: "Europe/Vienna")! + let queue = DispatchQueue(label: "SentryHubTests", qos: .utility, attributes: [.concurrent]) init() { session = SentrySession(releaseName: "release") @@ -1291,6 +1292,41 @@ class SentryClientTest: XCTestCase { XCTAssertEqual(item, fixture.transportAdapter.sendEventWithTraceStateInvocations.first?.additionalEnvelopeItems.first) } + @available(iOS 10.0, tvOS 10.0, OSX 10.12, *) + func testConcurrentlyAddingInstalledIntegrations_WhileSendingEvents() { + let sut = fixture.getSut() + + let hub = SentryHub(client: sut, andScope: nil) + SentrySDK.setCurrentHub(hub) + + func addIntegrations(amount: Int) { + let emptyIntegration = EmptyIntegration() + for i in 0.. Event { let event = Event(level: SentryLevel.fatal) let debugMeta = DebugMeta() diff --git a/Tests/SentryTests/SentryHubTests.swift b/Tests/SentryTests/SentryHubTests.swift index 859b2d678ee..0b3ce695d95 100644 --- a/Tests/SentryTests/SentryHubTests.swift +++ b/Tests/SentryTests/SentryHubTests.swift @@ -22,6 +22,7 @@ class SentryHubTests: XCTestCase { let transactionName = "Some Transaction" let transactionOperation = "Some Operation" let random = TestRandom(value: 0.5) + let queue = DispatchQueue(label: "SentryHubTests", qos: .utility, attributes: [.concurrent]) init() { options = Options() @@ -673,8 +674,7 @@ class SentryHubTests: XCTestCase { let sut = fixture.getSut() sut.startSession() - let queue = DispatchQueue(label: "SentryHubTests", qos: .utility, attributes: [.concurrent]) - + let queue = fixture.queue let group = DispatchGroup() for _ in 0.. Bool { + return true + } +}