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-6261] Detect uncaught objc exceptions on Mac Catalyst and iOSAppOnMac #1053

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
//#define BSG_KSLogger_LocalLevel TRACE
#import "BSG_KSLogger.h"

#import <objc/runtime.h>

// ============================================================================
#pragma mark - Globals -
// ============================================================================
Expand Down Expand Up @@ -130,9 +132,54 @@ void bsg_recordException(NSException *exception) {

BSG_KSLOG_DEBUG(@"Calling main crash handler.");
bsg_g_context->onCrash(crashContext());

bsg_kscrashsentry_resumeThreads();
}
}

// ============================================================================
#pragma mark - iOS apps on macOS -
// ============================================================================

// iOS apps behave a little differently when running on macOS via Catalyst or
// on Apple Silicon. Uncaught NSExceptions raised while handling UI events get
// caught by AppKit and are not propagated to NSUncaughtExceptionHandler or
// std::terminate_handler (reported to Apple: FB8901200) therefore we need
// another way to detect them...

#if TARGET_OS_IOS

static Method NSApplication_reportException;

/// Pointer to the real implementation of -[NSApplication reportException:]
static void (* NSApplication_reportException_imp)(id, SEL, NSException *);

/// Overrides -[NSApplication reportException:]
static void bsg_reportException(id self, SEL _cmd, NSException *exception) {
BSG_KSLOG_DEBUG(@"reportException: %@", exception);

bsg_kscrashsentry_beginHandlingCrash(bsg_g_context);

bsg_recordException(exception);

#if TARGET_OS_MACCATALYST
// Mac Catalyst apps continue to run after an uncaught exception is thrown
// while handling a UI event. Our crash sentries should remain installed to
// catch any subsequent unhandled exceptions or crashes.
#else
// iOS apps running on Apple Silicon Macs terminate with an EXC_BREAKPOINT
// mach exception. We don't want to catch that because its stack trace will
// not point to where the exception was raised (its top frame will be
// -[NSApplication _crashOnException:]) so we should uninstall our crash
// sentries.
bsg_kscrashsentry_uninstall(BSG_KSCrashTypeAll);
#endif

NSApplication_reportException_imp(self, _cmd, exception);
}

#endif

// ============================================================================
#pragma mark - API -
// ============================================================================
Expand All @@ -153,6 +200,18 @@ bool bsg_kscrashsentry_installNSExceptionHandler(
BSG_KSLOG_DEBUG(@"Setting new handler.");
NSSetUncaughtExceptionHandler(&bsg_ksnsexc_i_handleException);

#if TARGET_OS_IOS
NSApplication_reportException =
class_getInstanceMethod(NSClassFromString(@"NSApplication"),
NSSelectorFromString(@"reportException:"));
if (NSApplication_reportException) {
BSG_KSLOG_DEBUG(@"Overriding -[NSApplication reportException:]");
NSApplication_reportException_imp = (void *)
method_setImplementation(NSApplication_reportException,
(IMP)bsg_reportException);
}
#endif

return true;
}

Expand All @@ -164,5 +223,14 @@ void bsg_kscrashsentry_uninstallNSExceptionHandler(void) {

BSG_KSLOG_DEBUG(@"Restoring original handler.");
NSSetUncaughtExceptionHandler(bsg_g_previousUncaughtExceptionHandler);

#if TARGET_OS_IOS
if (NSApplication_reportException && NSApplication_reportException_imp) {
BSG_KSLOG_DEBUG(@"Restoring original -[NSApplication reportException:]");
method_setImplementation(NSApplication_reportException,
(IMP)NSApplication_reportException_imp);
}
#endif

bsg_g_installed = 0;
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## TBD

### Bug fixes

* Uncaught exceptions thrown while handing UI events in iOS apps running on macOS are now detected.
[#1053](https://github.com/bugsnag/bugsnag-cocoa/pull/1053)

## 6.8.1 (2021-03-24)

### Bug fixes
Expand Down