Подтвердить что ты не робот

Как предотвратить аварийные сообщения от iOS от аварийных приложений MonoTouch?

В iOS существует множество библиотек отчетов об ошибках iOS, включая TestFlight и HockeyApp. Если вы не хотите зависеть от служб, вы все равно можете использовать библиотеки, такие как PLCrashReporter. Связывание этих библиотек довольно тривиально, потому что их открытый API обычно состоит из нескольких классов с несколькими методами инициализации.

Однако при попытке использовать TestFlight, а затем и HockeyApp в нашем приложении, наше приложение стало случайно разбиваться. Оказывается, это известная проблема сообщила несколько раз, но Xamarin не предупреждает об этом, он относительно неясен, и мы нашли это трудным путем.

Мы узнали, что все репортеры Crash iOS не позволяют Mono перехватывать нулевые ссылочные исключения:

try {
    object o = null;
    o.GetHashCode ();
} catch {
    // Catch block isn't called with crash reporting enabled.
    // Instead, the app will crash.
}

Почему это происходит? Цитата Rolf, разработчик Xamarin,

Исключение с нулевой ссылкой фактически является сигналом SIGSEGV. Обычно mono runtime обрабатывает это и переводит его в исключение с нулевой ссылкой, позволяя продолжить выполнение. Проблема в том, что сигналы SIGSEGV являются очень плохо в приложениях ObjC (и когда это происходит за пределами управляемого кода), поэтому любое решение для отчетов о сбоях сообщит об этом как о сбое (и убьет приложение) - это происходит до того, как MonoTouch получит возможность обрабатывать SIGSEGV, так что есть ничего MonoTouch не может сделать с этим.

Я уверен, что многие используют TestFlight в приложениях MonoTouch, не зная, что это приводит к сбоям.
Разве это не иронично?

Как вы создаете библиотеки отчетов о сбоях, не разбивайте приложения MonoTouch?

4b9b3361

Ответ 1

Поместите это в 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 ();
}

Вызов EnableCrashReporting () в начале метода FinishedLaunching.
Оберните этот вызов в директиве #if !DEBUG, если хотите.


Как это работает?

Я последовал за предложением Рольфа:

Одно из возможных решений - позволить моно обрабатывать все сигналы SIGSEGV (технически говоря, сообщение об ошибке сбоя должно либо не обрабатываться сигнал SIGSEGV, или он должен быть привязан к моно-обработчику и не выполнять любая обработка сама по себе). Если моно определяет, что сигнал SIGSEGV не из управляемого кода (т.е. что-то очень плохое произошло), это будет поднимите сигнал SIGABORT (который должен сообщить об ошибке обрабатывать и обрабатывать как авария). Как вы можете понять, это что-то это должно быть сделано в библиотеке отчетов о сбоях.

И Ландон Фуллер Цель C:

#import <signal.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    /* Save Mono 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 signal handlers */
    sigaction(SIGBUS, &sigbus_action, NULL);
    sigaction(SIGSEGV, &sigsegv_action, NULL);

    return YES;
}

Я использовал исходный код Banshee в качестве отправной точки для вызова sigaction из MonoTouch.

Надеюсь, что это поможет!

Ответ 2

Начиная с 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 ();
}