diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index a3fc09cf17..9e5cd28877 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -789,7 +789,7 @@ A8F17B2E2901765900990B25 /* SentryRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A8F17B2D2901765900990B25 /* SentryRequest.m */; }; A8F17B342902870300990B25 /* SentryHttpStatusCodeRange.m in Sources */ = {isa = PBXBuildFile; fileRef = A8F17B332902870300990B25 /* SentryHttpStatusCodeRange.m */; }; D4252AE32D119D3F00184F6F /* SentryDataWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4252AE22D119D3B00184F6F /* SentryDataWrapperTests.swift */; }; - D48724DB2D352597005DE483 /* SentryTraceOrigins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48724DA2D352591005DE483 /* SentryTraceOrigins.swift */; }; + D48724DB2D352597005DE483 /* SentryTraceOrigin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48724DA2D352591005DE483 /* SentryTraceOrigin.swift */; }; D4AF00212D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AF00202D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m */; }; D4AF00232D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = D4AF00222D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h */; }; D4AF00252D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AF00242D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m */; }; @@ -1879,7 +1879,7 @@ A8F17B2D2901765900990B25 /* SentryRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryRequest.m; sourceTree = ""; }; A8F17B332902870300990B25 /* SentryHttpStatusCodeRange.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryHttpStatusCodeRange.m; sourceTree = ""; }; D4252AE22D119D3B00184F6F /* SentryDataWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryDataWrapperTests.swift; sourceTree = ""; }; - D48724DA2D352591005DE483 /* SentryTraceOrigins.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceOrigins.swift; sourceTree = ""; }; + D48724DA2D352591005DE483 /* SentryTraceOrigin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceOrigin.swift; sourceTree = ""; }; D4AF00202D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSFileManagerSwizzling.m; sourceTree = ""; }; D4AF00222D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryNSFileManagerSwizzling.h; path = include/SentryNSFileManagerSwizzling.h; sourceTree = ""; }; D4AF00242D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSFileManagerSwizzlingTests.m; sourceTree = ""; }; @@ -3665,7 +3665,7 @@ D48724D92D35258A005DE483 /* Transactions */ = { isa = PBXGroup; children = ( - D48724DA2D352591005DE483 /* SentryTraceOrigins.swift */, + D48724DA2D352591005DE483 /* SentryTraceOrigin.swift */, ); path = Transactions; sourceTree = ""; @@ -4910,7 +4910,7 @@ 7BC9A20628F41781001E7C4C /* SentryMeasurementUnit.m in Sources */, 63FE71A020DA4C1100CDBAE8 /* SentryCrashInstallation.m in Sources */, 63FE713520DA4C1100CDBAE8 /* SentryCrashMemory.c in Sources */, - D48724DB2D352597005DE483 /* SentryTraceOrigins.swift in Sources */, + D48724DB2D352597005DE483 /* SentryTraceOrigin.swift in Sources */, 63FE714520DA4C1100CDBAE8 /* SentryCrashObjC.c in Sources */, 63FE710520DA4C1000CDBAE8 /* SentryAsyncSafeLog.c in Sources */, 0A2D8D5B289815C0008720F6 /* SentryBaseIntegration.m in Sources */, diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 1e6c3cb131..2095575cf1 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -5,6 +5,7 @@ #import "SentryDispatchQueueWrapper.h" #import "SentryDisplayLinkWrapper.h" #import "SentryExtraContextProvider.h" +#import "SentryFileIOTracker.h" #import "SentryFileManager.h" #import "SentryInternalCDefines.h" #import "SentryLog.h" @@ -186,6 +187,21 @@ - (SentryThreadInspector *)threadInspector SENTRY_DISABLE_THREAD_SANITIZER( return _threadInspector; } +- (SentryFileIOTracker *)fileIOTracker SENTRY_DISABLE_THREAD_SANITIZER( + "double-checked lock produce false alarms") +{ + if (_fileIOTracker == nil) { + @synchronized(sentryDependencyContainerLock) { + if (_fileIOTracker == nil) { + _fileIOTracker = + [[SentryFileIOTracker alloc] initWithThreadInspector:[self threadInspector] + processInfoWrapper:[self processInfoWrapper]]; + } + } + } + return _fileIOTracker; +} + - (SentryDebugImageProvider *)debugImageProvider { @synchronized(sentryDependencyContainerLock) { diff --git a/Sources/Sentry/SentryFileIOTracker.m b/Sources/Sentry/SentryFileIOTracker.m index baaf04ba74..f5856769e7 100644 --- a/Sources/Sentry/SentryFileIOTracker.m +++ b/Sources/Sentry/SentryFileIOTracker.m @@ -34,10 +34,7 @@ @implementation SentryFileIOTracker + (instancetype)sharedInstance { - SentryFileIOTracker *tracker = [[SentryFileIOTracker alloc] - initWithThreadInspector:SentryDependencyContainer.sharedInstance.threadInspector - processInfoWrapper:SentryDependencyContainer.sharedInstance.processInfoWrapper]; - return tracker; + return SentryDependencyContainer.sharedInstance.fileIOTracker; } - (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector diff --git a/Sources/Sentry/SentryFileIOTrackingIntegration.m b/Sources/Sentry/SentryFileIOTrackingIntegration.m index 5c650ca7ce..d634e416ff 100644 --- a/Sources/Sentry/SentryFileIOTrackingIntegration.m +++ b/Sources/Sentry/SentryFileIOTrackingIntegration.m @@ -19,9 +19,7 @@ - (BOOL)installWithOptions:(SentryOptions *)options return NO; } - self.tracker = [[SentryFileIOTracker alloc] - initWithThreadInspector:[[SentryThreadInspector alloc] initWithOptions:options] - processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; + self.tracker = [[SentryDependencyContainer sharedInstance] fileIOTracker]; [self.tracker enable]; [SentryNSDataSwizzling.shared startWithOptions:options tracker:self.tracker]; diff --git a/Sources/Sentry/SentryNSDataSwizzling.m b/Sources/Sentry/SentryNSDataSwizzling.m index 567437e370..85d47e3233 100644 --- a/Sources/Sentry/SentryNSDataSwizzling.m +++ b/Sources/Sentry/SentryNSDataSwizzling.m @@ -46,7 +46,7 @@ + (void)swizzle measureNSData:self writeToFile:path atomically:useAuxiliaryFile - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] method:^BOOL(NSString *_Nonnull filePath, BOOL isAtomically) { return SentrySWCallOriginal(filePath, isAtomically); }]; @@ -62,7 +62,7 @@ + (void)swizzle measureNSData:self writeToFile:path options:writeOptionsMask - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] error:error method:^BOOL( NSString *filePath, NSDataWritingOptions options, NSError **outError) { @@ -80,7 +80,7 @@ + (void)swizzle return [SentryNSDataSwizzling.shared.tracker measureNSDataFromFile:path options:options - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] error:error method:^NSData *(NSString *filePath, NSDataReadingOptions options, NSError **outError) { @@ -95,7 +95,7 @@ + (void)swizzle SentrySWReturnType(NSData *), SentrySWArguments(NSString * path), SentrySWReplacement({ return [SentryNSDataSwizzling.shared.tracker measureNSDataFromFile:path - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] method:^NSData *( NSString *filePath) { return SentrySWCallOriginal(filePath); }]; }), @@ -110,7 +110,7 @@ + (void)swizzle return [SentryNSDataSwizzling.shared.tracker measureNSDataFromURL:url options:options - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] error:error method:^NSData *(NSURL *fileUrl, NSDataReadingOptions options, NSError **outError) { diff --git a/Sources/Sentry/SentryNSFileManagerSwizzling.m b/Sources/Sentry/SentryNSFileManagerSwizzling.m index c341e41dce..32651207a6 100644 --- a/Sources/Sentry/SentryNSFileManagerSwizzling.m +++ b/Sources/Sentry/SentryNSFileManagerSwizzling.m @@ -60,7 +60,7 @@ + (void)swizzle measureNSFileManagerCreateFileAtPath:path data:data attributes:attributes - origin:SentryTraceOriginAutoNSData + origin:[SentryTraceOrigin autoNSData] method:^BOOL(NSString *path, NSData *data, NSDictionary *attributes) { diff --git a/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h b/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h index 433cace15e..0ee1a14506 100644 --- a/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h +++ b/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h @@ -22,6 +22,7 @@ @class SentrySystemWrapper; @class SentryThreadWrapper; @class SentryThreadInspector; +@class SentryFileIOTracker; @protocol SentryRandom; @protocol SentryCurrentDateProvider; @protocol SentryRateLimits; @@ -77,6 +78,7 @@ SENTRY_NO_INIT @property (nonatomic, strong) SentrySysctl *sysctlWrapper; @property (nonatomic, strong) SentryThreadInspector *threadInspector; @property (nonatomic, strong) id rateLimits; +@property (nonatomic, strong) SentryFileIOTracker *fileIOTracker; #if SENTRY_UIKIT_AVAILABLE @property (nonatomic, strong) SentryFramesTracker *framesTracker; diff --git a/Sources/Sentry/include/SentryFileIOTracker.h b/Sources/Sentry/include/SentryFileIOTracker.h index b1fb0889c1..833c9cbdf2 100644 --- a/Sources/Sentry/include/SentryFileIOTracker.h +++ b/Sources/Sentry/include/SentryFileIOTracker.h @@ -9,6 +9,13 @@ NS_ASSUME_NONNULL_BEGIN @interface SentryFileIOTracker : NSObject SENTRY_NO_INIT +/** + * Convenience accessor to the shared instance of the tracker in the dependency container. + * + * @note Can be used from Swift without import the entire dependency container. + * + * @return The shared instance of the tracker. + */ + (instancetype)sharedInstance; - (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector diff --git a/Sources/Sentry/include/SentryTraceOrigins.h b/Sources/Sentry/include/SentryTraceOrigins.h index 7922cdb9a7..29bef94fea 100644 --- a/Sources/Sentry/include/SentryTraceOrigins.h +++ b/Sources/Sentry/include/SentryTraceOrigins.h @@ -5,7 +5,6 @@ static NSString *const SentryTraceOriginUIEventTracker = @"auto.ui.event_tracker static NSString *const SentryTraceOriginAutoAppStart = @"auto.app.start"; static NSString *const SentryTraceOriginAutoAppStartProfile = @"auto.app.start.profile"; -static NSString *const SentryTraceOriginAutoNSData = @"auto.file.ns_data"; static NSString *const SentryTraceOriginAutoDBCoreData = @"auto.db.core_data"; static NSString *const SentryTraceOriginAutoHttpNSURLSession = @"auto.http.ns_url_session"; static NSString *const SentryTraceOriginAutoUIViewController = @"auto.ui.view_controller"; diff --git a/Sources/Swift/Transactions/SentryTraceOrigins.swift b/Sources/Swift/Transactions/SentryTraceOrigin.swift similarity index 62% rename from Sources/Swift/Transactions/SentryTraceOrigins.swift rename to Sources/Swift/Transactions/SentryTraceOrigin.swift index d04c9d799b..d43d9d0b10 100644 --- a/Sources/Swift/Transactions/SentryTraceOrigins.swift +++ b/Sources/Swift/Transactions/SentryTraceOrigin.swift @@ -1,6 +1,7 @@ // This file is the Swift addition for `SentryTraceOrigins.h` in the `Sentry` module. @objcMembers -public class SentryTraceOrigin { +public class SentryTraceOrigin: NSObject { + static let autoNSData = "auto.file.ns_data" static let manualData = "manual.file.data" } diff --git a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationTests.swift index 8e2885907d..c67258ab87 100644 --- a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationTests.swift @@ -27,6 +27,14 @@ class SentryFileIOTrackingIntegrationTests: XCTestCase { fileURL = fileDirectory.appendingPathComponent("TestFile") filePath = fileURL?.path } + + func assertDataWritten(toUrl url: URL, file: StaticString = #file, line: UInt = #line) { + guard let data = try? Data(contentsOf: url) else { + XCTFail("Could not load written resource file", file: file, line: line) + return + } + XCTAssertEqual(self.data, data, file: file, line: line) + } } private var fixture: Fixture! @@ -111,15 +119,19 @@ class SentryFileIOTrackingIntegrationTests: XCTestCase { } func testDataExtension_Writing_Tracking() throws { + // -- Arrange -- // Automatic tracking of Swift.Data is not available starting with iOS 18, macOS 15, tvOS 15. // Therefore, the extension method is only tested with these OS versions. guard #available(iOS 18, macOS 15, tvOS 18, *) else { throw XCTSkip("Data+SentryTracing is not tested on this OS version") } SentrySDK.start(options: fixture.getOptions()) + + // -- Act & Assert -- assertSpans(1, "file.write") { try? fixture.data.writeWithSentryTracing(to: fixture.fileURL) } + fixture.assertDataWritten(toUrl: fixture.fileURL) } func testDataExtension_WritingWithOption_Tracking() throws { @@ -133,6 +145,7 @@ class SentryFileIOTrackingIntegrationTests: XCTestCase { try? fixture.data .writeWithSentryTracing(to: fixture.fileURL, options: .atomic) } + fixture.assertDataWritten(toUrl: fixture.fileURL) } func test_ReadingTrackingDisabled_forIOOption() { @@ -329,9 +342,9 @@ class SentryFileIOTrackingIntegrationTests: XCTestCase { return } - XCTAssertEqual(children.count, spansCount, file: file, line: line) + XCTAssertEqual(children.count, spansCount, "Actual span count is not equal to expected count", file: file, line: line) if let first = children.first { - XCTAssertEqual(first.operation, operation, file: file, line: line) + XCTAssertEqual(first.operation, operation, "Operation for span is not equal to expected operation", file: file, line: line) } } }