Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
aa8c7d9
Adding option
brustolin May 13, 2022
a031821
Moving Tracker
brustolin May 17, 2022
8a30e05
Capturing threads and stacktraces
brustolin May 19, 2022
6f531c9
tests
brustolin Jun 3, 2022
71fa2a9
Merge branch 'master' into feat/anr-report
brustolin Jun 3, 2022
bb55710
Format code
getsentry-bot Jun 3, 2022
8bb2744
Update CHANGELOG.md
brustolin Jun 3, 2022
4ceb2a0
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 3, 2022
bdbb43f
Update SentryANRTrackingIntegration.m
brustolin Jun 3, 2022
ac91664
Format code
getsentry-bot Jun 3, 2022
9329348
Update Sources/Sentry/Public/SentryOptions.h
brustolin Jun 7, 2022
f6cac32
Update SentryOutOfMemoryIntegrationTests.swift
brustolin Jun 7, 2022
40ad695
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 7, 2022
560a4da
Update SentryANRTracker.m
brustolin Jun 7, 2022
e92ec71
Update SentryANRTracker.m
brustolin Jun 7, 2022
1451d9c
Apply suggestions from code review
brustolin Jun 8, 2022
342d409
Review Comments
brustolin Jun 8, 2022
7ca6f9a
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 8, 2022
8668e7f
Format code
getsentry-bot Jun 8, 2022
7d59aaa
Update SentryANRTracker.m
brustolin Jun 8, 2022
9e35381
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 8, 2022
4aa37e2
fixes
brustolin Jun 8, 2022
e071ea6
Merge branch 'master' into feat/anr-report
brustolin Jun 8, 2022
ba4d310
Update SentryThreadInspectorTests.swift
brustolin Jun 8, 2022
15035ef
fix tests
brustolin Jun 10, 2022
c22dfd0
Renaming
brustolin Jun 10, 2022
b65589c
Format code
getsentry-bot Jun 10, 2022
c9ed569
Apply suggestions from code review
brustolin Jun 10, 2022
8398ee9
refactoring
brustolin Jun 10, 2022
2d70f8c
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 10, 2022
14284ac
Remove tests
brustolin Jun 10, 2022
53da818
Format code
getsentry-bot Jun 10, 2022
567049e
ANRTracker
brustolin Jun 13, 2022
3ac5510
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 13, 2022
4e0e708
Update SentrySDKTests.swift
brustolin Jun 13, 2022
521ed3a
Apply suggestions from code review
brustolin Jun 13, 2022
410cafa
Format code
getsentry-bot Jun 13, 2022
40b363d
improviments
brustolin Jun 13, 2022
243b550
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 13, 2022
4b49448
Update SentryANRTrackingIntegrationTests.swift
brustolin Jun 13, 2022
8a9264e
Update SentryANRTrackingIntegrationTests.swift
brustolin Jun 14, 2022
ae0b168
Merge branch 'master' into feat/anr-report
brustolin Jun 14, 2022
495a1d7
Update SentryANRTrackingIntegrationTests.swift
brustolin Jun 14, 2022
00659ac
remove default timeout
brustolin Jun 14, 2022
b5105a7
improving tests
brustolin Jun 14, 2022
109ed7f
Format code
getsentry-bot Jun 14, 2022
920226f
Update SentryANRTrackingIntegration.m
brustolin Jun 14, 2022
f336638
Merge branch 'feat/anr-report' of https://github.com/getsentry/sentry…
brustolin Jun 14, 2022
97b483a
Format code
getsentry-bot Jun 14, 2022
2737889
Main Thread
brustolin Jun 15, 2022
b49f671
Update SentryThreadInspector.h
brustolin Jun 20, 2022
47bf7a9
Disabled by default
brustolin Jun 20, 2022
3c6bcad
Merge branch 'master' into feat/anr-report
brustolin Jun 20, 2022
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
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- feat: Add ANR tracking (#1861)

### Fixes

- Fix JSON conversion error message (#1856)
Expand Down
4 changes: 4 additions & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.enableCoreDataTracking = true
options.enableProfiling = true
options.attachScreenshot = true
options.anrEnable = true
options.anrTimeoutInterval = 2
options.enableUserInteractionTracing = true
}

Thread.current.name = "MAIN_THREAD"

return true
}
}
6 changes: 6 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -643,9 +643,11 @@
D875ED0B276CC84700422FAC /* SentryNSDataTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D875ED0A276CC84700422FAC /* SentryNSDataTrackerTests.swift */; };
D884A20527C80F6300074664 /* SentryCoreDataTrackerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D884A20327C80F2700074664 /* SentryCoreDataTrackerTest.swift */; };
D885266427739D01001269FC /* SentryFileIOTrackingIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D885266327739D01001269FC /* SentryFileIOTrackingIntegrationTests.swift */; };
D8853C842833EABC00700D64 /* SentryANRTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BCFA71427D0BAB7008C662C /* SentryANRTracker.h */; };
D88817D826D7149100BF2251 /* SentryTraceState.m in Sources */ = {isa = PBXBuildFile; fileRef = D88817D626D7149100BF2251 /* SentryTraceState.m */; };
D88817DA26D72AB800BF2251 /* SentryTraceState.h in Headers */ = {isa = PBXBuildFile; fileRef = D88817D926D72AB800BF2251 /* SentryTraceState.h */; };
D88817DD26D72BA500BF2251 /* SentryTraceStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88817DB26D72B7B00BF2251 /* SentryTraceStateTests.swift */; };
D8918B222849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */; };
D8AB40DB2806EC1900E5E9F7 /* SentryScreenshotIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8AB40DA2806EC1900E5E9F7 /* SentryScreenshotIntegration.h */; };
D8ACE3C72762187200F5A213 /* SentryNSDataSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D8ACE3C42762187200F5A213 /* SentryNSDataSwizzling.m */; };
D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = D8ACE3C52762187200F5A213 /* SentryNSDataTracker.m */; };
Expand Down Expand Up @@ -1376,6 +1378,7 @@
D88817D626D7149100BF2251 /* SentryTraceState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTraceState.m; sourceTree = "<group>"; };
D88817D926D72AB800BF2251 /* SentryTraceState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryTraceState.h; path = include/SentryTraceState.h; sourceTree = "<group>"; };
D88817DB26D72B7B00BF2251 /* SentryTraceStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceStateTests.swift; sourceTree = "<group>"; };
D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySDKIntegrationTestsBase.swift; sourceTree = "<group>"; };
D8AB40DA2806EC1900E5E9F7 /* SentryScreenshotIntegration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryScreenshotIntegration.h; path = include/SentryScreenshotIntegration.h; sourceTree = "<group>"; };
D8ACE3C42762187200F5A213 /* SentryNSDataSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryNSDataSwizzling.m; sourceTree = "<group>"; };
D8ACE3C52762187200F5A213 /* SentryNSDataTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryNSDataTracker.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1766,6 +1769,7 @@
7B4260332630315C00B36EDD /* SampleError.swift */,
7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */,
8ED3D305264DFE700049393B /* SentryUIViewControllerSanitizerTests.swift */,
D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */,
);
path = SentryTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -2906,6 +2910,7 @@
7BE1E32824F7AE08009D3AD0 /* SentrySession+Private.h in Headers */,
7B5CAF7127F5953400ED0DB6 /* SentryEnvelope+Private.h in Headers */,
63FE713320DA4C1100CDBAE8 /* SentryCrashCPU.h in Headers */,
D8853C842833EABC00700D64 /* SentryANRTracker.h in Headers */,
63FE715B20DA4C1100CDBAE8 /* SentryCrashSignalInfo.h in Headers */,
63FE70E520DA4C1000CDBAE8 /* SentryCrashMonitor_CPPException.h in Headers */,
7B127B0D27CF6F2300A71ED2 /* SentryANRTrackingIntegration.h in Headers */,
Expand Down Expand Up @@ -3485,6 +3490,7 @@
7B59398224AB47650003AAD2 /* SentrySessionTrackerTests.swift in Sources */,
7B05A61824A4D14A00EF211D /* SentrySessionGeneratorTests.swift in Sources */,
63FE720920DA66EC00CDBAE8 /* XCTestCase+SentryCrash.m in Sources */,
D8918B222849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift in Sources */,
7B85BD8E24C5C3A6000A4225 /* SentryFileManagerTestExtension.swift in Sources */,
7B0002342477F52D0035FEF1 /* SentrySessionTests.swift in Sources */,
);
Expand Down
5 changes: 5 additions & 0 deletions Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
BlueprintName = "SentryTests"
ReferencedContainer = "container:Sentry.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "SentrySDKIntegrationTestsBase">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
</TestAction>
Expand Down
12 changes: 12 additions & 0 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,18 @@ NS_SWIFT_NAME(Options)
*/
@property (nonatomic, assign) BOOL sendClientReports;

/**
* When enabled, the SDK tracks when the application stops responding for a specific amount of
* time defined by the `anrTimeoutInterval` option.
*/
@property (nonatomic, assign) BOOL anrEnable;

/**
* The minimum amount of time an app shoud be unresponsive to be classified as an ANR.
* The actual amount may be a little longer.
*/
@property (nonatomic, assign) NSTimeInterval anrTimeoutInterval;

@end

NS_ASSUME_NONNULL_END
110 changes: 86 additions & 24 deletions Sources/Sentry/SentryANRTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,42 @@

NS_ASSUME_NONNULL_BEGIN

#if SENTRY_HAS_UIKIT

@interface
SentryANRTracker ()

@property (weak, nonatomic) id<SentryANRTrackerDelegate> delegate;
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
@property (nonatomic, strong) id<SentryCurrentDateProvider> currentDate;
@property (nonatomic, strong) SentryCrashWrapper *crashWrapper;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper;
@property (nonatomic, strong) SentryThreadWrapper *threadWrapper;
@property (nonatomic, strong) NSMutableSet<id<SentryANRTrackerDelegate>> *listeners;

@property (weak, nonatomic) NSThread *thread;

@end

@implementation SentryANRTracker
@implementation SentryANRTracker {
NSObject *threadLock;
BOOL running;
}

- (instancetype)initWithDelegate:(id<SentryANRTrackerDelegate>)delegate
timeoutIntervalMillis:(NSUInteger)timeoutIntervalMillis
currentDateProvider:(id<SentryCurrentDateProvider>)currentDateProvider
crashWrapper:(SentryCrashWrapper *)crashWrapper
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
threadWrapper:(SentryThreadWrapper *)threadWrapper
- (instancetype)initWithCurrentDateProvider:(id<SentryCurrentDateProvider>)currentDateProvider
crashWrapper:(SentryCrashWrapper *)crashWrapper
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
threadWrapper:(SentryThreadWrapper *)threadWrapper
{
if (self = [super init]) {
self.delegate = delegate;
self.timeoutInterval = (double)timeoutIntervalMillis / 1000;
self.timeoutInterval = (double)SENTRY_ANR_TRACKER_TIMEOUT_MILLIS / 1000;
self.currentDate = currentDateProvider;
self.crashWrapper = crashWrapper;
self.dispatchQueueWrapper = dispatchQueueWrapper;
self.threadWrapper = threadWrapper;
self.listeners = [NSMutableSet new];
threadLock = [[NSObject alloc] init];
running = false;
}
return self;
}

- (void)start
{
[NSThread detachNewThreadSelector:@selector(detectANRs) toTarget:self withObject:nil];
}

- (void)detectANRs
{
NSThread.currentThread.name = @"io.sentry.anr-tracker";
Expand All @@ -68,7 +63,7 @@ - (void)detectANRs
if (blockExecutedOnMainThread) {
if (wasPreviousANR) {
[SentryLog logWithMessage:@"ANR stopped." andLevel:kSentryLevelWarning];
[self.delegate anrStopped];
[self notifyEnd];
}

wasPreviousANR = NO;
Expand Down Expand Up @@ -103,18 +98,85 @@ - (void)detectANRs

wasPreviousANR = YES;
[SentryLog logWithMessage:@"ANR detected." andLevel:kSentryLevelWarning];
[self.delegate anrDetected];
[self notifyBegin];
}
}

- (void)notifyBegin
{
NSArray *targets = [NSArray new];
@synchronized(self.listeners) {
targets = [self.listeners allObjects];
}

for (id<SentryANRTrackerDelegate> target in targets) {
[target anrDetected];
}
}

- (void)notifyEnd
{
NSArray *targets = [NSArray new];
@synchronized(self.listeners) {
targets = [self.listeners allObjects];
}

for (id<SentryANRTrackerDelegate> target in targets) {
[target anrStopped];
}
}

- (void)addListener:(id<SentryANRTrackerDelegate>)listener
{
@synchronized(self.listeners) {
[self.listeners addObject:listener];

if (self.listeners.count > 0 && !running) {
@synchronized(threadLock) {
if (!running) {
[self start];
}
}
}
}
}

- (void)removeListener:(id<SentryANRTrackerDelegate>)listener
{
@synchronized(self.listeners) {
[self.listeners removeObject:listener];

if (self.listeners.count == 0) {
[self stop];
}
}
}

- (void)clear
{
@synchronized(self.listeners) {
[self.listeners removeAllObjects];
[self stop];
}
}

- (void)start
{
@synchronized(threadLock) {
[NSThread detachNewThreadSelector:@selector(detectANRs) toTarget:self withObject:nil];
running = YES;
}
}

- (void)stop
{
[SentryLog logWithMessage:@"Stopping ANR detection" andLevel:kSentryLevelInfo];
[self.thread cancel];
@synchronized(threadLock) {
[SentryLog logWithMessage:@"Stopping ANR detection" andLevel:kSentryLevelInfo];
[self.thread cancel];
running = NO;
}
}

@end

#endif

NS_ASSUME_NONNULL_END
60 changes: 33 additions & 27 deletions Sources/Sentry/SentryANRTrackingIntegration.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
#import "SentryANRTrackingIntegration.h"
#import "SentryANRTracker.h"
#import "SentryClient+Private.h"
#import "SentryCrashMachineContext.h"
#import "SentryCrashWrapper.h"
#import "SentryDefaultCurrentDateProvider.h"
#import "SentryDispatchQueueWrapper.h"
#import "SentryEvent.h"
#import "SentryException.h"
#import "SentryHub+Private.h"
#import "SentryLog.h"
#import "SentryMechanism.h"
#import "SentrySDK+Private.h"
#import "SentryThreadInspector.h"
#import "SentryThreadWrapper.h"
#import <Foundation/Foundation.h>
#import <SentryAppState.h>
Expand All @@ -15,53 +23,38 @@

#if SENTRY_HAS_UIKIT

/**
* As we only use the ANR tracking integration for detecting falsely reported OOMs we can use a more
* defensive value, because we are not reporting any ANRs.
*/
static NSUInteger const SENTRY_ANR_TRACKER_TIMEOUT_MILLIS = 2000;

@interface
SentryANRTrackingIntegration ()

@property (nonatomic, strong) SentryANRTracker *tracker;
@property (nonatomic, strong) SentryAppStateManager *appStateManager;
@property (nonatomic, strong) SentryCrashWrapper *crashWrapper;
@property (nonatomic, strong) SentryOptions *options;

@end

@implementation SentryANRTrackingIntegration

- (void)installWithOptions:(SentryOptions *)options
{
SentryDependencyContainer *dependencies = [SentryDependencyContainer sharedInstance];
self.crashWrapper = dependencies.crashWrapper;

if ([self shouldBeDisabled:options]) {
[options removeEnabledIntegration:NSStringFromClass([self class])];
return;
}

self.appStateManager = dependencies.appStateManager;
self.tracker = SentryDependencyContainer.sharedInstance.anrTracker;
self.tracker.timeoutInterval = options.anrTimeoutInterval;

self.tracker =
[[SentryANRTracker alloc] initWithDelegate:self
timeoutIntervalMillis:SENTRY_ANR_TRACKER_TIMEOUT_MILLIS
currentDateProvider:[SentryDefaultCurrentDateProvider sharedInstance]
crashWrapper:dependencies.crashWrapper
dispatchQueueWrapper:[[SentryDispatchQueueWrapper alloc] init]
threadWrapper:dependencies.threadWrapper];
[self.tracker start];
[self.tracker addListener:self];
self.options = options;
}

- (BOOL)shouldBeDisabled:(SentryOptions *)options
{
if (!options.enableOutOfMemoryTracking) {
if (!options.anrEnable) {
return YES;
}

// In case the debugger is attached
if ([self.crashWrapper isBeingTraced]) {
if ([SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
return YES;
}

Expand All @@ -70,19 +63,32 @@ - (BOOL)shouldBeDisabled:(SentryOptions *)options

- (void)uninstall
{
[self.tracker stop];
[self.tracker removeListener:self];
}

- (void)anrDetected
{
[self.appStateManager
updateAppState:^(SentryAppState *appState) { appState.isANROngoing = YES; }];
NSString *message =
[NSString stringWithFormat:@"Application Not Responding for at least %li ms.",
(long)(self.options.anrTimeoutInterval * 1000)];

NSArray<SentryThread *> *threads =
[SentrySDK.currentHub.getClient.threadInspector getCurrentThreadsWithStackTrace:YES];

SentryEvent *event = [[SentryEvent alloc] initWithLevel:kSentryLevelError];
SentryException *sentryException = [[SentryException alloc] initWithValue:message
type:@"App Hanging"];
sentryException.mechanism = [[SentryMechanism alloc] initWithType:@"anr"];

event.exceptions = @[ sentryException ];
event.threads = threads;

[SentrySDK captureEvent:event];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

h: @marandaneto, can you have a look if creating the ANR event aligns with Android, please?

}

- (void)anrStopped
{
[self.appStateManager
updateAppState:^(SentryAppState *appState) { appState.isANROngoing = NO; }];
// We dont report when an ANR ends.
}

@end
Expand Down
1 change: 1 addition & 0 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
|| (nil != event.exceptions && [event.exceptions count] > 0);

BOOL threadsNotAttached = !(nil != event.threads && event.threads.count > 0);

if (!isCrashEvent && shouldAttachStacktrace && threadsNotAttached) {
event.threads = [self.threadInspector getCurrentThreads];
}
Expand Down
Loading