[macios-devel] Signal-chaining & crash reporters

Sebastien Pouliot sepoulio at microsoft.com
Fri Sep 16 14:59:42 UTC 2016


c.c. the android email list so we can have more feedback and a shared solution

From: macios-devel <macios-devel-bounces at lists.dot.net> on behalf of Rolf Kvinge via macios-devel <macios-devel at lists.dot.net>
Reply-To: Rolf Kvinge <Rolf.Kvinge at microsoft.com>
Date: Friday, September 16, 2016 at 2:13 AM
To: "macios-devel at lists.dot.net" <macios-devel at lists.dot.net>
Subject: [macios-devel] Signal-chaining & crash reporters

Hi!

Recently we've realized that third-party libraries that do crash reporting (such as Insights or HockeyApp) are not notified when crashes occur (SIGSEGV).

These third-party libraries have typically adopted code similar to this: http://stackoverflow.com/a/14499336/183422<https://na01.safelinks.protection.outlook.com/?url=http%3a%2f%2fstackoverflow.com%2fa%2f14499336%2f183422&data=02%7c01%7csepoulio%40microsoft.com%7c3ce39e771237412153d308d3ddf8a605%7c72f988bf86f141af91ab2d7cd011db47%7c1%7c0%7c636096032415594012&sdata=%2b%2fxmh%2bB5fgOx8PMXwZqnQHoa2syScTzbJjD0Mn8T%2bUw%3d> to cope with the fact that the Mono runtime must handle SIGSEGV and SIGBUS before anybody else (SIGSEGVs occur for NullReferenceExceptions, and the GC uses SIGBUS frequently for its own purposes).

The current sequence of events is this:

a) The app launches, and Xamarin.iOS initializes the Mono runtime. At that time the Mono runtime checks if there are any existing signal handlers, and store those handlers (so that they can be invoked if the Mono runtime determines a SIGSEGV did not occur in managed code).
b) Managed code starts running, and the actual app initializes any third-party libraries. At this point third-party libraries typically do something like this:

    * Fetches current signal handlers (which would be Mono's signal handlers) and stores them.
   * Initializes third-party crash-reporting libraries, which installs signal handlers.
    * Restores the stored signal handlers.

    Sample HockeyApp code: https://github.com/bitstadium/HockeySDK-Xamarin/blob/master/source/HockeySDK.iOSBindings/Additions.cs - L30-L52<https://na01.safelinks.protection.outlook.com/?url=https%3a%2f%2fgithub.com%2fbitstadium%2fHockeySDK-Xamarin%2fblob%2fmaster%2fsource%2fHockeySDK.iOSBindings%2fAdditions.cs%23L30-L52&data=02%7c01%7csepoulio%40microsoft.com%7c3ce39e771237412153d308d3ddf8a605%7c72f988bf86f141af91ab2d7cd011db47%7c1%7c0%7c636096032415594012&sdata=UUYFTCO4lC21KMKVcgpOqVsPMHFJCfaGPbKu0q3hU9g%3d>

c) Something goes wrong (in native code), and the app receives a SIGSEGV.

    * Mono's signal handler is invoked and determines it's not a NullReferenceException.
    * Mono calls the signal handler it stored during initialization. As you can see this is not the third-party library's signal handler, it's the OS' default signal handler, which causes the process to exit (without notifying the third-party crash-reporting library) and the OS generates a crash report (which the app developer will not see, because he believes the third-party crash-reporting library reports those).

So how do we fix this?

A couple of ideas:

1) Add an API to select/set the signal handler Mono chains to:

namespace ObjCRuntime {
      public class Runtime {
            public struct SigAction
            {
                  public IntPtr Handler; //
                  public uint Mask; // sa_mask => sigset_t => __darwin_sigset_t => uint32_t
                  public int Flags; // sa_flags => int
            }

            public static bool InstallSignalHandler (int signal, SigAction handler, out SigAction previous_handler);
      }
}

    The idea being to do something like this (modified code from HockeyApp's link from above):

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

      // Enable crash reporting libraries
      DoStartManager();

      +     Runtime.SigAction handler, previous_handler;
      +     IntPtr hockey_signal = Marshal.AllocHGlobal (512);
      +     foreach (var signal in new Signal [] { Signal.SIGBUS, Signal.SIGSEGV }) {
      +           sigaction (signal, IntPtr.Zero, hockey_signal);
      +           handler = (Runtime.SigAction) Marshal.PtrToStructure (hockey_signal, typeof (Runtime.SigAction));
      +           bool rv = Runtime.InstallSignalHandler ((int) signal, handler, out previous_handler);
      +     }
      +     Marshal.FreeHGlobal (hockey_signal);

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

    IMHO this is ugly, and also it can't be implemented in current third-party bindings without losing support for older versions of Xamarin.iOS.

    OTOH it does not put any restrictions on the signal handler to chain to (more flexibility).

2) Add an attribute that gives the name of the signal handler, and then Xamarin.iOS generates the required code to make sure these signal handlers are called for signals Mono doesn't handle:

      [assembly: SignalHandler (Signal.SIGSEGV, "third_party_signal_handler", EnableAtStartup: false)]
      [assembly: SignalHandler (Signal.SIGBUS, "third_party_signal_handler", EnableAtStartup: false)]

    The problem here is that the signal handler might be called before the third-party library is initialized (Xamarin.iOS would enable this signal handler before mono is initialized, which means that the app has not had time to initialize the third-party library).

    This could be solved adding a simple function to enable the attribute's signal handler:

                ObjCRuntime.Runtime.EnableCustomSignalHandlers ();

    The big advantage here is that it can catch crashes during app initialization (including Mono's initialization).

3) A mix of both of the above:

      ObjCRuntime.Runtime.EnableCustomSignalHandler (Signals.SIGSEGV, "third_party_signal_handler");

   The problem here is that we'd have to use dlsym to lookup the function pointer for third_party_signal_handler, which can be problematic sometimes (#1 just gets the function pointer from the signal handler the native third-party library installed, and for #2 generate native code that references the function instead).

4) A mix of all of the above:

      // A function that fetches the current signal handler and makes mono chain to this signal handler
      ObjCRuntime.Runtime.EnableCurrentSignalHandler (Signals.SIGSEGV);

    Which would be used like this:

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

      // Enable crash reporting libraries
      DoStartManager();

      +     ObjCRuntime.Runtime.EnableCurrentSignalHandler (Signals.SIGSEGV);
      +     ObjCRuntime.Runtime.EnableCurrentSignalHandler (Signals.SIGBUS);

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

Any other ideas? Suggestions for better naming for the API?

BTW this is multiple-choice: we can implement all of these, they're not mutually exclusive.

I think #4 looks like the best option for now, so I'll start implementing that one unless someone says otherwise.

Rolf
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.dot.net/pipermail/macios-devel/attachments/20160916/595a4d01/attachment-0001.html>


More information about the macios-devel mailing list