Hello,
I'm trying to simulate miscellaneous crashes to test my handlers. Things works as expected with NSException and C++ exceptions, however I cannot find a way to trig a C signal.
I tried with this code:
NSArray *runningApplications = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.myCompany.myApp"];
NSRunningApplication *app = runningApplications[0];
UInt32 pid = [app processIdentifier];
kill(pid, SIGABRT);
It is caught by my handler, but it doesn't crash the app (although it's detached from the debugger), I can even continue using the app normally.
I'm wondering if this could be related to something wrong in my handler (especially on how it ends):
signal(sig, SIG_IGN);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(source, ^{
// I write some logs on disk here, then uninstall the handlers associated with this or that signal:
for(int i=0; i<SignalSourceCount; i++) {
if (_signalSources[i]) {
dispatch_source_cancel(_signalSources[i]);
_signalSources[i] = NULL;
}
}
});
dispatch_resume(source);
I've seen some examples finishing rather with exit() or abort(). Abort crashes the app as expected, however the Crash Report produced by Apple then focuses on the handler instead of the code triggering the signal...
Any help appreciated, thanks
This context is not captured within the handler but before, during the normal usage of the app.
That’s still quite tricky. You have to protect this data structure from concurrent access, because the signal handler might fire while you’re updating it. And you can’t use a mutex for that, because the signal handler might fire regardless of whether the updating thread is holding that mutex.
To make this work you need an atomic. Consider this general outline:
-
Maintain the data structure in the normal way, if necessary using a mutex.
-
Then flatten it into a pointer-sized snapshot for the benefit of your signal handler.
-
Atomically replace the old snapshot with the new.
-
In the signal handler, work from the current snapshot. Remember that this read needs to be atomic.
But even this isn’t as easy as it sounds. Consider this sequence:
-
The signal handler fires on thread A.
-
It reads the snapshot and starts working on it.
-
Thread B, which continues to run even though you’re in a signal handler, replaces the snapshot with a new one.
-
And then deallocates the old one.
At this point the signal handler is working with deallocated memory.
There are ways around this but none of them are straightforward:
-
You could have the signal handler suspend all the other threads before it starts working from the snapshot. But that introduces its own complexities, for example, how do you avoid deadlock if two threads crash at the same time.
-
You could create a more elaborate data structure. The key term here is lock free. If you search the ’net for that, you’ll find lots of info about it. However, you’re in a particularly bad place because of the signal handler constraints.
Finally … I should implement a second handler using
signal
orsigaction
?
If you’re doing a crash reporter, ignore Dispatch sources and use only sigaction
.
Finally, this is on the Mac right? If so, you have a much better option: Do this in a separate process. So, spawn a child process and have it monitor the app process. When the app process dies, the child process writes out the app process’s state. This is in a separate process, and thus has none of the constraints associated with a signal handler.
The traditional challenge with such an approach is getting state out of the crashed process. That involves Mach exception handlers. Given the choice between signal handlers, Mach exception handlers, and gnawing my own leg off, I’m not 100% sure which way I’d go (-:
However, in this case that’s not needed because you only want to capture app state. Thus, you could use the snapshot approach I’ve described above but put the result into memory that you share between the app and this child process. The mechanics of this are still tricky, but it’s likely to be more reliable than anything you do in process. Moreover, there’s no way it could disrupt the Apple crash reporter.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"