diff --git a/SentryTestUtils/ClearTestState.swift b/SentryTestUtils/ClearTestState.swift index c1f25057a8..c0c8a92e75 100644 --- a/SentryTestUtils/ClearTestState.swift +++ b/SentryTestUtils/ClearTestState.swift @@ -44,7 +44,7 @@ class TestCleanup: NSObject { #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) SentryProfiler.getCurrent().stop(for: .normal) - SentryProfiler.sentry_resetConcurrencyTracking() + SentryProfiler.resetConcurrencyTracking() #endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) diff --git a/Sources/Sentry/SentryProfileTimeseries.mm b/Sources/Sentry/SentryProfileTimeseries.mm index 0abaf648d0..285070d3ff 100644 --- a/Sources/Sentry/SentryProfileTimeseries.mm +++ b/Sources/Sentry/SentryProfileTimeseries.mm @@ -12,6 +12,7 @@ # import "SentryTime.h" # endif // SENTRY_HAS_UIKIT +namespace { /** * Print a debug log to help diagnose slicing errors. * @param start @c YES if this is an attempt to find the start of the sliced data based on the @@ -19,7 +20,7 @@ * transaction's end, to accurately describe what's happening in the log statement. */ void -logSlicingFailureWithArray( +_sentry_logSlicingFailureWithArray( NSArray *array, uint64_t startSystemTime, uint64_t endSystemTime, BOOL start) { if (!SENTRY_CASSERT_RETURN( @@ -43,6 +44,8 @@ firstSampleRelativeToTransactionStart, lastSampleRelativeToTransactionStart); } +} // namespace + NSArray *_Nullable sentry_slicedProfileSamples( NSArray *samples, uint64_t startSystemTime, uint64_t endSystemTime) { @@ -59,7 +62,7 @@ }]; if (firstIndex == NSNotFound) { - logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ YES); + _sentry_logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ YES); return nil; } else { SENTRY_LOG_DEBUG(@"Found first slice sample at index %lu", firstIndex); @@ -74,7 +77,7 @@ }]; if (lastIndex == NSNotFound) { - logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ NO); + _sentry_logSlicingFailureWithArray(samples, startSystemTime, endSystemTime, /*start*/ NO); return nil; } else { SENTRY_LOG_DEBUG(@"Found last slice sample at index %lu", lastIndex); diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index cacaa17451..3fab8e46fd 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -30,14 +30,18 @@ # import # endif // SENTRY_HAS_UIKIT +using namespace sentry::profiling; + +namespace { + const int kSentryProfilerFrequencyHz = 101; NSTimeInterval kSentryProfilerTimeoutInterval = 30; -using namespace sentry::profiling; - std::mutex _gProfilerLock; SentryProfiler *_Nullable _gCurrentProfiler; +} // namespace + # pragma mark - Public void @@ -273,15 +277,12 @@ + (SentryProfiler *)getCurrentProfiler return _gCurrentProfiler; } -// this just calls through to SentryProfiledTracerConcurrency.sentry_resetConcurrencyTracking(). we -// have to do this through SentryTracer because SentryProfiledTracerConcurrency cannot be included -// in test targets via ObjC bridging headers because it contains C++. -+ (void)sentry_resetConcurrencyTracking ++ (void)resetConcurrencyTracking { sentry_resetConcurrencyTracking(); } -+ (NSUInteger)sentry_currentProfiledTracers ++ (NSUInteger)currentProfiledTracers { return sentry_currentProfiledTracers(); } diff --git a/Sources/Sentry/include/SentryProfiler+Private.h b/Sources/Sentry/include/SentryProfiler+Private.h index b7c7430374..4d9839b91a 100644 --- a/Sources/Sentry/include/SentryProfiler+Private.h +++ b/Sources/Sentry/include/SentryProfiler+Private.h @@ -26,8 +26,11 @@ typedef NS_ENUM(NSUInteger, SentryProfilerTruncationReason) { NS_ASSUME_NONNULL_BEGIN -SENTRY_EXTERN const int kSentryProfilerFrequencyHz; - +/** + * Perform necessary profiler tasks that should take place when the SDK starts: configure the next + * launch's profiling, stop legacy profiling if no automatic performance transaction is running, + * start the continuous profiler if enabled and not profiling from launch. + */ SENTRY_EXTERN void sentry_manageProfilerOnStartSDK(SentryOptions *options, SentryHub *hub); /** diff --git a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift index c234cf0060..99c9e9b072 100644 --- a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift +++ b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift @@ -279,7 +279,7 @@ class SentryProfilerSwiftTests: XCTestCase { func createConcurrentSpansWithMetrics() throws { XCTAssertFalse(SentryProfiler.isCurrentlyProfiling()) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) for i in 0 ..< numberOfTransactions { print("creating new concurrent transaction for test") @@ -289,7 +289,7 @@ class SentryProfilerSwiftTests: XCTestCase { fixture.systemWrapper.overrides.cpuEnergyUsage = NSNumber(value: fixture.systemWrapper.overrides.cpuEnergyUsage!.intValue + fixture.mockEnergyUsage.intValue) XCTAssertTrue(SentryProfiler.isCurrentlyProfiling()) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(i + 1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(i + 1)) spans.append(span) fixture.currentDateProvider.advanceBy(nanoseconds: 100) } @@ -301,7 +301,7 @@ class SentryProfilerSwiftTests: XCTestCase { try fixture.gatherMockedMetrics(span: span) XCTAssertTrue(SentryProfiler.isCurrentlyProfiling()) span.finish() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(numberOfTransactions - (i + 1))) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(numberOfTransactions - (i + 1))) try self.assertValidProfileData(expectedThreadMetadata: [threadMetadata]) @@ -314,7 +314,7 @@ class SentryProfilerSwiftTests: XCTestCase { } XCTAssertFalse(SentryProfiler.isCurrentlyProfiling()) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) } try createConcurrentSpansWithMetrics() @@ -453,102 +453,102 @@ class SentryProfilerSwiftTests: XCTestCase { /// based on ``SentryTracerTests.testFinish_WithoutHub_DoesntCaptureTransaction`` func testProfilerCleanedUpAfterTransactionDiscarded_NoHub() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() { let sut = SentryTracer(transactionContext: TransactionContext(name: fixture.transactionName, operation: fixture.transactionOperation), hub: nil) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) sut.finish() } performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } /// based on ``SentryTracerTests.testFinish_WaitForAllChildren_ExceedsMaxDuration_NoTransactionCaptured`` func testProfilerCleanedUpAfterTransactionDiscarded_ExceedsMaxDuration() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() throws { let sut = try fixture.newTransaction(automaticTransaction: true) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) fixture.currentDateProvider.advance(by: 500) sut.finish() } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } func testProfilerCleanedUpAfterInFlightTransactionDeallocated() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() throws { let sut = try fixture.newTransaction(automaticTransaction: true) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) XCTAssertFalse(sut.isFinished) } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } /// based on ``SentryTracerTests.testFinish_IdleTimeout_ExceedsMaxDuration_NoTransactionCaptured`` func testProfilerCleanedUpAfterTransactionDiscarded_IdleTimeout_ExceedsMaxDuration() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() throws { let sut = try fixture.newTransaction(automaticTransaction: true, idleTimeout: 1) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) fixture.currentDateProvider.advance(by: 500) sut.finish() } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } /// based on ``SentryTracerTests.testIdleTimeout_NoChildren_TransactionNotCaptured`` func testProfilerCleanedUpAfterTransactionDiscarded_IdleTimeout_NoChildren() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() throws { let span = try fixture.newTransaction(automaticTransaction: true, idleTimeout: 1) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) fixture.currentDateProvider.advance(by: 500) fixture.dispatchQueueWrapper.invokeLastDispatchAfter() XCTAssert(span.isFinished) } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } /// based on ``SentryTracerTests.testIdleTransaction_CreatingDispatchBlockFails_NoTransactionCaptured`` func testProfilerCleanedUpAfterTransactionDiscarded_IdleTransaction_CreatingDispatchBlockFails() throws { fixture.dispatchQueueWrapper.createDispatchBlockReturnsNULL = true - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) func performTransaction() throws { let span = try fixture.newTransaction(automaticTransaction: true, idleTimeout: 1) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) fixture.currentDateProvider.advance(by: 500) span.finish() } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } #if !os(macOS) /// based on ``SentryTracerTests.testFinish_WaitForAllChildren_StartTimeModified_NoTransactionCaptured`` func testProfilerCleanedUpAfterTransactionDiscarded_WaitForAllChildren_StartTimeModified() throws { - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) let appStartMeasurement = fixture.getAppStartMeasurement(type: .cold) SentrySDK.setAppStartMeasurement(appStartMeasurement) fixture.currentDateProvider.advance(by: 1) func performTransaction() throws { let sut = try fixture.newTransaction(testingAppLaunchSpans: true, automaticTransaction: true) - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(1)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(1)) fixture.currentDateProvider.advance(by: 499) sut.finish() } try performTransaction() - XCTAssertEqual(SentryProfiler.sentry_currentProfiledTracers(), UInt(0)) + XCTAssertEqual(SentryProfiler.currentProfiledTracers(), UInt(0)) XCTAssertEqual(self.fixture.client?.captureEventWithScopeInvocations.count, 0) } #endif // !os(macOS) diff --git a/Tests/SentryTests/SentryProfiler+Test.h b/Tests/SentryTests/SentryProfiler+Test.h index a57270dd8c..def706aee3 100644 --- a/Tests/SentryTests/SentryProfiler+Test.h +++ b/Tests/SentryTests/SentryProfiler+Test.h @@ -13,9 +13,19 @@ SentryProfiler () + (SentryProfiler *)getCurrentProfiler; -+ (void)sentry_resetConcurrencyTracking; - -+ (NSUInteger)sentry_currentProfiledTracers; +/** + * This just calls through to SentryProfiledTracerConcurrency.sentry_resetConcurrencyTracking(). we + * have to do this through SentryTracer because SentryProfiledTracerConcurrency.h cannot be included + * in test targets via ObjC bridging headers because it contains C++. + */ ++ (void)resetConcurrencyTracking; + +/** + * This just calls through to SentryProfiledTracerConcurrency.sentry_currentProfiledTracers(). we + * have to do this through SentryTracer because SentryProfiledTracerConcurrency.h cannot be included + * in test targets via ObjC bridging headers because it contains C++. + */ ++ (NSUInteger)currentProfiledTracers; @end