Skip to content

Commit

Permalink
feat: Add flag stichAsyncCode
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 committed Jun 16, 2021
1 parent 1694a0f commit 32e1eeb
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 37 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)
- Remove invalid excludes from `Package.swift` (#1169)

## 7.2.0-beta.1
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
71 changes: 45 additions & 26 deletions Tests/SentryTests/Integrations/SentryCrashIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,30 @@ class SentryCrashIntegrationTests: XCTestCase {

private class Fixture {

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

return session
}

let session: SentrySession
let currentDateProvider = TestCurrentDateProvider()
let dispatchQueueWrapper = TestSentryDispatchQueueWrapper()
let options: Options
let hub: SentryHub
let fileManager: SentryFileManager
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() {
session = SentrySession(releaseName: "1.0.0")
session.incrementErrors()

sentryCrash = TestSentryCrashAdapter.sharedInstance()
sentryCrash.internalActiveDurationSinceLastCrash = 5.0
sentryCrash.internalCrashedLastLaunch = true
return sentryCrash
}

var hub: SentryHub {
let client = Client(options: options)
return TestHub(client: client, andScope: nil)
}

var fileManager: SentryFileManager {
return try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider())

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

fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider())

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

func getSut() -> SentryCrashIntegration {
Expand Down Expand Up @@ -172,6 +165,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:@{ @"attachStacktrace" : @"Invalid" }];
XCTAssertEqual(NO, options.attachStacktrace);
}

- (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 32e1eeb

Please sign in to comment.