Skip to content

Commit

Permalink
[One .NET] Use new API for exception debugger notification (#6106)
Browse files Browse the repository at this point in the history
[One .NET] Use Mono embedding API for exception debugger notification (#6106)

Context: dotnet/runtime#56071
Context: #4877
Context: #4927 (comment)
Context: #4927 (comment)

Context: xamarin/monodroid@3e9de5a
Context: xamarin/monodroid@b0f8597
Context: xamarin/monodroid@12a012e

What should happen when an exception is thrown and a debugger is
attached?

This is in fact a loaded question: there's what Xamarin.Android
(Legacy) *has* done, vs. what .NET 6 Android for .NET does, vs.
what "should" happen.

What "should" happen is easiest:

 1. We should behave like a "normal" Desktop .NET app when a debugger
    is attached, AND

 2. We shouldn't corrupt JVM state.

Unfortunately, (1)+(2) is currently not possible, in part because
Java doesn't have an equivalent to Windows' [two attempt][0] debugger
notification infrastructure.
See #4877 for details.

What Legacy Xamarin.Android does is also detailed in
#4877, and relies on the
`Debugger.Mono_UnhandledException()` method in order to alert an
attached debugger that there is an exception to show to the user.

However, `Debugger.Mono_UnhandledException()` never made it to the
`dotnet/runtime` repo.  It never existed there.

Thus, what .NET 6 Android for .NET *currently* does is…*nothing*.
If an exception is thrown and a debugger is attached, the debugger
is *not* notified.  Eventually you'll get an unhandled exception,
long after it was originally thrown; see commit c1a2ee7.

PR dotnet/runtime#56071 added a new
`mono_debugger_agent_unhandled_exception()` Mono embedding API which
is equivalent to `Debugger.Mono_UnhandledException()` for use with
.NET 6 + MonoVM.

Update `src/Mono.Android` and `src/monodroid` so that
`mono_debugger_agent_unhandled_exception()` is used to alert the
debugger that an exception has been thrown at a JNI boundary.
This should allow .NET 6 + Android to have equivalent exception
handling semantics as legacy Xamarin.Android.

[0]: https://docs.microsoft.com/en-us/windows/win32/debug/debugger-exception-handling
  • Loading branch information
thaystg authored and jonpryor committed Jul 23, 2021
1 parent e9f70aa commit 7d48ba8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,12 @@ static void ManualJavaObjectDispose (Java.Lang.Object obj)
GC.SuppressFinalize (obj);
}

static Action<Exception> mono_unhandled_exception = null!;
#if NETCOREAPP
internal static Action<Exception> mono_unhandled_exception = monodroid_debugger_unhandled_exception;
#else // NETCOREAPP
internal static Action<Exception> mono_unhandled_exception = null!;
#endif // NETCOREAPP

#if !NETCOREAPP
static Action<AppDomain, UnhandledExceptionEventArgs> AppDomain_DoUnhandledException = null!;
#endif // ndef NETCOREAPP
Expand Down Expand Up @@ -695,6 +700,11 @@ public static string GetClassNameFromInstance (IntPtr jobject)
[MethodImplAttribute(MethodImplOptions.InternalCall)]
static extern unsafe IntPtr monodroid_typemap_managed_to_java (Type type, byte* mvid);

#if NETCOREAPP
[MethodImplAttribute(MethodImplOptions.InternalCall)]
static extern unsafe void monodroid_debugger_unhandled_exception (Exception e);
#endif // NETCOREAPP

internal static void LogTypemapTrace (StackTrace st)
{
string? trace = st.ToString ()?.Trim ();
Expand Down
3 changes: 3 additions & 0 deletions src/Mono.Android/Android.Runtime/JNINativeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ static void get_runtime_types ()
if (mono_unhandled_exception_method == null)
AndroidEnvironment.FailFast ("Cannot find System.Diagnostics.Debugger.Mono_UnhandledException");
#endif
#if NETCOREAPP
mono_unhandled_exception_method = JNIEnv.mono_unhandled_exception.Method;
#endif // NETCOREAPP
exception_handler_method = typeof (AndroidEnvironment).GetMethod (
"UnhandledException", BindingFlags.NonPublic | BindingFlags.Static);
if (exception_handler_method == null)
Expand Down
3 changes: 3 additions & 0 deletions src/monodroid/jni/monodroid-glue-internal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ namespace xamarin::android::internal
static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name);

static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid);
#if defined (NET6)
static void monodroid_debugger_unhandled_exception (MonoException *ex);
#endif

#if defined (DEBUG)
void set_debug_env_vars (void);
Expand Down
9 changes: 9 additions & 0 deletions src/monodroid/jni/monodroid-glue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,14 @@ MonodroidRuntime::lookup_bridge_info (MonoDomain *domain, MonoImage *image, cons
}
#endif // ndef NET6

#if defined (NET6)
void
MonodroidRuntime::monodroid_debugger_unhandled_exception (MonoException *ex)
{
mono_debugger_agent_unhandled_exception (ex);
}
#endif

void
MonodroidRuntime::init_android_runtime (
#if !defined (NET6)
Expand All @@ -1011,6 +1019,7 @@ MonodroidRuntime::init_android_runtime (
mono_add_internal_call ("Java.Interop.TypeManager::monodroid_typemap_java_to_managed", reinterpret_cast<const void*>(typemap_java_to_managed));
mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java", reinterpret_cast<const void*>(typemap_managed_to_java));
#if defined (NET6)
mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_debugger_unhandled_exception", reinterpret_cast<const void*> (monodroid_debugger_unhandled_exception));
mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_unhandled_exception", reinterpret_cast<const void*>(monodroid_unhandled_exception));
#endif // def NET6

Expand Down

0 comments on commit 7d48ba8

Please sign in to comment.