dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.56k stars 4.54k forks source link

[.NET 6/7] An alternative to Mono.Runtime.RemoveSignalHandlers/InstallSignalHandlers in .NET 6/7? #79706

Open akravch opened 1 year ago

akravch commented 1 year ago

TLDR

Is there any API in Android/iOS .NET 6/7 apps to replace Mono.Runtime.RemoveSignalHandlers/InstallSignalHandlers which are unavailable now?

Motivation

We have Xamarin.Android and Xamarin.iOS apps that can catch and report native crashes by installing their own signal handlers like this:

Mono.Runtime.RemoveSignalHandlers();

try
{
    NativeCrashListener.Initialize();
}
finally
{
    Mono.Runtime.InstallSignalHandlers();
}

Migrating from Xamarin to .NET 6 I noticed that the Mono.Runtime type is not accessible any more, even though the Mono runtime is still there for Android and iOS, so obviously, you can't remove and install the handlers like this.

Mono suggests the following alternative:

IntPtr sigbus = Marshal.AllocHGlobal (512);
IntPtr sigsegv = Marshal.AllocHGlobal (512);

// Store Mono's SIGSEGV and SIGBUS handlers
sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus);
sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv);

// Enable crash reporting libraries
EnableCrashReporting ();

// Restore Mono SIGSEGV and SIGBUS handlers
sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero);
sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero);

Marshal.FreeHGlobal (sigbus);
Marshal.FreeHGlobal (sigsegv);

We tried both the way Mono recommends and getting rid of the remove/install code at all on our Xamarin apps to see if it makes a difference. Turns out that it does, and the number of crashes that we get significantly differs in both cases. It is hard to say which way is more correct though, since you can only see the difference on big numbers, but in any case the behavior is not the same.

I would still want to apply something similar to the .NET 6 apps, and I wonder whether there is an alternative API I didn't find. I know that there is some API to work with POSIX signals now in .NET 6, so maybe I'm just missing something.

So is there a way to achieve something like that in .NET 6+ apps or any plans to add a new API to support this?

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.

Issue Details
# TLDR Is there any API in Android/iOS .NET 6/7 apps to replace Mono.Runtime.RemoveSignalHandlers/InstallSignalHandlers which are unavailable now? # Motivation We have Xamarin.Android and Xamarin.iOS apps that can catch and report native crashes by installing their own signal handlers like this: ```c# Mono.Runtime.RemoveSignalHandlers(); try { NativeCrashListener.Initialize(); } finally { Mono.Runtime.InstallSignalHandlers(); } ``` Migrating from Xamarin to .NET 6 I noticed that the `Mono.Runtime` type is not accessible any more, even though the Mono runtime is still there for Android and iOS, so obviously, you can't remove and install the handlers like this. Mono [suggests](https://www.mono-project.com/docs/advanced/signals/) the following alternative: ```c# IntPtr sigbus = Marshal.AllocHGlobal (512); IntPtr sigsegv = Marshal.AllocHGlobal (512); // Store Mono's SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus); sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv); // Enable crash reporting libraries EnableCrashReporting (); // Restore Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero); sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero); Marshal.FreeHGlobal (sigbus); Marshal.FreeHGlobal (sigsegv); ``` We tried both the way Mono recommends and getting rid of the remove/install code at all on our Xamarin apps to see if it makes a difference. Turns out that it does, and the number of crashes that we get significantly differs in both cases. It is hard to say which way is more correct though, since you can only see the difference on big numbers, but in any case the behavior is not the same. I would still want to apply something similar to the .NET 6 apps, and I wonder whether there is an alternative API I didn't find. I know that there is some API to work with POSIX signals now in .NET 6, so maybe I'm just missing something. So is there a way to achieve something like that in .NET 6+ apps or any plans to add a new API to support this?
Author: akravch
Assignees: -
Labels: `area-System.Runtime.InteropServices`
Milestone: -
lambdageek commented 1 year ago

cc @rolfbjarne @jonpryor @jonathanpeppers do we have any guidance for .NET 6/7 customers who want to use a third party crash reporting library on mobile?

We don't seem to support PosixSignalRegistration on mobile (it throws PNSE) but maybe we should? Although it wouldn't solve the issue with null reference exceptions.

The problem that Mono.Runtime.RemoveSignalHandlers/Mono.Runtime.InstallSignalHandlers were meant to solve is that normally we want the runtime to handle SIGBUS/SIGSEGV first (to handle null managed pointer derefs and convert them to NullReferenceExceptions). Mono's normal signal handler first checks the faulting page and if it is in the managed heap it will create the NRE otherwise it will chain to the signal handler that was registered at runtime startup (normally the system default handler, but in embedders it could be something else).

In order to install a custom crash handler, however, the user code would want to insert itself after mono's default handler. (ie: PosixSignalRegistration.Create (SIGSEGV, ...) would create a chain like this: UserHandler -> MonoHandler -> SystemDefault, but we would like this: MonoHandler -> UserHandler -> SystemDefault). I don't see a way to achieve that with PosixSignalRegistration.

ghost commented 1 year ago

Tagging subscribers to 'os-ios': @steveisok, @akoeplinger See info in area-owners.md if you want to be subscribed.

Issue Details
# TLDR Is there any API in Android/iOS .NET 6/7 apps to replace Mono.Runtime.RemoveSignalHandlers/InstallSignalHandlers which are unavailable now? # Motivation We have Xamarin.Android and Xamarin.iOS apps that can catch and report native crashes by installing their own signal handlers like this: ```c# Mono.Runtime.RemoveSignalHandlers(); try { NativeCrashListener.Initialize(); } finally { Mono.Runtime.InstallSignalHandlers(); } ``` Migrating from Xamarin to .NET 6 I noticed that the `Mono.Runtime` type is not accessible any more, even though the Mono runtime is still there for Android and iOS, so obviously, you can't remove and install the handlers like this. Mono [suggests](https://www.mono-project.com/docs/advanced/signals/) the following alternative: ```c# IntPtr sigbus = Marshal.AllocHGlobal (512); IntPtr sigsegv = Marshal.AllocHGlobal (512); // Store Mono's SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus); sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv); // Enable crash reporting libraries EnableCrashReporting (); // Restore Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero); sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero); Marshal.FreeHGlobal (sigbus); Marshal.FreeHGlobal (sigsegv); ``` We tried both the way Mono recommends and getting rid of the remove/install code at all on our Xamarin apps to see if it makes a difference. Turns out that it does, and the number of crashes that we get significantly differs in both cases. It is hard to say which way is more correct though, since you can only see the difference on big numbers, but in any case the behavior is not the same. I would still want to apply something similar to the .NET 6 apps, and I wonder whether there is an alternative API I didn't find. I know that there is some API to work with POSIX signals now in .NET 6, so maybe I'm just missing something. So is there a way to achieve something like that in .NET 6+ apps or any plans to add a new API to support this?
Author: akravch
Assignees: -
Labels: `os-android`, `os-ios`, `area-Interop-mono`
Milestone: -
jonathanpeppers commented 1 year ago

What would a coreclr console app do on Linux, for example? AppDomain.CurrentDomain.UnhandledException is for managed-land, is there an equivalent for native crashes?

jkotas commented 1 year ago

What would a coreclr console app do on Linux, for example?

Preload the native crash reporting library using LD_PRELOAD.

rolfbjarne commented 1 year ago

What would a coreclr console app do on Linux, for example?

Preload the native crash reporting library using LD_PRELOAD.

That's not an option on iOS :/

A library initializer in a static library might work:

void initialize_crash_reporting () __attribute__ ((constructor));
void initialize_crash_reporting ()
{
    // initialize...
}

which is a rather steep hill for app developers (since they'd likely be the ones who'd have to write and build the initializing static library to use the actual crash reporting library).

In order to install a custom crash handler, however, the user code would want to insert itself after mono's default handler. (ie: PosixSignalRegistration.Create (SIGSEGV, ...) would create a chain like this: UserHandler -> MonoHandler -> SystemDefault, but we would like this: MonoHandler -> UserHandler -> SystemDefault). I don't see a way to achieve that with PosixSignalRegistration.

Another point is that PosixSignalRegistration only seems to allow managed callbacks. Executing managed code in a signal that causes a crash is not really a good idea at all.

akravch commented 1 year ago

A library initializer in a static library might work

But can we actually use static libraries in .NET for Android? I believe that was not possible with Xamarin.Android unless it's wrapped into a dynamic library, and loading a dynamic library still happens after Mono signal handlers are installed.

akravch commented 1 year ago

Are there any updates or ideas on this topic? It is still very relevant for me and I wouldn't even mind an ugliest workaround if it existed.

karpikpl commented 1 year ago

Is there a plan to include this in .Net 7/8 ?

What are alternatives for handling process crashes in native code in .NET? If EnableCrashReporting(); is not an option, is there a way to monitor the .Net process in some other way, produce dumps and report them?

jkotas commented 1 year ago

This is open design discussion with milestone set to Future. There is no agreed upon plan for how to resolve this issue.

InstallSignalHandlers/RemoveSignalHandlers APIs are difficult to implement 100% reliably and they come with portability issues. I do not think they would be a good addition to the platform.

If the goal is to allow installation of the custom signal handlers before the runtime ones, writing a custom runtime host and registering the signals before the runtime starts is the way to go. This tutorial works for Windows/Linux/macOS apps that typically run on CoreCLR runtime. We do not have a tutorial like this for mobile apps (they use custom hosting) so that would be the piece to fill in.

If the goal is to get good actionable crash dumps, CoreCLR runtime comes with crash dump capabilities: https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash . The built-in crash dump collector is better than the generic crash dump collectors since it understands the structure of the .NET runtime and the piece that must be saved into the crashdump. We can keep evolving this solution to meet the needs.

If the goal is to allow interception of fatal process crashes, we can consider introducing an API for it. The API would allow registering unmanaged callback. The callback would have to be implemented with signal handler style restrictions in unmanaged code. This callback would be issued for any fatal crashes that are intercepted by the .NET runtime. In addition to fatal signals, this callback would be also issued for Environment.FailFast and other similar situations where the runtime terminates process without giving anybody chance to intercept it today.

akravch commented 10 months ago

Since custom hosting and dump collection are not options for Android/iOS applications, I would love to see the signal handling API implemented. I will wait for any updates on the topic. Thank you very much!

jkotas commented 10 months ago

I assume that you are interested in 3rd point (allow interception of fatal process crashes). The managed APIs for that can look like this:

This is what the APIs can look like:

// Returns a pointer to a function that can be used to check whether given instruction pointer is managed code.
// The function is safe to call in signal handlers. The 3rd party crash handlers can use this function to check
// whether the .NET runtime is going to handle signal at this instruction pointer.
public static delegate* unmanaged<IntPtr, int> GetIsManagedCode();

// .NET runtime is going to call `fatalErrorHandler` set by this method before its own
// fatal error handling (creating .NET runtime-specific crash dump, etc.). This can be only called once in given
// process. 
// TODO: What should be the signature of the `fatalErrorHandler`?
public static void SetFatalErrorHandler(IntPtr fatalErrorHandler);

Would that work for you?

rolfbjarne commented 10 months ago

The main scenario here is to use third-party crash reporters.

Then one important question comes up: do we want a solution that would require modifications to any third-party crash reporters that might want to support .NET apps, or do we want a solution that would work with any third-party crash reporters as-is?

The former Mono API (RemoveSignalHandlers/InstallSignalHandlers) is in the latter category, it just required a few lines of code by the app developer.

If we want to go with the former category, the API needs to be designed together with those third-party crash reporter vendors. It doesn't make much sense to design and implement an API that's hard/impossible for them to use.

jkotas commented 1 month ago

API proposal for customization of fatal error handling: https://github.com/dotnet/runtime/issues/101560