[macios-devel] [android-devel] Signal-chaining & crash reporters

Rolf Kvinge Rolf.Kvinge at microsoft.com
Fri Sep 16 17:23:57 UTC 2016


Hi,

> On 16/09/16 18:43, "Jonathan Pryor" <jonpryor at vt.edu> wrote:
> 
> Kumpera: Please see Option #5, at end.
> 
> On Sep 16, 2016, at 10:59 AM, Sebastien Pouliot via android-devel <android-devel at lists.dot.net> wrote:
> > The current sequence of events is this:
> >  
> > a) The app launches, and Xamarin.iOS initializes the Mono runtime.
> 
> Which raises Plausible Solution #0: structure things such that the 3rd party lib sets the default signal handler *before* mono is initialized.
> 
> This could be done by e.g. using a C++ global object in an archive linked into the main binary: the constructor will be executed before `main()` executes, so by the time mono has initialized it will see the 3rd party SIGSEGV handler, not the system handler.
> 
> There are problems with this:
> 
> 1. Before `main()`, execution order of global constructors is undefined. This solution is “scalable”, in that any number of libraries can participate, but the order between those libraries can’t be controlled.

Most native crash reporting libraries I've seen on iOS asks the app developer to initialize the library in the [UIApplicationDelegate application:didFinishLaunchingWithOptions:] method, which is late in the app startup sequence. At this point Xamarin apps are running managed code (in fact the managed equivalent: UIApplicationDelegate.FinishedLaunching), so requiring 3rd party libraries to initialize earlier when used with Xamarin.iOS is not a general/good solution.

> 
> 2. It requires that the 3rd party library be create-able in this fashion. For example, if it uses a license key which is only available in managed code, this can’t work.

Typically, on iOS there's a native library (where the actual crash detection / data collection code resides), and managed bindings for Xamarin.iOS. The app developer initializes the library using managed code, which is another reason this won't work.

> 
> 3. It only works for *app* projects. This can thus work for Xamarin.iOS, but it *can’t* work for Xamarin.Android, which uses shared libraries.
> 
> However, we can apply similar Judo to make things work in Xamarin.Android. Mono is initialized on Android through a ContentProvider with a high //provider/@android:initOrder value; the `initOrder` value controls the order of loading ContentProviders. You could thus set things up so that the 3rd party lib uses a Java ContentProvider with a *higher* `initOrder` value, thus ensuring that the 3rd party lib is initialized *before* Xamarin.Android. Mono will thus see the 3rd party’s SIGSEGV handler, not the system one.
> 
> The immediate problem here is that Xamarin.Android sets the initOrder value to int.MaxValue, but we can certainly lower the value we use to allow code to execute before the Xamarin.Android bootstrap code…
> 
> Pro: works/can be made to work *now*, with minimal/no changes to mono and Xamarin.
> 
> Con: Requires modifying process state before Mono starts executing, which isn’t always easy or straightforward (requires native 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:
> 
> This is assuming/requiring that the 3rd party code is managed code. When it comes to signal handlers, I don’t think that’s valid; any reasonably sane SIGSEGV-handling code *must* be native. Thus, we don’t necessarily need to support initializing it *during* execution of managed code.

Yes, we do.

There is always a native 3rd party library  (usable for all types of iOS apps, not just Xamarin ones), and then Xamarin-specific managed bindings on top of the native library.

So the app developer writes something like this (in managed code):

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        var HockeyAppManager = BITHockeyManager.SharedHockeyManager;
        HockeyAppManager.Configure("app-specific-value");
        HockeyAppManager.StartManager();
        return true;
    }

HockeyAppManager.StartManager is a managed binding for a native method (with some magic to make it not override Mono's signals): https://github.com/bitstadium/HockeySDK-Xamarin/blob/master/source/HockeySDK.iOSBindings/Additions.cs#L24-L54

> 
> That might still be a good idea… I’m not sure, myself.
> 
> > 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);
> >       }
> > }
> 
> I would prefer to *not* expose a managed binding of `struct sigaction`, in part to reduce portability issues. On Android, `sigset_t` is `unsigned long`, so on 64-bit platforms this will be a 64-bit value, not a 32-bit value, so this wouldn’t be compatible.
> 
> Granted, we could have “parallel” definitions for Android vs. iOS, but if we’re doing to define a new API, we should try to minimize such things. :-)

We could just expose an IntPtr:

> public static bool InstallSignalHandler (int signal, byte[] /* or IntPtr */ handler);

But not sure that makes it nicer.

> 
> > 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:
> 
> Such an attribute would need to be iOS/Mac-specific; there’s no Android equivalent. It would also require that `third_party_signal_handler` be a public symbol that the linker can find, in addition to the “use before initialized” issue that Rolf notes.
> 
> I think Solution #0 (global C++ objects) overlaps with this general idea, and is cleaner to boot.
> 
> > 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).
> 
> Another issue: the signals that are specified are signals that Mono needs to handle *first*. I’m not sure that this list of signals is/should be set in stone; what if Mono needs to be first for processing SIGILL in the future? Then this code will be out-of-date, possibly resulting on obscure errors.

If Mono needs to process new signals, then every single crash reporting library for Xamarin.iOS has a problem.

Also I don't see any solution that won't require updates to the crash reporting library anyway.
 
> 
> This doesn’t feel “safe” to me
> 
> >  4) A mix of all of the above:
> ..
> > Any other ideas? Suggestions for better naming for the API?
> 
> Option 5: a “new" Mono function.
> 
> The fundamental problem is that Mono *must* be the first signal handler for e.g. SIGSEGV, but ~anybody else can replace the SIGSEGV handler at any time.
> 
> What if the Mono team made one of the following function public:
> 
> 	// in: https://github.com/mono/mono/blob/master/mono/mini/mini-posix.c
> 	void mono_runtime_install_handlers (void);
> 	void mono_runtime_posix_install_handlers (void);
> 
> My quick reading of `mono_runtime_posix_install_handlers()` (line ~470) suggests that it registers those signals. It’s currently “internal”, i.e. you can’t `dlsym()` that function from `libmonosgen-2.0.dylib`, but if it were public, you could then register all your third party library code, then call `mono_runtime_install_handlers()`, and the app would be fine.

So mono would install its signal handlers twice?

i.e. something like this:

a) Mono initializes, calls mono_runtime_install_handlers.
b) App starts up, installs the crash reporting signal handlers.
c) App calls mono_runtime_install_handlers, which will make mono chain to the crash reporting signal handlers.

Which will probably confuse mono if the crash reporting signal handlers chain to the first mono signal handlers (which I believe PLCrashReporting does). It will probably chain again to the crash reporting signal handler, ending up with either an infinite loop or stack overflow.

I think one major point is that I don't think we can/should impose restrictions on when the crash reporting signal handlers are installed. There are a number of 3rd party libraries that provide crash reporting services, and they already work well with Xcode projects, and we need to figure out how we can make those libraries work well with us, without any changes.

Rolf




More information about the macios-devel mailing list