Skip to content

Commit 3fd6a8b

Browse files
authored
feat(profiling): expose continuous profiling API (#4010)
1 parent 077e940 commit 3fd6a8b

File tree

10 files changed

+66
-36
lines changed

10 files changed

+66
-36
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog
22
## Unreleased
33

4+
### Features
5+
6+
- Continuous mode profiling (see `SentrySDK.startProfiler` and `SentryOptions.profilesSampleRate`) (#4010)
7+
48
### Fixes
59

610
- Proper redact SR during animation (#4289)

Samples/iOS-Swift/iOS-Swift/AppDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
8989
options.enableTimeToFullDisplayTracing = true
9090
options.enablePerformanceV2 = true
9191
options.enableMetrics = !args.contains("--disable-metrics")
92+
options.profilesSampleRate = ProcessInfo.processInfo.arguments.contains("--io.sentry.enable-continuous-profiling") ? nil : 1
9293

9394
options.add(inAppInclude: "iOS_External")
9495

Sources/Sentry/Public/SentryOptions.h

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

478479
/**
479480
* @note Profiling is not supported on watchOS or tvOS.
480481
* Indicates the percentage profiles being sampled out of the sampled transactions.
481-
* @note The default is @c 0.
482482
* @note The value needs to be >= @c 0.0 and \<= @c 1.0. When setting a value out of range
483-
* the SDK sets it to the default of @c 0.
484-
* This property is dependent on @c tracesSampleRate -- if @c tracesSampleRate is @c 0 (default),
485-
* no profiles will be collected no matter what this property is set to. This property is
486-
* used to undersample profiles *relative to* @c tracesSampleRate .
483+
* the SDK sets it to @c 0. When set to a valid nonnull value, this property is dependent on
484+
* @c tracesSampleRate -- if @c tracesSampleRate is @c 0 (default), no profiles will be collected no
485+
* matter what this property is set to. This property is used to undersample profiles *relative to*
486+
* @c tracesSampleRate .
487487
* @note Setting this value to @c nil enables an experimental new profiling mode, called continuous
488488
* profiling. This allows you to start and stop a profiler any time with @c SentrySDK.startProfiler
489489
* and @c SentrySDK.stopProfiler, which can run with no time limit, periodically uploading profiling
@@ -493,6 +493,9 @@ NS_SWIFT_NAME(Options)
493493
* automatically started for app launches. If you wish to sample them, you must do so at the
494494
* callsites where you use the API or configure launch profiling. Continuous profiling is not
495495
* automatically started for performance transactions as was the previous version of profiling.
496+
* @seealso https://docs.sentry.io/platforms/apple/profiling/ for more information about the
497+
* different profiling modes.
498+
* @note The default is @c nil (which implies continuous profiling mode).
496499
* @warning The new continuous profiling mode is experimental and may still contain bugs.
497500
* @note Profiling is automatically disabled if a thread sanitizer is attached.
498501
*/

Sources/Sentry/Public/SentrySDK.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,26 @@ SENTRY_NO_INIT
353353

354354
#endif
355355

356+
#if SENTRY_TARGET_PROFILING_SUPPORTED
357+
/**
358+
* Start a new continuous profiling session if one is not already running.
359+
* @note Unlike trace-based profiling, continuous profiling does not take into account @c
360+
* SentryOptions.profilesSampleRate ; a call to this method will always start a profile if one is
361+
* not already running. This includes app launch profiles configured with @c
362+
* SentryOptions.enableAppLaunchProfiling .
363+
* @warning Continuous profiling mode is experimental and may still contain bugs.
364+
* @seealso https://docs.sentry.io/platforms/apple/guides/ios/profiling/#continuous-profiling
365+
*/
366+
+ (void)startProfiler;
367+
368+
/**
369+
* Stop a continuous profiling session if there is one ongoing.
370+
* @warning Continuous profiling mode is experimental and may still contain bugs.
371+
* @seealso https://docs.sentry.io/platforms/apple/guides/ios/profiling/#continuous-profiling
372+
*/
373+
+ (void)stopProfiler;
374+
#endif // SENTRY_TARGET_PROFILING_SUPPORTED
375+
356376
@end
357377

358378
NS_ASSUME_NONNULL_END

Sources/Sentry/include/SentrySDK+Private.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,6 @@ SentrySDK ()
5252
*/
5353
+ (void)captureEnvelope:(SentryEnvelope *)envelope;
5454

55-
#if SENTRY_TARGET_PROFILING_SUPPORTED
56-
/**
57-
* Start a new continuous profiling session if one is not already running.
58-
* @seealso https://docs.sentry.io/platforms/apple/profiling/
59-
*/
60-
+ (void)startProfiler;
61-
62-
/**
63-
* Stop a continuous profiling session if there is one ongoing. This doesn't immediately
64-
* stop the profiler, rather, the current profiling chunk timer will be allowed to expire and will
65-
* not be renewed afterwards.
66-
* @seealso https://docs.sentry.io/platforms/apple/profiling/
67-
*/
68-
+ (void)stopProfiler;
69-
#endif // SENTRY_TARGET_PROFILING_SUPPORTED
70-
7155
@end
7256

7357
NS_ASSUME_NONNULL_END

Sources/Sentry/include/SentrySampleDecision+Private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#import <Foundation/Foundation.h>
22

3+
#import "SentrySampleDecision.h"
4+
35
/**
46
Returns the value to use when serializing a SentrySampleDecision.
57
*/

Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ class SentryFramesTrackerTests: XCTestCase {
3333
fixture = Fixture()
3434
}
3535

36+
override func tearDown() {
37+
super.tearDown()
38+
clearTestState()
39+
}
40+
3641
func testIsNotRunning_WhenNotStarted() {
3742
XCTAssertFalse(self.fixture.sut.isRunning)
3843
}

Tests/SentryTests/SentryOptionsTest.m

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ - (void)assertDefaultValues:(SentryOptions *)options
732732
# pragma clang diagnostic pop
733733
XCTAssertNil(options.profilesSampleRate);
734734
XCTAssertNil(options.profilesSampler);
735-
XCTAssert([options isContinuousProfilingEnabled]);
735+
XCTAssertTrue([options isContinuousProfilingEnabled]);
736736
#endif // SENTRY_TARGET_PROFILING_SUPPORTED
737737

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

1154-
XCTAssert([options isContinuousProfilingEnabled]);
1154+
XCTAssertTrue([options isContinuousProfilingEnabled]);
11551155
}
11561156

11571157
- (void)testProfilesSampleRate_SetToNil
@@ -1182,6 +1182,8 @@ - (void)testProfilesSampleRateLowerBound
11821182
options.profilesSampleRate = tooLow;
11831183
XCTAssertEqual(options.profilesSampleRate.doubleValue, 0);
11841184

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

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

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

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

12141218
XCTAssertNil(options.profilesSampleRate);
1215-
XCTAssert([options isContinuousProfilingEnabled]);
1219+
XCTAssertTrue([options isContinuousProfilingEnabled]);
12161220
}
12171221

12181222
- (void)testIsProfilingEnabled_ProfilesSampleRateSetToZero_IsDisabled
@@ -1311,14 +1315,14 @@ - (void)testDefaultProfilesSampler
13111315
{
13121316
SentryOptions *options = [self getValidOptions:@{}];
13131317
XCTAssertNil(options.profilesSampler);
1314-
XCTAssert([options isContinuousProfilingEnabled]);
1318+
XCTAssertTrue([options isContinuousProfilingEnabled]);
13151319
}
13161320

13171321
- (void)testGarbageProfilesSampler_ReturnsNil
13181322
{
13191323
SentryOptions *options = [self getValidOptions:@{ @"profilesSampler" : @"fault" }];
13201324
XCTAssertNil(options.profilesSampler);
1321-
XCTAssert([options isContinuousProfilingEnabled]);
1325+
XCTAssertTrue([options isContinuousProfilingEnabled]);
13221326
}
13231327

13241328
#endif // SENTRY_TARGET_PROFILING_SUPPORTED

Tests/SentryTests/SentrySDKTests.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,17 +423,18 @@ class SentrySDKTests: XCTestCase {
423423
func testStartingContinuousProfilerWithSampleRateZero() throws {
424424
givenSdkWithHub()
425425

426-
// nil is the default value for profilesSampleRate, so we don't have to explicitly set it on the fixture
427-
XCTAssertNil(fixture.options.profilesSampleRate)
426+
fixture.options.profilesSampleRate = 0
427+
XCTAssertEqual(try XCTUnwrap(fixture.options.profilesSampleRate).doubleValue, 0)
428+
428429
XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
429430
SentrySDK.startProfiler()
430-
XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling())
431+
XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
431432
}
432433

433434
func testStartingContinuousProfilerWithSampleRateNil() throws {
434435
givenSdkWithHub()
435436

436-
fixture.options.profilesSampleRate = nil
437+
// nil is the default initial value for profilesSampleRate, so we don't have to explicitly set it on the fixture
437438
XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling())
438439
SentrySDK.startProfiler()
439440
XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling())

Tests/SentryTests/Transaction/SentryTransactionTests.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ class SentryTransactionTests: XCTestCase {
4747
super.setUp()
4848
fixture = Fixture()
4949
}
50+
51+
override func tearDown() {
52+
super.tearDown()
53+
clearTestState()
54+
}
5055

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

224229
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
225230
func testTransactionWithContinuousProfile() throws {
226-
SentrySDK.setStart(Options())
231+
let options = Options()
232+
SentrySDK.setStart(options)
227233
let transaction = fixture.getTransaction()
228234
SentryContinuousProfiler.start()
229235
let profileId = try XCTUnwrap(SentryContinuousProfiler.profiler()?.profilerId.sentryIdString)

0 commit comments

Comments
 (0)