IOSには、 TestFlight や HockeyApp など、iOSのクラッシュレポートライブラリがたくさんあります。サービスに依存したくない場合でも、 PLCrashReporter のようなライブラリを使用できます。これらのライブラリのバインドは かなり些細な です。なぜなら、それらのパブリックAPIは通常、いくつかの初期化メソッドを持ついくつかのクラスで構成されているからです。
ただし、アプリケーションでTestFlightとその後のHockeyAppを使用しようとすると、アプリがランダムにクラッシュし始めました。結局のところ、これはis既知の問題 報告済みいくつか回 ですが、Xamarinはそれについて警告していません。は比較的あいまいで、難しい方法であることがわかりました。
すべてのiOSクラッシュレポーターがMonoがnull参照例外をキャッチするのを防ぐことを学びました:
try {
object o = null;
o.GetHashCode ();
} catch {
// Catch block isn't called with crash reporting enabled.
// Instead, the app will crash.
}
なぜこれが起こるのですか? Xamarin開発者のRolfを引用して、
ヌル参照例外は、実際には最初はSIGSEGV信号です。通常、monoランタイムはこれを処理し、nullreference例外に変換して、実行を続行できるようにします。問題は、SIGSEGVシグナルがObjCアプリでは非常に悪いことであるため(マネージコードの外部で発生した場合)、クラッシュレポートソリューションはクラッシュとして報告します(そしてアプリを強制終了します)-これはMonoTouchがチャンスを得る前に発生しますSIGSEGVを処理するため、MonoTouchがこれについてできることは何もありません。
多くの人が、クラッシュの原因になることを知らずに、MonoTouchアプリでTestFlightを使用していると確信しています。
皮肉ではないですか?
クラッシュレポートライブラリを作成するにはどうすればよいですかnot MonoTouchアプリをクラッシュさせますか?
これを_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 ();
}
_
FinishedLaunching
メソッドの先頭でEnableCrashReporting ()
を呼び出します。
必要に応じて、この呼び出しを_#if !DEBUG
_ディレクティブでラップします。
私はロルフの提案に従いました:
考えられる解決策の1つは、monoがすべてのSIGSEGVシグナルを処理できるようにすることです(技術的に言えば、クラッシュレポートライブラリはSIGSEGVシグナルを処理しないか、monoのハンドラーにチェーンして単独で処理を行わないようにする必要があります)。 monoがSIGSEGVシグナルがマネージコードからのものではないと判断した場合(つまり、非常に悪いことが起こった場合)、SIGABORTシグナルを生成します(クラッシュレポートライブラリはすでにクラッシュとして処理および処理する必要があります)。ご存知のように、これはクラッシュレポートライブラリで実行する必要があることです。
そして Landon Fuller のObjective C実装:
_#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;
}
_
MonoTouchからsigaction
を呼び出す方法の参照ポイントとして Bansheeソースコード を使用しました。
それが役に立てば幸い!
Xamarin.iOS 10.4以降、これを行うためのサポートされている方法があります。
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 ();
}