There are plenty iOS crash reporting libraries in iOS, including TestFlight and HockeyApp. If you don't want to depend on services, you can still use libraries like PLCrashReporter. Binding these libraries is fairly trivial because their public API usually consists of a couple of classes with several initialization methods.
However, when trying to use TestFlight, and later HockeyApp in our application, our app began to randomly crash. Turns out, this is a known issue reported several times, but Xamarin doesn't warn about it, it is relatively obscure and we found it the hard way.
We have learned that all iOS crash reporters prevent Mono from catching null reference exceptions:
try { object o = null; o.GetHashCode (); } catch { // Catch block isn't called with crash reporting enabled. // Instead, the app will crash. }
Why does this happen? Quoting Rolf, a Xamarin developer,
A null reference exception is actually a SIGSEGV signal at first. Usually the mono runtime handles this and translates it into a nullreference exception, allowing the execution to continue. The problem is that SIGSEGV signals are a very bad thing in ObjC apps (and when it occurs outside of managed code), so any crash reporting solution will report it as a crash (and kill the app) - this happens before MonoTouch gets a chance to handle the SIGSEGV, so there is nothing MonoTouch can do about this.
I'm sure many use TestFlight in MonoTouch apps without knowing it causes crashes.
Isn't it ironic?
How do you make crash reporting libraries not crash MonoTouch apps?
You can also see the crash log on an iPhone/iPad by going to Settings -> Privacy -> Analytics -> Analytics Data. You can also see the crash log on a simulator if the app crashes during building through a simulator.
Firebase Crashlytics Crashlytics is a crash reporting tool that allows you to monitor your iOS applications.
You can access the crash log in macOS in two ways: Launch Finder, press Command + Shift + G, and type ~/Library/Logs/DiagnosticReports/ in the Go to Folder dialog box. Launch Console, and look for Diagnostic Reports located under ~/Library/Logs/.
Tombstone crash logs are written when a native crash in C/C++ code occurs in an Android application. The Android platform writes a trace of all the running threads at the time of the crash to /data/tombstones, along with additional information for debugging, such as information about memory and open files.
Put this in AppDelegate.cs
:
[DllImport ("libc")] private static extern int sigaction (Signal sig, IntPtr act, IntPtr oact); enum Signal { SIGBUS = 10, SIGSEGV = 11 } static void EnableCrashReporting () { IntPtr sigbus = Marshal.AllocHGlobal (512); IntPtr sigsegv = Marshal.AllocHGlobal (512); // Store Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus); sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv); // Enable crash reporting libraries EnableCrashReportingUnsafe (); // Restore Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero); sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero); Marshal.FreeHGlobal (sigbus); Marshal.FreeHGlobal (sigsegv); } static void EnableCrashReportingUnsafe () { // Run your crash reporting library initialization code here-- // this example uses HockeyApp but it should work well // with TestFlight or other libraries. // Verify in documentation that your library of choice // installs its sigaction hooks before leaving this method. var manager = BITHockeyManager.SharedHockeyManager; manager.Configure (HockeyAppId, null); manager.StartManager (); }
Call EnableCrashReporting ()
in the beginning of FinishedLaunching
method.
Wrap this call in #if !DEBUG
directive if you want.
I followed Rolf's suggestion:
One possible solution is to allow mono to handle all SIGSEGV signals (technically speaking the crash reporting lib should either not handle the SIGSEGV signal, or it should chain to mono's handler and not do any processing by itself). If mono determines that the SIGSEGV signal is not from managed code (i.e. something very bad happened), it will raise a SIGABORT signal (which the crash reporting lib should already handle and treat as a crash). As you can understand this is something that has to be done in the crash reporting library.
And Landon Fuller's Objective C implementation:
#import <signal.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* Save Mono's signal handler actions */ struct sigaction sigbus_action, sigsegv_action; sigaction(SIGBUS, NULL, &sigbus_action); sigaction(SIGSEGV, NULL, &sigsegv_action); // Enable the crash reporter here. Ie, [[PLCrashReporter sharedReporter] enableCrashReporterAndReturnError:], // or whatever is the correct initialization mechanism for the crash reporting service you're using /* Restore Mono's signal handlers */ sigaction(SIGBUS, &sigbus_action, NULL); sigaction(SIGSEGV, &sigsegv_action, NULL); return YES; }
I used Banshee source code as a reference point for how to call sigaction
from MonoTouch.
Hope it helps!
Starting with Xamarin.iOS 10.4 there's now a supported way of doing this:
static void EnableCrashReporting () { try { } finally { Mono.Runtime.RemoveSignalHandlers (); try { EnableCrashReportingUnsafe (); } finally { Mono.Runtime.InstallSignalHandlers (); } } } static void EnableCrashReportingUnsafe () { // Run your crash reporting library initialization code here-- // this example uses HockeyApp but it should work well // with TestFlight or other libraries. // Verify in documentation that your library of choice // installs its sigaction hooks before leaving this method. // Have in mind that at this point Mono will not handle // any NullReferenceExceptions, if there are any // NullReferenceExceptions on any thread (not just the current one), // then the app will crash. var manager = BITHockeyManager.SharedHockeyManager; manager.Configure (HockeyAppId, null); manager.StartManager (); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With