I recently discovered CoffeeCatch, which I want to use to log the C/C native crashes on Android. I haven't managed, but still I am curious about how it works internally.
My understanding is that it basically catches and emitted signal (e.g. SIGSEGV) and allows the user to do something with it; in my case I would like save the stack trace and crash.
It is used like this:
COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));
Where call_dangerous_function() is a function that could crash and therefore emit a signal.
The macro is defined here:
#define COFFEE_TRY_JNI(ENV, CODE) \
do { \
COFFEE_TRY() { \
CODE; \
} COFFEE_CATCH() { \
coffeecatch_throw_exception(ENV); \
} COFFEE_END(); \
} while(0)
Which resolves to something like this:
do {
if (coffeecatch_inside() ||
(coffeecatch_setup() == 0
&& sigsetjmp(*coffeecatch_get_ctx(), 1) == 0)) { \
call_dangerous_function(env, object);
} else {
coffeecatch_throw_exception(ENV);
} coffeecatch_cleanup();
} while(0)
Here, my understanding is that sigsetjmp(*coffeecatch_get_ctx(), 1) == 0) somehow sets a pointer to the current "context", which I guess is something like the execution state of this thread at this moment.
So somehow, later, siglongjmp(t->ctx, code); will be called (after the environment has been somehow prepared for it) to jump back here.
What's not clear to me is if we then jump at the if statement (and evaluate the statement again), or somehow end up into the else statement for some reason.
My expectation is that siglongjmp now results in going into the else statement (where in my case a Java exception could be thrown). But:
- First, that's not what I see (my program emits the SIGSEGV signal right after
siglongjmpis called without ever reaching theelsestatement). - Second, I don't get where
siglongjmpshould actually jump. If it goes to theifstatement again and evaluates the condition again, it will evaluate totrueagain and that's a loop. How would it go toelse?
CodePudding user response:
siglongjmp jumps back to where sigsetjmp was called, and makes it look like sigsetjmp returned the value that siglongjmp was passed. So in this case, if siglongjmp is called with a non-zero value, then it will jump back to the sigsetjmp(*coffeecatch_get_ctx(), 1) == 0 condition, which will evaluate to false and thus the if will not be satisfied and the else block will execute.
It's very unlikely you can meaningfully recover from a SIGSEGV however. By the time a SIGSEGV happens your program has wandered so far off into undefined behavior that it is impossible to reason about its current state. There is a high chance that data has been corrupted and/or your call stack has been destroyed. The only meaningful action is to terminate the process.
Note: Using (sig)?setjmp/(sig)?longjmp in C is a very bad idea. They do not execute object destructors, and thus can easily leak memory and/or violate class invariants.
