From ab3e172f8a91600c228b829c8f247bd17f156af4 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Aug 2023 14:19:26 +0200 Subject: [PATCH 1/2] chore: update profiling API for hybrid SDKs --- Sources/Sentry/PrivateSentrySDKOnly.m | 24 +++++++++++------ .../HybridPublic/PrivateSentrySDKOnly.h | 14 +++++++--- .../PrivateSentrySDKOnlyTests.swift | 27 ++++++++++++++++--- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.m index 7ac4444b56c..f65a42aef68 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.m @@ -8,6 +8,7 @@ #import "SentryInstallation.h" #import "SentryInternalDefines.h" #import "SentryMeta.h" +#import "SentryProfiledTracerConcurrency.h" #import "SentryProfiler.h" #import "SentrySDK+Private.h" #import "SentrySerialization.h" @@ -121,20 +122,21 @@ + (NSDictionary *)getExtraContext } #if SENTRY_TARGET_PROFILING_SUPPORTED -+ (uint64_t)startProfilingForTrace:(SentryId *)traceId; ++ (uint64_t)startProfilerForTrace:(SentryId *)traceId; { [SentryProfiler startWithTracer:traceId]; return SentryDependencyContainer.sharedInstance.dateProvider.systemTime; } -+ (nullable NSDictionary *)collectProfileForTrace:(SentryId *)traceId - since:(uint64_t)startSystemTime; ++ (nullable NSDictionary *)collectProfileBetween:(uint64_t)startSystemTime + and:(uint64_t)endSystemTime + forTrace:(SentryId *)traceId; { - NSMutableDictionary *payload = [SentryProfiler - collectProfileBetween:startSystemTime - and:SentryDependencyContainer.sharedInstance.dateProvider.systemTime - forTrace:traceId - onHub:[SentrySDK currentHub]]; + NSMutableDictionary *payload = + [SentryProfiler collectProfileBetween:startSystemTime + and:endSystemTime + forTrace:traceId + onHub:[SentrySDK currentHub]]; if (payload != nil) { payload[@"platform"] = SentryPlatformName; @@ -142,6 +144,12 @@ + (uint64_t)startProfilingForTrace:(SentryId *)traceId; return payload; } + ++ (void)discardProfilerForTrace:(SentryId *)traceId; +{ + discardProfilerForTracer(traceId); +} + #endif // SENTRY_TARGET_PROFILING_SUPPORTED #if SENTRY_HAS_UIKIT diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index 79674c49cba..a80a900cd7d 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -85,13 +85,21 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( * Start a profiler session associated with the given @c SentryId. * @return The system time when the profiler session started. */ -+ (uint64_t)startProfilingForTrace:(SentryId *)traceId; ++ (uint64_t)startProfilerForTrace:(SentryId *)traceId; /** * Collect a profiler session data associated with the given @c SentryId. + * This also discards the profiler. */ -+ (nullable NSDictionary *)collectProfileForTrace:(SentryId *)traceId - since:(uint64_t)startSystemTime; ++ (nullable NSDictionary *)collectProfileBetween:(uint64_t)startSystemTime + and:(uint64_t)endSystemTime + forTrace:(SentryId *)traceId; + +/** + * Discard profiler session data associated with the given @c SentryId. + * This only needs to be called in case you haven't collected the profile (and don't intend to). + */ ++ (void)discardProfilerForTrace:(SentryId *)traceId; #endif @property (class, nullable, nonatomic, copy) diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index a43c4c97728..679dc45599a 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -124,7 +124,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { /** * Smoke Tests profiling via PrivateSentrySDKOnly. Actual profiling unit tests are done elsewhere. */ - func testProfiling() { + func testProfilingStartAndCollect() { let options = Options() options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") let client = TestClient(options: options) @@ -132,10 +132,10 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let traceIdA = SentryId() - let startTime = PrivateSentrySDKOnly.startProfiling(forTrace: traceIdA) + let startTime = PrivateSentrySDKOnly.startProfiler(forTrace: traceIdA) XCTAssertGreaterThan(startTime, 0) Thread.sleep(forTimeInterval: 0.2) - let payload = PrivateSentrySDKOnly.collectProfile(forTrace: traceIdA, since: startTime) + let payload = PrivateSentrySDKOnly.collectProfileBetween(startTime, and: startTime + 200_000_000, forTrace: traceIdA) XCTAssertNotNil(payload) XCTAssertEqual(payload?["platform"] as? String, "cocoa") XCTAssertNotNil(payload?["debug_meta"]) @@ -147,6 +147,27 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertNotNil(profile?["stacks"]) XCTAssertNotNil(profile?["frames"]) } + + func testProfilingDiscard() { + let options = Options() + options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") + let client = TestClient(options: options) + SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + + let traceIdA = SentryId() + + let startTime = PrivateSentrySDKOnly.startProfiler(forTrace: traceIdA) + XCTAssertGreaterThan(startTime, 0) + Thread.sleep(forTimeInterval: 0.2) + PrivateSentrySDKOnly.discardProfiler(forTrace: traceIdA) + // how can we test that that this fails with an NCAssert failure? + // XCTAssertThrowsError( + // PrivateSentrySDKOnly.collectProfileBetween( + // startTime, and: startTime + 200_000_000, forTrace: traceIdA) + // ) { error in + // XCTAssertTrue(error is NSException) + // } + } #endif #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) From 26003e67900f5da520eb34ee23c56bc3d1641a2e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sat, 26 Aug 2023 14:05:13 +0200 Subject: [PATCH 2/2] add thread ID to hybrid SDK profile payload --- Sentry.xcodeproj/project.pbxproj | 8 ++++---- .../{PrivateSentrySDKOnly.m => PrivateSentrySDKOnly.mm} | 5 +++++ Tests/SentryTests/PrivateSentrySDKOnlyTests.swift | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) rename Sources/Sentry/{PrivateSentrySDKOnly.m => PrivateSentrySDKOnly.mm} (96%) diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 7a820c7fef0..2686da382d0 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -377,7 +377,7 @@ 7B6C5F8126034354007F7DFF /* SentryWatchdogTerminationLogic.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B6C5F8026034354007F7DFF /* SentryWatchdogTerminationLogic.h */; }; 7B6C5F8726034395007F7DFF /* SentryWatchdogTerminationLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B6C5F8626034395007F7DFF /* SentryWatchdogTerminationLogic.m */; }; 7B6CC50224EE5A42001816D7 /* SentryHubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6CC50124EE5A42001816D7 /* SentryHubTests.swift */; }; - 7B6D1261265F784000C9BE4B /* PrivateSentrySDKOnly.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.m */; }; + 7B6D1261265F784000C9BE4B /* PrivateSentrySDKOnly.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.mm */; }; 7B6D1263265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */; }; 7B6D135C27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D135B27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift */; }; 7B6D98E924C6D336005502FA /* SentrySdkInfo+Equality.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D98E824C6D336005502FA /* SentrySdkInfo+Equality.m */; }; @@ -1277,7 +1277,7 @@ 7B6C5F8026034354007F7DFF /* SentryWatchdogTerminationLogic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryWatchdogTerminationLogic.h; path = include/SentryWatchdogTerminationLogic.h; sourceTree = ""; }; 7B6C5F8626034395007F7DFF /* SentryWatchdogTerminationLogic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryWatchdogTerminationLogic.m; sourceTree = ""; }; 7B6CC50124EE5A42001816D7 /* SentryHubTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryHubTests.swift; sourceTree = ""; }; - 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateSentrySDKOnly.m; sourceTree = ""; }; + 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PrivateSentrySDKOnly.mm; sourceTree = ""; }; 7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateSentrySDKOnlyTests.swift; sourceTree = ""; }; 7B6D135B27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestEnvelopeRateLimitDelegate.swift; sourceTree = ""; }; 7B6D98E724C6D336005502FA /* SentrySdkInfo+Equality.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentrySdkInfo+Equality.h"; sourceTree = ""; }; @@ -2286,7 +2286,7 @@ 63AA76931EB9C1C200D153DE /* Sentry.h */, D8BBD32628FD9FBF0011F850 /* SentrySwift.h */, D81A346B291AECC7005A27A9 /* PrivateSentrySDKOnly.h */, - 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.m */, + 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.mm */, 63AA76941EB9C1C200D153DE /* SentryClient.h */, 63AA75ED1EB8B3C400D153DE /* SentryClient.m */, 7B85DC1C24EFAFCD007D01D2 /* SentryClient+Private.h */, @@ -4092,7 +4092,7 @@ 7BE1E33424F7E3CB009D3AD0 /* SentryMigrateSessionInit.m in Sources */, 15E0A8F22411A45A00F044E3 /* SentrySession.m in Sources */, 844EDCE62947DC3100C86F34 /* SentryNSTimerFactory.m in Sources */, - 7B6D1261265F784000C9BE4B /* PrivateSentrySDKOnly.m in Sources */, + 7B6D1261265F784000C9BE4B /* PrivateSentrySDKOnly.mm in Sources */, 63BE85711ECEC6DE00DC44F5 /* NSDate+SentryExtras.m in Sources */, 7BD4BD4927EB2A5D0071F4FF /* SentryDiscardedEvent.m in Sources */, 03F84D3827DD4191008FE43F /* SentryBacktrace.cpp in Sources */, diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.mm similarity index 96% rename from Sources/Sentry/PrivateSentrySDKOnly.m rename to Sources/Sentry/PrivateSentrySDKOnly.mm index f65a42aef68..befce66fe20 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.mm @@ -12,6 +12,7 @@ #import "SentryProfiler.h" #import "SentrySDK+Private.h" #import "SentrySerialization.h" +#import "SentryThreadHandle.hpp" #import "SentryUser+Private.h" #import "SentryViewHierarchy.h" #import @@ -140,6 +141,10 @@ + (uint64_t)startProfilerForTrace:(SentryId *)traceId; if (payload != nil) { payload[@"platform"] = SentryPlatformName; + payload[@"transaction"] = @{ + @"active_thread_id" : + [NSNumber numberWithLongLong:sentry::profiling::ThreadHandle::current()->tid()] + }; } return payload; diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 679dc45599a..908cfc3d4e3 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -146,6 +146,9 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertNotNil(profile?["samples"]) XCTAssertNotNil(profile?["stacks"]) XCTAssertNotNil(profile?["frames"]) + let transactionInfo = payload?["transaction"] as? NSDictionary + XCTAssertNotNil(transactionInfo) + XCTAssertGreaterThan(transactionInfo?["active_thread_id"] as! Int64, 0) } func testProfilingDiscard() {