Skip to content

Commit

Permalink
feat: Add flag stichAsyncCode (#1172)
Browse files Browse the repository at this point in the history
Add stichAsyncCode to SentryOptions to be able to enable stitching stack traces
of async code together.

Fixes GH-1086
  • Loading branch information
philipphofmann authored Jun 18, 2021
1 parent 73c3f43 commit 570212d
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- feat: Add flag stichAsyncCode (#1172)
- feat: Support XCFramework for Carthage (#1175)
- fix: Remove invalid excludes from `Package.swift` (#1169)

Expand Down
10 changes: 10 additions & 0 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ NS_SWIFT_NAME(Options)
*/
@property (nonatomic, assign) BOOL attachStacktrace;

/**
* Attention: This is an experimental feature. Turning this feature on can have an impact on
* the grouping of your issues.
*
* When enabled, the SDK stitches stack traces of asynchronous code together.
*
* This feature is disabled by default.
*/
@property (nonatomic, assign) BOOL stitchAsyncCode;

/**
* Describes the Sentry SDK and its configuration used to capture and transmit an event.
*/
Expand Down
11 changes: 11 additions & 0 deletions Sources/Sentry/SentryCrashAdapter.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#import "SentryCrashAdapter.h"
#import "SentryCrash.h"
#import "SentryHook.h"
#import <Foundation/Foundation.h>
#import <SentryCrashDebug.h>

Expand Down Expand Up @@ -30,6 +31,16 @@ - (BOOL)isBeingTraced
return sentrycrashdebug_isBeingTraced();
}

- (void)installAsyncHooks
{
sentrycrash_install_async_hooks();
}

- (void)deactivateAsyncHooks
{
sentrycrash_deactivate_async_hooks();
}

@end

NS_ASSUME_NONNULL_END
24 changes: 13 additions & 11 deletions Sources/Sentry/SentryCrashIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#import "SentryDispatchQueueWrapper.h"
#import "SentryEvent.h"
#import "SentryFrameInAppLogic.h"
#import "SentryHook.h"
#import "SentryHub.h"
#import "SentryOutOfMemoryLogic.h"
#import "SentrySDK+Private.h"
Expand Down Expand Up @@ -37,20 +36,20 @@ @implementation SentryCrashIntegration

- (instancetype)init
{
if (self = [super init]) {
self.crashAdapter = [SentryCrashAdapter sharedInstance];
self.dispatchQueueWrapper = [[SentryDispatchQueueWrapper alloc] init];
}
self = [self initWithCrashAdapter:[SentryCrashAdapter sharedInstance]
andDispatchQueueWrapper:[[SentryDispatchQueueWrapper alloc] init]];

return self;
}

/** Internal constructor for testing */
- (instancetype)initWithCrashAdapter:(SentryCrashAdapter *)crashAdapter
andDispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
{
self = [self init];
self.crashAdapter = crashAdapter;
self.dispatchQueueWrapper = dispatchQueueWrapper;
if (self = [super init]) {
self.crashAdapter = crashAdapter;
self.dispatchQueueWrapper = dispatchQueueWrapper;
}

return self;
}
Expand Down Expand Up @@ -88,8 +87,11 @@ - (void)installWithOptions:(nonnull SentryOptions *)options
outOfMemoryLogic:logic];

[self startCrashHandler];
// TODO: enable with feature flag from SentryOptions because this is still experimental
// sentrycrash_install_async_hooks();

if (options.stitchAsyncCode) {
[self.crashAdapter installAsyncHooks];
}

[self configureScope];
}

Expand Down Expand Up @@ -136,7 +138,7 @@ - (void)uninstall
installation = nil;
installationToken = 0;
}
sentrycrash_deactivate_async_hooks();
[self.crashAdapter deactivateAsyncHooks];
}

- (void)configureScope
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ - (instancetype)init
self.enableOutOfMemoryTracking = YES;
self.sessionTrackingIntervalMillis = [@30000 unsignedIntValue];
self.attachStacktrace = YES;
self.stitchAsyncCode = NO;
self.maxAttachmentSize = 20 * 1024 * 1024;
self.sendDefaultPii = NO;
self.enableAutoUIPerformanceTracking = YES;
Expand Down Expand Up @@ -197,6 +198,10 @@ - (void)validateOptions:(NSDictionary<NSString *, id> *)options
self.attachStacktrace = [options[@"attachStacktrace"] boolValue];
}

if (nil != options[@"stitchAsyncCode"]) {
self.stitchAsyncCode = [options[@"stitchAsyncCode"] boolValue];
}

if (nil != options[@"maxAttachmentSize"]) {
self.maxAttachmentSize = [options[@"maxAttachmentSize"] unsignedIntValue];
}
Expand Down
4 changes: 4 additions & 0 deletions Sources/Sentry/include/SentryCrashAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ SENTRY_NO_INIT

- (BOOL)isBeingTraced;

- (void)installAsyncHooks;

- (void)deactivateAsyncHooks;

@end

NS_ASSUME_NONNULL_END
63 changes: 43 additions & 20 deletions Tests/SentryTests/Integrations/SentryCrashIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,30 @@ class SentryCrashIntegrationTests: XCTestCase {

private class Fixture {

var session: SentrySession {
let session = SentrySession(releaseName: "1.0.0")
session.incrementErrors()

return session
}

let currentDateProvider = TestCurrentDateProvider()
let dispatchQueueWrapper = TestSentryDispatchQueueWrapper()
let hub: SentryHub
let options: Options
let sentryCrash: TestSentryCrashAdapter

var options: Options {
let options = Options()
options.dsn = SentryCrashIntegrationTests.dsnAsString
options.releaseName = TestData.appState.releaseName
return options
}

var sentryCrash: TestSentryCrashAdapter {
let sentryCrash = TestSentryCrashAdapter.sharedInstance()
init() {
sentryCrash = TestSentryCrashAdapter.sharedInstance()
sentryCrash.internalActiveDurationSinceLastCrash = 5.0
sentryCrash.internalCrashedLastLaunch = true
return sentryCrash

options = Options()
options.dsn = SentryCrashIntegrationTests.dsnAsString
options.releaseName = TestData.appState.releaseName

let client = Client(options: options)
hub = TestHub(client: client, andScope: nil)
}

var hub: SentryHub {
let client = Client(options: options)
return TestHub(client: client, andScope: nil)
var session: SentrySession {
let session = SentrySession(releaseName: "1.0.0")
session.incrementErrors()

return session
}

var fileManager: SentryFileManager {
Expand Down Expand Up @@ -172,6 +169,32 @@ class SentryCrashIntegrationTests: XCTestCase {
XCTAssertNil(fileManager.readCrashedSession())
}

func testInstall_WhenStitchAsyncCallsEnabled_CallsInstallAsyncHooks() {
let sut = fixture.getSut()

let options = Options()
options.stitchAsyncCode = true
sut.install(with: options)

XCTAssertTrue(fixture.sentryCrash.installAsyncHooksCalled)
}

func testInstall_WhenStitchAsyncCallsDisabled_DoesNotCallInstallAsyncHooks() {
fixture.getSut().install(with: Options())

XCTAssertFalse(fixture.sentryCrash.installAsyncHooksCalled)
}

func testUninstall_CallsDeactivateAsyncHooks() {
let sut = fixture.getSut()

sut.install(with: Options())

sut.uninstall()

XCTAssertTrue(fixture.sentryCrash.deactivateAsyncHooksCalled)
}

func testOSCorrectlySetToScopeContext() {
let hub = fixture.hub
SentrySDK.setCurrentHub(hub)
Expand Down
4 changes: 4 additions & 0 deletions Tests/SentryTests/SentryCrash/TestSentryCrashAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ SENTRY_NO_INIT

@property (nonatomic, assign) BOOL internalIsBeingTraced;

@property (nonatomic, assign) BOOL installAsyncHooksCalled;

@property (nonatomic, assign) BOOL deactivateAsyncHooksCalled;

@end

NS_ASSUME_NONNULL_END
12 changes: 12 additions & 0 deletions Tests/SentryTests/SentryCrash/TestSentryCrashAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ + (instancetype)sharedInstance
instance.internalActiveDurationSinceLastCrash = NO;
instance.internalActiveDurationSinceLastCrash = 0;
instance.internalIsBeingTraced = NO;
instance.installAsyncHooksCalled = NO;
instance.deactivateAsyncHooksCalled = NO;
return instance;
}

Expand All @@ -27,4 +29,14 @@ - (BOOL)isBeingTraced
return self.internalIsBeingTraced;
}

- (void)installAsyncHooks
{
self.installAsyncHooksCalled = YES;
}

- (void)deactivateAsyncHooks
{
self.deactivateAsyncHooksCalled = YES;
}

@end
19 changes: 19 additions & 0 deletions Tests/SentryTests/SentryOptionsTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,24 @@ - (void)testInvalidAttachStackTrace
XCTAssertEqual(NO, options.attachStacktrace);
}

- (void)testStitchAsyncCodeDisabledPerDefault
{
SentryOptions *options = [self getValidOptions:@{}];
XCTAssertEqual(NO, options.stitchAsyncCode);
}

- (void)testStitchAsyncCodeEnabled
{
SentryOptions *options = [self getValidOptions:@{ @"stitchAsyncCode" : @YES }];
XCTAssertEqual(YES, options.stitchAsyncCode);
}

- (void)testInvalidStitchAsyncCode
{
SentryOptions *options = [self getValidOptions:@{ @"stitchAsyncCode" : @"Invalid" }];
XCTAssertEqual(NO, options.stitchAsyncCode);
}

- (void)testEmptyConstructorSetsDefaultValues
{
SentryOptions *options = [[SentryOptions alloc] init];
Expand All @@ -418,6 +436,7 @@ - (void)testEmptyConstructorSetsDefaultValues
XCTAssertEqual(YES, options.enableOutOfMemoryTracking);
XCTAssertEqual([@30000 unsignedIntValue], options.sessionTrackingIntervalMillis);
XCTAssertEqual(YES, options.attachStacktrace);
XCTAssertEqual(NO, options.stitchAsyncCode);
XCTAssertEqual(20 * 1024 * 1024, options.maxAttachmentSize);
XCTAssertTrue(options.enableAutoUIPerformanceTracking);
}
Expand Down

0 comments on commit 570212d

Please sign in to comment.