Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(profiling): expose continuous profiling API #4010

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog
## Unreleased

### Features

- Continuous mode profiling (see `SentrySDK.startProfiler` and `SentryOptions.profilesSampleRate`) (#4010)

### Fixes

- Proper redact SR during animation (#4289)
Expand Down
1 change: 1 addition & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.enableTimeToFullDisplayTracing = true
options.enablePerformanceV2 = true
options.enableMetrics = !args.contains("--disable-metrics")
options.profilesSampleRate = ProcessInfo.processInfo.arguments.contains("--io.sentry.enable-continuous-profiling") ? nil : 1

options.add(inAppInclude: "iOS_External")

Expand Down
23 changes: 13 additions & 10 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,24 +466,24 @@ NS_SWIFT_NAME(Options)
/**
* @warning This is an experimental feature and may still have bugs.
* Set to @c YES to run the profiler as early as possible in an app launch, before you would
* normally have the opportunity to call @c SentrySDK.start . If enabled, the @c tracesSampleRate
* and @c profilesSampleRate are persisted to disk and read on the next app launch to decide whether
* to profile that launch.
* @see @c tracesSampler and @c profilesSampler for more information on how they work for this
* feature.
* normally have the opportunity to call @c SentrySDK.start . If @c profilesSampleRate is nonnull,
* the @c tracesSampleRate and @c profilesSampleRate are persisted to disk and read on the next app
* launch to decide whether to profile that launch.
* @warning If @c profilesSampleRate is @c nil then a continuous profile will be started on every
* launch; if you desire sampling profiled launches, you must compute your own sample rate to decide
* whether to set this property to @c YES or @c NO .
* @note Profiling is automatically disabled if a thread sanitizer is attached.
*/
@property (nonatomic, assign) BOOL enableAppLaunchProfiling;

/**
* @note Profiling is not supported on watchOS or tvOS.
* Indicates the percentage profiles being sampled out of the sampled transactions.
* @note The default is @c 0.
* @note The value needs to be >= @c 0.0 and \<= @c 1.0. When setting a value out of range
* the SDK sets it to the default of @c 0.
* This property is dependent on @c tracesSampleRate -- if @c tracesSampleRate is @c 0 (default),
* no profiles will be collected no matter what this property is set to. This property is
* used to undersample profiles *relative to* @c tracesSampleRate .
* the SDK sets it to @c 0. When set to a valid nonnull value, this property is dependent on
* @c tracesSampleRate -- if @c tracesSampleRate is @c 0 (default), no profiles will be collected no
* matter what this property is set to. This property is used to undersample profiles *relative to*
* @c tracesSampleRate .
* @note Setting this value to @c nil enables an experimental new profiling mode, called continuous
* profiling. This allows you to start and stop a profiler any time with @c SentrySDK.startProfiler
* and @c SentrySDK.stopProfiler, which can run with no time limit, periodically uploading profiling
Expand All @@ -493,6 +493,9 @@ NS_SWIFT_NAME(Options)
* automatically started for app launches. If you wish to sample them, you must do so at the
* callsites where you use the API or configure launch profiling. Continuous profiling is not
* automatically started for performance transactions as was the previous version of profiling.
* @seealso https://docs.sentry.io/platforms/apple/profiling/ for more information about the
* different profiling modes.
* @note The default is @c nil (which implies continuous profiling mode).
* @warning The new continuous profiling mode is experimental and may still contain bugs.
* @note Profiling is automatically disabled if a thread sanitizer is attached.
*/
Expand Down
20 changes: 20 additions & 0 deletions Sources/Sentry/Public/SentrySDK.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,26 @@ SENTRY_NO_INIT

#endif

#if SENTRY_TARGET_PROFILING_SUPPORTED
/**
* Start a new continuous profiling session if one is not already running.
* @note Unlike trace-based profiling, continuous profiling does not take into account @c
* SentryOptions.profilesSampleRate ; a call to this method will always start a profile if one is
* not already running. This includes app launch profiles configured with @c
* SentryOptions.enableAppLaunchProfiling .
* @warning Continuous profiling mode is experimental and may still contain bugs.
* @seealso https://docs.sentry.io/platforms/apple/guides/ios/profiling/#continuous-profiling
*/
+ (void)startProfiler;

/**
* Stop a continuous profiling session if there is one ongoing.
* @warning Continuous profiling mode is experimental and may still contain bugs.
* @seealso https://docs.sentry.io/platforms/apple/guides/ios/profiling/#continuous-profiling
*/
+ (void)stopProfiler;
#endif // SENTRY_TARGET_PROFILING_SUPPORTED

@end

NS_ASSUME_NONNULL_END
16 changes: 0 additions & 16 deletions Sources/Sentry/include/SentrySDK+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,6 @@ SentrySDK ()
*/
+ (void)captureEnvelope:(SentryEnvelope *)envelope;

#if SENTRY_TARGET_PROFILING_SUPPORTED
/**
* Start a new continuous profiling session if one is not already running.
* @seealso https://docs.sentry.io/platforms/apple/profiling/
*/
+ (void)startProfiler;

/**
* Stop a continuous profiling session if there is one ongoing. This doesn't immediately
* stop the profiler, rather, the current profiling chunk timer will be allowed to expire and will
* not be renewed afterwards.
* @seealso https://docs.sentry.io/platforms/apple/profiling/
*/
+ (void)stopProfiler;
#endif // SENTRY_TARGET_PROFILING_SUPPORTED

@end

NS_ASSUME_NONNULL_END
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentrySampleDecision+Private.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#import <Foundation/Foundation.h>

#import "SentrySampleDecision.h"

/**
Returns the value to use when serializing a SentrySampleDecision.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class SentryFramesTrackerTests: XCTestCase {
fixture = Fixture()
}

override func tearDown() {
super.tearDown()
clearTestState()
}

func testIsNotRunning_WhenNotStarted() {
XCTAssertFalse(self.fixture.sut.isRunning)
}
Expand Down
14 changes: 9 additions & 5 deletions Tests/SentryTests/SentryOptionsTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ - (void)assertDefaultValues:(SentryOptions *)options
# pragma clang diagnostic pop
XCTAssertNil(options.profilesSampleRate);
XCTAssertNil(options.profilesSampler);
XCTAssert([options isContinuousProfilingEnabled]);
XCTAssertTrue([options isContinuousProfilingEnabled]);
#endif // SENTRY_TARGET_PROFILING_SUPPORTED

XCTAssertTrue([options.spotlightUrl isEqualToString:@"http://localhost:8969/stream"]);
Expand Down Expand Up @@ -1151,7 +1151,7 @@ - (void)testDefaultProfilesSampleRate
// rev
XCTAssertFalse(options.isProfilingEnabled);

XCTAssert([options isContinuousProfilingEnabled]);
XCTAssertTrue([options isContinuousProfilingEnabled]);
}

- (void)testProfilesSampleRate_SetToNil
Expand Down Expand Up @@ -1182,6 +1182,8 @@ - (void)testProfilesSampleRateLowerBound
options.profilesSampleRate = tooLow;
XCTAssertEqual(options.profilesSampleRate.doubleValue, 0);

// setting an invalid sample rate effectively now enables continuous profiling, since it can let
// the backing variable remain nil
XCTAssertFalse([options isContinuousProfilingEnabled]);
}

Expand All @@ -1200,6 +1202,8 @@ - (void)testProfilesSampleRateUpperBound
options.profilesSampleRate = tooLow;
XCTAssertEqual(options.profilesSampleRate.doubleValue, 0);

// setting an invalid sample rate effectively now enables continuous profiling, since it can let
// the backing variable remain nil
XCTAssertFalse([options isContinuousProfilingEnabled]);
}

Expand All @@ -1212,7 +1216,7 @@ - (void)testIsProfilingEnabled_NothingSet_IsDisabled
XCTAssertFalse(options.isProfilingEnabled);

XCTAssertNil(options.profilesSampleRate);
XCTAssert([options isContinuousProfilingEnabled]);
XCTAssertTrue([options isContinuousProfilingEnabled]);
}

- (void)testIsProfilingEnabled_ProfilesSampleRateSetToZero_IsDisabled
Expand Down Expand Up @@ -1311,14 +1315,14 @@ - (void)testDefaultProfilesSampler
{
SentryOptions *options = [self getValidOptions:@{}];
XCTAssertNil(options.profilesSampler);
XCTAssert([options isContinuousProfilingEnabled]);
XCTAssertTrue([options isContinuousProfilingEnabled]);
}

- (void)testGarbageProfilesSampler_ReturnsNil
{
SentryOptions *options = [self getValidOptions:@{ @"profilesSampler" : @"fault" }];
XCTAssertNil(options.profilesSampler);
XCTAssert([options isContinuousProfilingEnabled]);
XCTAssertTrue([options isContinuousProfilingEnabled]);
}

#endif // SENTRY_TARGET_PROFILING_SUPPORTED
Expand Down
9 changes: 5 additions & 4 deletions Tests/SentryTests/SentrySDKTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -423,17 +423,18 @@ class SentrySDKTests: XCTestCase {
func testStartingContinuousProfilerWithSampleRateZero() throws {
givenSdkWithHub()

// nil is the default value for profilesSampleRate, so we don't have to explicitly set it on the fixture
XCTAssertNil(fixture.options.profilesSampleRate)
fixture.options.profilesSampleRate = 0
XCTAssertEqual(try XCTUnwrap(fixture.options.profilesSampleRate).doubleValue, 0)

XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
SentrySDK.startProfiler()
XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling())
XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
}

func testStartingContinuousProfilerWithSampleRateNil() throws {
givenSdkWithHub()

fixture.options.profilesSampleRate = nil
// nil is the default initial value for profilesSampleRate, so we don't have to explicitly set it on the fixture
XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
SentrySDK.startProfiler()
XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class SentryTransactionTests: XCTestCase {
super.setUp()
fixture = Fixture()
}

override func tearDown() {
super.tearDown()
clearTestState()
}

func testSerializeMeasurements_NoMeasurements() {
let actual = fixture.getTransaction().serialize()
Expand Down Expand Up @@ -223,7 +228,8 @@ class SentryTransactionTests: XCTestCase {

#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func testTransactionWithContinuousProfile() throws {
SentrySDK.setStart(Options())
let options = Options()
SentrySDK.setStart(options)
let transaction = fixture.getTransaction()
SentryContinuousProfiler.start()
let profileId = try XCTUnwrap(SentryContinuousProfiler.profiler()?.profilerId.sentryIdString)
Expand Down
Loading