-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Mono & .NET 5 & The Debugger & Exceptions, oh my! #38465
Comments
Tagging subscribers to this area: @tommcdon |
Tagging subscribers to this area: @thaystg |
I'm curious what the debugger situation is like on macos/ios - I recall we convert managed exceptions into objective-C exceptions and back at Managed->Native->Managed transitions - and the unwinding in native frames is handled by objC. But I don't know what the debugger behavior is like. It would be good if Android and ios didn't diverge too much here... |
I'm planning to take a look at this next week, to understand how iOS and Android works to understand which would be the best approach to follow on wasm and on Android using .NET 5 mono. |
There is also another call related to exceptions: This allows the AppDomain.UnhandledException event to work, but |
Context: dotnet/runtime#38465 Both of these methods are missing in .NET 5+: * `System.Diagnostics.Debugger.Mono_UnhandledException` * `AppDomain.CurrentDomain.DoUnhandledException` The code path that runs during an unhandled exception relies on these methods. Currently, an unhandled exception will throw several additional exceptions we should fix: Unable to initialize UncaughtExceptionHandler. Nested exception caught: System.ArgumentNullException: Value cannot be null. (Parameter 'method') at System.Delegate.CreateDelegate(Type type, Object firstArgument, MethodInfo method, Boolean throwOnBindFailure, Boolean allowClosed) at System.Delegate.CreateDelegate(Type type, MethodInfo method, Boolean throwOnBindFailure) at System.Delegate.CreateDelegate(Type type, MethodInfo method) at Android.Runtime.JNIEnv.Initialize() at Android.Runtime.JNIEnv.PropagateUncaughtException(IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr) And: Exception thrown while raising AppDomain.UnhandledException event: System.NullReferenceException: Object reference not set to an instance of an object at Android.Runtime.JNIEnv.PropagateUncaughtException(IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr) Added the appropriate null checks in `JNIEnv.PropagateUncaughtException` to fix the additional exceptions.
Context: dotnet/runtime#38465 When a thread throws an exception which isn't caught, we (eventually) hit the "unhandled exception" codepath of `JNIEnv.PropagateUncaughtException()` (2aff4e7 & others), which relies on the following methods which are not present in .NET 5: * `System.Diagnostics.Debugger.Mono_UnhandledException()` * `AppDomain.DoUnhandledException()` Unfortunately, we didn't check that the `MethodInfo`s for these methods were `null` before invoking them, which means under .NET 5 we would throw an exception while attempting to deal with an already "in-flight" exception, i.e. we'd throw a *nested* exception! Unable to initialize UncaughtExceptionHandler. Nested exception caught: System.ArgumentNullException: Value cannot be null. (Parameter 'method') at System.Delegate.CreateDelegate(Type type, Object firstArgument, MethodInfo method, Boolean throwOnBindFailure, Boolean allowClosed) at System.Delegate.CreateDelegate(Type type, MethodInfo method, Boolean throwOnBindFailure) at System.Delegate.CreateDelegate(Type type, MethodInfo method) at Android.Runtime.JNIEnv.Initialize() at Android.Runtime.JNIEnv.PropagateUncaughtException(IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr) or: Exception thrown while raising AppDomain.UnhandledException event: System.NullReferenceException: Object reference not set to an instance of an object at Android.Runtime.JNIEnv.PropagateUncaughtException(IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr) Add appropriate `null` checks in `JNIEnv.PropagateUncaughtException()` to avoid generation of nested exceptions. TODO: Work with the dotnet/mono teams so that we can reintroduce the functionality of `AppDomain.DoUnhandledException()`/etc..
Repro here: dotnet/android#4927 (comment) |
Related: dotnet/android#4927 (comment) .NET 6 project: https://github.com/xamarin/xamarin-android/files/6779689/Scratch.JMJMException.zip Unhandled exception handling is broken on .NET 6 when running within VS 2022: instead of getting a first-chance exception, or any form of unhandled exception dialog, apps crash immediately if they have an unhandled exception. See this
Also (unrelatedly) odd is the line wrapping for the C# stack trace message, e.g.
|
Related: dotnet/android#4877
Related: dotnet/android#4864
Related: https://xamarinhq.slack.com/archives/C03CEGRUW/p1593198033319900
Related: https://files.slack.com/files-pri/T03C6CW9H-F0164ULFMEE/image.png
Xamarin.Android v10.x uses the mono/mono/2020-02 branch, and uses the
System.Diagnostics.Debugger.Mono_UnhandledException()
internal call when a debugger is attached, in order to simulate the first chance notification within the debugger.Debugger.Mono_UnhandledException()
does not exist in .NET 5 mono. Consequently, when an exception is thrown in a .NET 5 Xamarin.Android app, the debugger "first chance notification" behavior differs.As such, when an exception is thrown and not caught, there is no "first chance" notification, nor is there a "last chance" notification. Instead, execution continues, the entire call stack is unwound, and then the exception is visible within the debugger.
This might be a Glorious Opportunity to rework how exceptions work within Xamarin.Android apps when a debugger is attached. dotnet/android#4877 describes the current state of affairs, and it's "not good." In particular, current behavior is that when a managed exception is caught while the debugger is attached, when execution continues mono will unwind stack frames, without allowing execution to return to Java code.
This basically means that if you have a Managed > Java > Managed callstack transition, the process is effectively unusable if the rightmost frame throws an exception while the process is being debugged.
(Not using a debugger results in "sane" exception handling, but means you don't have a debugger…)
The text was updated successfully, but these errors were encountered: