views:

196

answers:

1

How can I efficiently catch and handle segmentation faults from C in an OSX Carbon application?

Background: I am making an OSX Carbon application. I must call a library function from a third party. Because of threading issues, the function can occasionally crash, usually because it's updating itself from one thread, and it's got some internally stale pointer or handle as I query it from another. The function is a black box to me. I want to be able to call the function but be able to "catch" if it has crashed and supply an alternative return. In Windows, I can use the simple Visual C and Intel C compiler's __try{} and __except.

/* Working Windows Example */
__try { x=DangerousFunction(y);}
__except(EXCEPTION_EXECUTE_HANDLER) {x=0.0;} /* whups, func crashed! */

I am trying to make the same kind of crash-catcher for OSX. I am using pure C on a very large application. I call the function millions of times per second, so efficiency is very important too. (Impressively, the Windows __try() overhead is immeasurably small!)

Here's what I have experimented with:

1) C++ exceptions. I am not sure if C++ exceptions catch the segfault crashes. And my app is currently C. I could try wrappers and #ifdefs to make it C++ but this is a lot of work for the app, and I don't think C++ exceptions will catch the crash.

2) signal + setjump + longjmp. I thought this would work... it's what it's designed for. But I set up my SEGV error handler [in fact I set it up for every signal!] and it's never called during the crash. I can manually test (and succeed) when calling raise(SEGV). But the crashes don't seem to actually call it. My thoughts are that CFM applications do NOT have access to the full BSD signals, only a subset, and that Mach applications are necessary for the Real Thing.

3) MPSetExceptionHandler. Not well documented. I attempted to set a handler. It compiled and ran, but did not catch the segfault.

+2  A: 

Are you sure you're not getting a SIGBUS rather then a SIGSEGV?

The below catches SIGBUS as caused by trying to write at memory location 0:

cristi:tmp diciu$ cat test.c

#include <signal.h>

static void sigac(int sig)
{
    printf("sig action here, signal is %d\n", sig);
    exit(1);
}

int main()
{
    (void)signal(SIGSEGV, sigac);
    (void)signal(SIGBUS, sigac);

    printf("Raising\n");
    strcpy(0, "aaksdjkajskd|");
}



cristi:tmp diciu$ ./a.out 
Raising
sig action here, signal is 10
diciu
Yes. I think the key is to catch EVERY exception of every type!
SPWorley