Bug 18427 - Catching native exceptions doesn't work properly
Summary: Catching native exceptions doesn't work properly
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 7.0.7
Hardware: Macintosh Mac OS
: --- enhancement
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-03-17 13:54 UTC by kissling
Modified: 2018-01-09 08:23 UTC (History)
8 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description kissling 2014-03-17 13:54:29 UTC
I would like to catch exceptions thrown by native code. In order to test this, I use NSArray's objectAtIndex: method with an invalid index. However, the app sometimes crashes with SIGSEGV after the call.

Steps to reproduce:
1. Create a new iPhone > Single View Controller project.
2. Replace the ViewDidLoad method in the ViewController with the following code.
3. Uncomment exactly one test case.
4. Everything except "Works0" will crash the app when run on both 64-bit and 32-bit iOS devices.
5. The crashes are different for every test case.

Workarounds:
So far, I have found NO workaround for this issue. While I understand that catching objective-C exceptions is not generally necessary (and ARC will not properly clean up without a special flag), I would like to have at least the possibility to catch and log them before the app terminates.
Providing a workaround would also be an appreciate intermediate solution!

Test Code:


        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            
            // Perform any additional setup after loading the view, typically from a nib.


            // Uncomment only one (1) of the following lines. 
            // * BuggyX will crash the process and produce a log in Xcode Organizer.
            // * WorksX will not crash and will log the exceptions as expected.

//            Buggy0();
            Works0();
//            Buggy1();
//            Buggy2();
//            Buggy3();
//            Buggy4();

        }

        private static void Buggy0()
        {
            Test();
        }

        private static void Works0()
        {
            System.Threading.Tasks.Task.Run(() =>
            {
                Test();
            }).Wait();

            Test();
        }

        private static void Buggy1()
        {
            System.Threading.Tasks.Task.Run(() =>
            {
                Test();
            }).Wait();

            System.Threading.Tasks.Task.Run(() =>
            {
                Test();
            }).Wait();
        }

        private static void Buggy2()
        {
            System.Threading.Tasks.Task.Run(() =>
            {
                Test();
                Test();
            }).Wait();
        }

        private static void Buggy3()
        {
            Works0();
            Works0();
        }

        private static void Buggy4()
        {
            Works0();
            Test();
        }

        private static void Test()
        {
            Console.WriteLine("Test {0}...", ++_invocationCount);
            try
            {
                new NSArray().ValueAt(0);
                Console.WriteLine("<no exception>");
            }
            catch (MonoTouchException e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                Console.WriteLine("Test complete.");
            }
        }

        private static int _invocationCount;






Crashes:

I experimented with various ways while trying to find a workaround for this issue, and found many different ways that Xcode reports when the app crashes. However, I cannot reproduce all of them anymore.
Besides Task.Run I also tried Task.Run with an async method that uses await Task.Yield(), leading to even more crashes.

***************************************************************************

Exception Type:  EXC_BAD_ACCESS (SIGABRT)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000020

***************************************************************************

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000

Thread 0 Crashed:
0   libsystem_kernel.dylib        	0x3a7791fc __pthread_kill + 8
1   libsystem_pthread.dylib       	0x3a7e0a4f pthread_kill + 55
2   libsystem_c.dylib             	0x3a72a029 abort + 73
3   NativeExceptionBug            	0x0013cbc9 0x21000 + 1162185
4   NativeExceptionBug            	0x00141873 0x21000 + 1181811
5   libsystem_platform.dylib      	0x3a7db721 _sigtramp + 41
6   libsystem_pthread.dylib       	0x3a7e0a4f pthread_kill + 55
7   libsystem_c.dylib             	0x3a72a029 abort + 73
8   NativeExceptionBug            	0x001c99a4 0x21000 + 1739172

***************************************************************************

Exception Type:  EXC_BAD_ACCESS (SIGABRT)
Exception Subtype: KERN_PROTECTION_FAILURE at 0x0000000002f66108
Triggered by Thread:  0

Thread 0 Crashed:
0   libsystem_kernel.dylib        	0x3a7791fc __pthread_kill + 8
1   libsystem_pthread.dylib       	0x3a7e0a4f pthread_kill + 55
2   libsystem_c.dylib             	0x3a72a029 abort + 73
3   NativeExceptionBug            	0x00127bc9 mono_handle_native_sigsegv (mini-exceptions.c:2335)
4   NativeExceptionBug            	0x00130c75 mono_sigsegv_signal_handler (mini.c:6744)
5   libsystem_platform.dylib      	0x3a7db721 _sigtramp + 41

***************************************************************************

Thread 0 Crashed:
0   NativeExceptionBug   
0x002b0cc9 mono_class_from_name (class.c:7459)
1   NativeExceptionBug   
0x002c70e1 mono_exception_from_name_domain (exception.c:63)
2   NativeExceptionBug   
0x002c73fd mono_get_exception_null_reference (exception.c:40)
3   NativeExceptionBug   
0x00297377 mono_handle_exception_internal (mini-exceptions.c:1519)
4   NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
5   NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
6   ???                           
0xffffffe0 0 + 4294967264
7   NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
8   NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
9   NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
10  NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
11  ???                           
0xffffffe0 0 + 4294967264
12  NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
13  NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
14  NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
15  NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
16  ???                           
0xffffffe0 0 + 4294967264
17  NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
18  NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
19  NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
20  NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
21  ???                           
0xffffffe0 0 + 4294967264
22  NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
23  NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
24  NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
25  NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
26  ???                           
0xffffffe0 0 + 4294967264
27  NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
28  NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
29  NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
30  NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
31  ???                           
0xffffffe0 0 + 4294967264

….

501 ???                           
0xffffffe0 0 + 4294967264
502 NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
503 NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
504 NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
505 NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)
506 ???                           
0xffffffe0 0 + 4294967264
507 NativeExceptionBug   
0x002965c5 mono_find_jit_info_ext (mini-exceptions.c:368)
508 NativeExceptionBug   
0x00297765 mono_handle_exception_internal (mini-exceptions.c:1357)
509 NativeExceptionBug   
0x0029731b mono_handle_exception (mini-exceptions.c:1915)
510 NativeExceptionBug   
0x00291b79 handle_signal_exception (exceptions-arm.c:532)

***************************************************************************




Version information:

=== Xamarin Studio ===

Version 4.2.3 (build 60)
Installation UUID: 938751df-f7a5-457d-b87b-379763e0ada1
Runtime:
	Mono 3.2.6 ((no/9b58377)
	GTK+ 2.24.23 theme: Raleigh
	GTK# (2.12.0.0)
	Package version: 302060000

=== Apple Developer Tools ===

Xcode 5.1 (5084)
Build 5B130a

=== Xamarin.iOS ===

Version: 7.2.0.2 (Business Edition)
Hash: 58c3efa
Branch: 
Build date: 2014-10-03 18:02:26-0400

=== Xamarin.Android ===

Version: 4.12.1 (Business Edition)
Android SDK: /Users/etankissling/Library/Developer/Xamarin/android-sdk-mac_x86
	Supported Android versions:
		4.3 (API level 18)
Java SDK: /usr
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)

=== Xamarin.Mac ===

Xamarin.Mac: Not Installed

=== Build Information ===

Release ID: 402030060
Git revision: 30c4afc300c2a39ec5300851357ce02e49dd217e
Build date: 2014-03-05 22:09:33+0000
Xamarin addins: f8a9589b57c2bfab2ccd73c880e7ad81e3ecf044

=== Operating System ===

Mac OS X 10.9.2
Darwin Etans-MacBook-Pro.local 13.1.0 Darwin Kernel Version 13.1.0
    Thu Jan 16 19:40:37 PST 2014
    root:xnu-2422.90.20~2/RELEASE_X86_64 x86_64
Comment 1 Zoltan Varga 2014-03-17 17:15:57 UTC
-> ios.

These are not native exceptions, they are crashes in native code, and they cannot generally be recovered from, the os/xcode will create a crash report.
Comment 2 kissling 2014-03-17 17:20:43 UTC
Why can they be caught sometimes, and sometimes not? How to catch exceptions throw via @throw?
Comment 3 Rolf Bjarne Kvinge [MSFT] 2014-03-18 08:52:37 UTC
First note that Apple recommends against using Objective-C exceptions except for exceptional circumstances [1]

Xamarin.iOS installs an unhandled (Objetive-C) exception handler, which will convert any Objective-C exceptions to managed exceptions.

The problem is that on the main thread, iOS will have an exception handler in their main loop, which aborts the app if hit. This means that the unhandled exception handler installed by Xamarin.iOS usually only works on any thread but the main thread.

Additionally we've added exception handlers around a few iOS API we've found where Apple has violated their own recommendations and use exceptions for normal execution. This is handled on an API-per-API basis, since adding exception handling to every iOS API would bloat the app both in size and speed (in general we don't add Objective-C exception handlers when you can add checks in managed code to avoid the exception in the first place - for instance NSArray's objectAtIndex: does not have an exception handler because you can check if the index is valid yourself).

Do you know of any specific API you want to call that may throw Objective-C exceptions, or are you trying to handle all possible error scenarios?

[1] https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html#//apple_ref/doc/uid/10000012i
Comment 4 kissling 2014-03-18 11:57:41 UTC
We are trying to log all possible unhandled exceptions before stopping the app.

I know that use of ObjC exceptions for runtime errors is discouraged by Apple, and that ARC does not properly clean up state after an exception is thrown except if a flag is present. 
However, if an exception is hit, we still would like to have the log, so we at least know why the app crashed.

The only issue I have in this bug report is the following:

In Xcode, I can write

    @try {
        [NSArray array][0];
        NSLog(@"test failed");
    }
    @catch (NSException *exception) {
        NSLog(@"test successful");
    }
    @finally {
        NSLog(@"test completed");
    }

and this works fine.

In Xamarin, I can write 

            try
            {
                new NSArray().ValueAt(0);
                Console.WriteLine("test failed");
            }
            catch (MonoTouchException)
            {
                Console.WriteLine("test successful");
            }
            finally
            {
                Console.WriteLine("test completed");
            }

and this does not work.

Or at least does not always work.

If I run it on the threadpool exactly once, I can run it on the main thread afterwards exactly once. 
If I run it on the threadpool multiple times, the app crashes. 
If I run it on the main thread twice after running it on the threadpool once, the app crashes.

And the one crash report I attached to the original message where 511 stack frames of the mini-exceptions.c exception handler got pushed until mono_class_from_name failed doesn't really seem to be correct, even if an exception handler is not plugged in on the main thread.
Comment 5 Rolf Bjarne Kvinge [MSFT] 2014-04-02 07:26:08 UTC
I'm not sure we can fix this without prohibitive side-effects (significant slowdown of the app for instance).

Another idea might be to use crash reporting tools (Crashlytics, Crittercism, TestFlight, etc), which should provide you with a bit more information at least.
Comment 7 Rolf Bjarne Kvinge [MSFT] 2017-02-03 12:59:50 UTC
We've implemented support for optionally catching Objective-C exceptions in managed code the current stable.
Comment 8 alex 2018-01-05 00:40:46 UTC
@Rolf would you be kind to elaborate? How one can use that now?
Comment 9 Rolf Bjarne Kvinge [MSFT] 2018-01-09 08:23:43 UTC
@Alex, see https://developer.xamarin.com/guides/ios/advanced_topics/exception_marshaling/