Showing the Crash Dialog for Backgrounded Cocoa Apps

While developing Shortcat, I ran into an extremely annoying problem with LSUIElement apps: They crash silently, giving the user no indication that it has crashed. This is especially bad when you don’t have any visual indicator that it’s running.

This post on StackOverflow says that you can use CrashReporterPrefs.app to enable developer mode so it would show the dialog when a backgrounded app crashes. However, this is still a bad experience for end users.

My first attempt at fixing this was to install my own signal handlers so it would bring the application into the foreground, remove the handler, then reraise the signal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void BringAppToForeground()
{
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
}

void SignalHandler(int signal) {
    NSLog(@"Signal caught: %d", signal);
    BringAppToForeground();

    // Restore default handler
    struct sigaction action;
    action.sa_handler = SIG_DFL;
    action.sa_flags = 0;
    sigemptyset(&action.sa_mask);
    sigaction(signal, &action, NULL);

    raise(signal);
}

void InstallSignalHandlers()
{
    struct sigaction action;
    action.sa_handler = &SignalHandler;
    action.sa_flags = 0;
    sigemptyset(&action.sa_mask);

    sigaction(SIGABRT, &action, NULL);
    sigaction(SIGILL, &action, NULL);
    sigaction(SIGSEGV, &action, NULL);
    sigaction(SIGFPE, &action, NULL);
    sigaction(SIGBUS, &action, NULL);
    sigaction(SIGTRAP, &action, NULL);
}

// Call InstallSignalHandlers while initialising the app

This worked well. When the app crashed, the dock icon showed up for a second, then the crash dialogue showed up.

However, I noticed that the HockeyApp crash reporting was no longer working, probably due to PLCrashReporter also using signal handlers for its own crash handling and I was overriding them.

This is what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void BringAppToForeground()
{
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
}

static int fatal_signals[] = {
    SIGABRT,
    SIGBUS,
    SIGFPE,
    SIGILL,
    SIGSEGV,
    SIGTRAP
};

static int n_fatal_signals = (sizeof(fatal_signals) / sizeof(fatal_signals[0]));
static struct sigaction original_handlers[6]; // Must set this to the same number of fatal_signals

void SignalHandler(int signal) {
    BringAppToForeground();

    // Restore PLCrashreporter's handlers
    for (int i = 0; i<n_fatal_signals; i++) {
        sigaction(fatal_signals[i], &original_handlers[i], NULL);
    }

    raise(signal);
}

void InstallSignalHandlers()
{
    struct sigaction action;
    action.sa_handler = &SignalHandler;
    action.sa_flags = 0;
    sigemptyset(&action.sa_mask);

    struct sigaction default_action;
    default_action.sa_handler = SIG_DFL;
    default_action.sa_flags = 0;
    sigemptyset(&default_action.sa_mask);


    // Store the original handlers
    struct sigaction original_handler;
    for (int i = 0; i<n_fatal_signals; i++) {
        if (sigaction(fatal_signals[i], NULL, &original_handler) == 0) {
            original_handlers[i] = original_handler;
        }
        else {
            original_handlers[i] = default_action;
        }
        sigaction(fatal_signals[i], &action, NULL);
    }
}

Ideally, it would be better if PLCrashReporter supported some sort of callback so you can perform your own actions before it crashes, but I’d say a lot of people would try to call Objective-C code within it, making the crash handling not as reliable.

Please let me know if there’s ways to improve on this!

Comments