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

[PLAT-6311] Ignore hangs while app is in the background #1075

Merged
merged 2 commits into from
Apr 21, 2021
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
2 changes: 1 addition & 1 deletion .buildkite/pipeline.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ steps:
- "--app=/app/build/iOSTestApp.ipa"
- "--farm=bs"
- "--device=IOS_11_0_IPHONE_8_PLUS"
- "--appium-version=1.7.0"
- "--appium-version=1.8.0"
- "--fail-fast"
concurrency: 9
concurrency_group: browserstack-app
Expand Down
4 changes: 2 additions & 2 deletions .buildkite/pipeline.quick.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ steps:
- "--app=/app/build/iOSTestApp.ipa"
- "--farm=bs"
- "--device=IOS_10"
- "--appium-version=1.7.0"
- "--appium-version=1.8.0"
- "--fail-fast"
- "-e"
- "features/[o-z].*.feature"
Expand All @@ -246,7 +246,7 @@ steps:
- "--app=/app/build/iOSTestApp.ipa"
- "--farm=bs"
- "--device=IOS_10"
- "--appium-version=1.7.0"
- "--appium-version=1.8.0"
- "--fail-fast"
- "-e"
- "features/[a-n].*.feature"
Expand Down
2 changes: 1 addition & 1 deletion .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ steps:
- "--app=/app/build/iOSTestApp.ipa"
- "--farm=bs"
- "--device=IOS_10"
- "--appium-version=1.7.0"
- "--appium-version=1.8.0"
- "--fail-fast"
concurrency: 9
concurrency_group: browserstack-app
Expand Down
36 changes: 36 additions & 0 deletions Bugsnag/Helpers/BSGAppHangDetector.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,45 @@
#import "BugsnagThread+Recording.h"
#import "BugsnagThread+Private.h"

#if TARGET_OS_IOS
#import "BSGUIKit.h"
#endif


@interface BSGAppHangDetector ()

@property (nonatomic) CFRunLoopObserverRef observer;

@property (nonatomic) BOOL isInBackground;

@end


@implementation BSGAppHangDetector

#if TARGET_OS_IOS

- (instancetype)init {
if (self = [super init]) {
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationDidEnterBackground)
name:UIApplicationDidEnterBackgroundNotification object:nil];

[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification object:nil];
}
return self;
}

- (void)applicationDidEnterBackground {
self.isInBackground = YES;
}

- (void)applicationWillEnterForeground {
self.isInBackground = NO;
}

#endif

- (void)dealloc {
if (_observer) {
CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
Expand Down Expand Up @@ -59,6 +88,7 @@ - (void)startWithDelegate:(id<BSGAppHangDetectorDelegate>)delegate {
dispatch_queue_t backgroundQueue;
__block dispatch_semaphore_t semaphore;
__weak typeof(delegate) weakDelegate = delegate;
__weak typeof(self) weakSelf = self;

backgroundQueue = dispatch_queue_create("com.bugsnag.app-hang-detector", DISPATCH_QUEUE_SERIAL);

Expand All @@ -75,6 +105,12 @@ - (void)startWithDelegate:(id<BSGAppHangDetectorDelegate>)delegate {
dispatch_time_t timeout = dispatch_time(now, (int64_t)(threshold * NSEC_PER_SEC));
dispatch_after(after, backgroundQueue, ^{
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
if (weakSelf.isInBackground) {
bsg_log_debug(@"Ignoring app hang because app is in the background");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return;
}

if (bsg_ksmachisBeingTraced()) {
bsg_log_debug("Ignoring app hang because debugger is attached");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
Expand Down
24 changes: 24 additions & 0 deletions features/app_hangs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,27 @@ Feature: App hangs
And I relaunch the app
And I configure Bugsnag for "AppHangFatalDisabledScenario"
Then I should receive no errors

@skip_macos
Scenario: Fatal app hangs should be reported if the app hangs before going to the background
When I run "AppHangFatalOnlyScenario"
And I background the app for 3 seconds
And I relaunch the app
And I configure Bugsnag for "AppHangFatalOnlyScenario"
And I wait to receive an error
And the exception "message" equals "The app was terminated while unresponsive"

@skip_macos
Scenario: Fatal app hangs should not be reported if they occur once the app is in the background
When I run "AppHangDidEnterBackgroundScenario"
And I background the app for 3 seconds
And I relaunch the app
And I configure Bugsnag for "AppHangDidEnterBackgroundScenario"
Then I should receive no errors

@skip_macos
Scenario: App hangs should be reported if the app hangs after resuming from the background
When I run "AppHangDidBecomeActiveScenario"
And I background the app for 3 seconds
And I wait to receive an error
And the exception "message" equals "The app's main thread failed to respond to an event within 2000 milliseconds"
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
01B6BBB625DA82B800FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; };
01E0DB0B25E8EBD100A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */; };
01E5EAD225B713990066EA8A /* OOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E5EAD125B713990066EA8A /* OOMScenario.m */; };
01F1474425F282E600C2DC65 /* AppHangScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1474325F282E600C2DC65 /* AppHangScenario.swift */; };
01F1474425F282E600C2DC65 /* AppHangScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1474325F282E600C2DC65 /* AppHangScenarios.swift */; };
6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */; };
8A14F0F62282D4AE00337B05 /* (null) in Sources */ = {isa = PBXBuildFile; };
8A32DB8222424E3000EDD92F /* NSExceptionShiftScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A32DB8122424E3000EDD92F /* NSExceptionShiftScenario.m */; };
Expand Down Expand Up @@ -177,7 +177,7 @@
01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = "<group>"; };
01E5EAD025B713990066EA8A /* OOMScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OOMScenario.h; sourceTree = "<group>"; };
01E5EAD125B713990066EA8A /* OOMScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OOMScenario.m; sourceTree = "<group>"; };
01F1474325F282E600C2DC65 /* AppHangScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHangScenario.swift; sourceTree = "<group>"; };
01F1474325F282E600C2DC65 /* AppHangScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHangScenarios.swift; sourceTree = "<group>"; };
4994F05E0421A0B037DD2CC5 /* Pods_iOSTestApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOSTestApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = "<group>"; };
8A32DB8022424E3000EDD92F /* NSExceptionShiftScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSExceptionShiftScenario.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -569,7 +569,7 @@
F49695AE2445476700105DA9 /* Plugin */,
0037410E2473CF2300BE41AA /* AppAndDeviceAttributesScenario.swift */,
01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */,
01F1474325F282E600C2DC65 /* AppHangScenario.swift */,
01F1474325F282E600C2DC65 /* AppHangScenarios.swift */,
01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */,
0163BFA62583B3CF008DC28B /* DiscardClassesScenarios.swift */,
01E5EAD025B713990066EA8A /* OOMScenario.h */,
Expand Down Expand Up @@ -986,7 +986,7 @@
E7A324DE247E70E6008B0052 /* SessionCallbackOrderScenario.swift in Sources */,
E7A324DA247E70C4008B0052 /* SessionCallbackCrashScenario.swift in Sources */,
E7DD40452473D980000EDC14 /* UserDefaultInfoScenario.swift in Sources */,
01F1474425F282E600C2DC65 /* AppHangScenario.swift in Sources */,
01F1474425F282E600C2DC65 /* AppHangScenarios.swift in Sources */,
E7A324E3247E7C17008B0052 /* SessionCallbackRemovalScenario.m in Sources */,
E7767F15221C223C0006648C /* NewSessionScenario.swift in Sources */,
F42951BEB2518C610A85FE0D /* BuiltinTrapScenario.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
01E0DB0625E8E95700A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */; };
01ECBCF425A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ECBCF225A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift */; };
01ECBCF525A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ECBCF325A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift */; };
01F1473A25F2817100C2DC65 /* AppHangScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1473925F2817100C2DC65 /* AppHangScenario.swift */; };
01F1473A25F2817100C2DC65 /* AppHangScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1473925F2817100C2DC65 /* AppHangScenarios.swift */; };
01F47CC4254B1B3100B184AD /* OriginalErrorNSExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C21254B1B2C00B184AD /* OriginalErrorNSExceptionScenario.swift */; };
01F47CC5254B1B3100B184AD /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */; };
01F47CC6254B1B3100B184AD /* HandledExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C28254B1B2C00B184AD /* HandledExceptionScenario.swift */; };
Expand Down Expand Up @@ -175,7 +175,7 @@
01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = "<group>"; };
01ECBCF225A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledFalseScenario.swift; sourceTree = "<group>"; };
01ECBCF325A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledTrueScenario.swift; sourceTree = "<group>"; };
01F1473925F2817100C2DC65 /* AppHangScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHangScenario.swift; sourceTree = "<group>"; };
01F1473925F2817100C2DC65 /* AppHangScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHangScenarios.swift; sourceTree = "<group>"; };
01F47C21254B1B2C00B184AD /* OriginalErrorNSExceptionScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OriginalErrorNSExceptionScenario.swift; sourceTree = "<group>"; };
01F47C22254B1B2C00B184AD /* ThreadScenarios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadScenarios.h; sourceTree = "<group>"; };
01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -364,7 +364,7 @@
01F47C5B254B1B2E00B184AD /* AccessNonObjectScenario.m */,
01F47C60254B1B2E00B184AD /* AppAndDeviceAttributesScenario.swift */,
01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */,
01F1473925F2817100C2DC65 /* AppHangScenario.swift */,
01F1473925F2817100C2DC65 /* AppHangScenarios.swift */,
01018BAA25E417EC000312C6 /* AsyncSafeMallocScenario.m */,
01F47C73254B1B2E00B184AD /* AsyncSafeThreadScenario.h */,
01F47C40254B1B2D00B184AD /* AsyncSafeThreadScenario.m */,
Expand Down Expand Up @@ -720,7 +720,7 @@
017FBFB8254B09C300809042 /* MainWindowController.m in Sources */,
01F47CF0254B1B3100B184AD /* OnSendOverwriteScenario.swift in Sources */,
01F47D1B254B1B3100B184AD /* SIGSEGVScenario.m in Sources */,
01F1473A25F2817100C2DC65 /* AppHangScenario.swift in Sources */,
01F1473A25F2817100C2DC65 /* AppHangScenarios.swift in Sources */,
0163BF9B2583AF2A008DC28B /* DiscardClassesScenarios.swift in Sources */,
01F47D09254B1B3100B184AD /* BreadcrumbCallbackOverrideScenario.swift in Sources */,
0176C0B1254AE81B0066E0F3 /* AppDelegate.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class AppHangFatalOnlyScenario: Scenario {
}

override func run() {
NSLog("Hanging indefinitely...")
while true {}
}
}
Expand All @@ -69,6 +70,36 @@ class AppHangFatalDisabledScenario: Scenario {
}

override func run() {
NSLog("Hanging indefinitely...")
while true {}
}
}

#if os(iOS)

class AppHangDidEnterBackgroundScenario: Scenario {

override func run() {
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) {
NSLog("Recevied \($0.name), now hanging indefinitely...")
while true {}
}
}
}

class AppHangDidBecomeActiveScenario: Scenario {

override func startBugsnag() {
config.appHangThresholdMillis = 2_000
super.startBugsnag()
}

override func run() {
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) {
NSLog("Recevied \($0.name), now sleeping for 3 seconds...")
Thread.sleep(forTimeInterval: 3)
}
}
}

#endif
4 changes: 4 additions & 0 deletions features/steps/ios_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def click_if_present(element)
Maze.driver.background_app(-1)
end

When('I background the app for {int} seconds') do |duration|
Maze.driver.background_app(duration)
end

When('I relaunch the app') do
case Maze.driver.capabilities['platformName']
when 'Mac'
Expand Down