microsoft / plcrashreporter

Reliable, open-source crash reporting for iOS, macOS and tvOS
Other
2.9k stars 536 forks source link

crashReport lack exceptionInfo #177

Closed tamwei closed 3 years ago

tamwei commented 3 years ago

Description

When throw an exception, and load CrashReport, I found the crashReport lack exceptionInfo. But if move the crash code into dispatch block, and then the crashReport has the exceptionInfo. I think this problem maybe cause by NSSetUncaughtExceptionHandler, so I write a demo test.

1618473031

As the above code, the method 'crash' produced exception can not been caught, but exception produced by the 'dispatchCrash' can been caught.

Details

  1. Which SDK version are you using? PLCrashReporter-1.8.1
  2. Which OS version did you experience the issue on? macOS Big Sur 11.2
  3. Which CocoaPods/Carthage/Xcode version are you using? Xcode version: 12.3
  4. What device version did you see this error on? Were you using an emulator or a physical device? MacBook Pro,Apple M1
  5. What language are you using? Objective C
  6. What third party libraries are you using? none
MatkovIvan commented 3 years ago

Hi @tamwei,

Thanks for getting in touch! This is a known case. You can look how we deal with it in App Center (that wraps this library): https://github.com/microsoft/appcenter-sdk-apple/blob/4.1.1/AppCenterCrashes/AppCenterCrashes/Internals/Util/MSACApplicationForwarder.m#L35-L138

Duplicating the description here:

On macOS runtime, not all uncaught exceptions end in a custom NSUncaughtExceptionHandler. In addition "sometimes" exceptions don't even cause the app to crash, depending on where and when they happen.

Here are the known scenarios:

  1. Custom NSUncaughtExceptionHandler don't start working until after NSApplication has finished calling all of its delegate methods!

    Example:

    - (void)applicationDidFinishLaunching:(NSNotification *)note {
        ...
        [NSException raise:@"ExceptionAtStartup" format:@"This will not be recognized!"];
        ...
    }
  2. The default NSUncaughtExceptionHandler in NSApplication only logs exceptions to the console and ends their processing. Resulting in exceptions that occur in the NSApplication "scope" not occurring in a registered custom NSUncaughtExceptionHandler.

    Example:

    - (void)applicationDidFinishLaunching:(NSNotification *)note {
        ...
        [self performSelector:@selector(delayedException) withObject:nil afterDelay:5];
        ...
    }
    
    - (void)delayedException {
        NSArray *array = [NSArray array];
        [array objectAtIndex:23];
    }
  3. Any exceptions occurring in IBAction or other GUI does not even reach the NSApplication default UncaughtExceptionHandler.

    Example:

    - (IBAction)doExceptionCrash:(id)sender {
        NSArray *array = [NSArray array];
        [array objectAtIndex:23];
    }

Solution A:

Implement NSExceptionHandler and set the ExceptionHandlingMask to NSLogAndHandleEveryExceptionMask

Benefits:

  1. Solves all of the above scenarios.
  2. Clean solution using a standard Cocoa System specifically meant for this purpose.
  3. Safe. Doesn't use private API.

Problems:

  1. To catch all exceptions the NSExceptionHandlers mask has to include NSLogOtherExceptionMask and NSHandleOtherExceptionMask. But this will result in @catch blocks to be called after the exception handler processed the exception and likely lets the app crash and create a crash report. This makes the @catch block basically not work at all.
  2. If anywhere in the app a custom NSUncaughtExceptionHandler will be registered, e.g. in a closed source library the developer has to use, the complete mechanism will stop working.
  3. Not clear if this solves all scenarios there can be.
  4. Requires to adjust PLCrashReporter not to register its NSUncaughtExceptionHandler which is not a good idea, since it would require the NSExceptionHandler would catch all exceptions and that would cause PLCrashReporter to stop all running threads every time an exception occurs even if it will be handled right away, e.g. by a system framework.

Solution B:

Overwrite and extend specific methods of NSApplication. Can be implemented via subclassing NSApplication or by using a category.

Benefits:

  1. Solves scenarios 2 (by overwriting reportException:) and 3 (by overwriting sendEvent:).
  2. Subclassing approach isn't enforcing the mechanism onto apps and lets developers opt-in. (Category approach would enforce it and rather be a problem of this solution.)
  3. Safe. Doesn't use private API.

Problems:

  1. Does not automatically solve scenario 1. Developer would have to put all that code into @try @catch blocks.
  2. Not a clean implementation, rather feels like a workaround.
  3. Not clear if this solves all scenarios there can be.

References:

https://developer.apple.com/library/mac/documentation/cocoa/Conceptual/Exceptions/Tasks/ControllingAppResponse.html#//apple_ref/doc/uid/20000473-BBCHGJIJ http://stackoverflow.com/a/4199717/474794 http://stackoverflow.com/a/3419073/474794 http://macdevcenter.com/pub/a/mac/2007/07/31/understanding-exceptions-and-handlers-in-cocoa.html

tamwei commented 3 years ago

Thank you every much!