/* Internal signal pass-through. Allows to peek the "real" crash before * calling the Java handler. Remember than Java needs many of the signals * (for the JIT, for test-free NullPointerException handling, etc.) * We record the siginfo_t context in this function each time it is being * called, to be able to know what error caused an issue. */ static void coffeecatch_signal_pass(const int code, siginfo_t *const si, void *const sc) { native_code_handler_struct *t; DEBUG(print("caught signal\n")); /* Call the "real" Java handler for JIT and internals. */ coffeecatch_call_old_signal_handler(code, si, sc); /* Still here ? * FIXME TODO: This is the Dalvik behavior - but is it the SunJVM one ? */ /* Ensure we do not deadlock. Default of ALRM is to die. * (signal() and alarm() are signal-safe) */ signal(code, SIG_DFL); coffeecatch_start_alarm(); /* Available context ? */ t = coffeecatch_get(); if (t != NULL) { /* An alarm() call was triggered. */ coffeecatch_mark_alarm(t); /* Take note of the signal. */ coffeecatch_copy_context(t, code, si, sc); /* Back to the future. */ coffeecatch_try_jump_userland(t, code, si, sc); } /* Nope. (abort() is signal-safe) */ DEBUG(print("calling abort()\n")); signal(SIGABRT, SIG_DFL); abort(); }
/* Internal crash handler for abort(). Java calls abort() if its signal handler * could not resolve the signal ; thus calling us through this handler. */ static void coffeecatch_signal_abort(const int code, siginfo_t *const si, void *const sc) { native_code_handler_struct *t; (void) sc; /* UNUSED */ DEBUG(print("caught abort\n")); /* Ensure we do not deadlock. Default of ALRM is to die. * (signal() and alarm() are signal-safe) */ signal(code, SIG_DFL); coffeecatch_start_alarm(); /* Available context ? */ t = coffeecatch_get(); if (t != NULL) { /* An alarm() call was triggered. */ coffeecatch_mark_alarm(t); /* Take note (real "abort()") */ coffeecatch_copy_context(t, code, si, sc); /* Back to the future. */ coffeecatch_try_jump_userland(t, code, si, sc); } /* No such restore point, call old signal handler then. */ DEBUG(print("calling old signal handler\n")); coffeecatch_call_old_signal_handler(code, si, sc); /* Nope. (abort() is signal-safe) */ DEBUG(print("calling abort()\n")); abort(); }
/** * Enumerate backtrace information. */ void coffeecatch_get_backtrace_info(void (*fun)(void *arg, const char *module, uintptr_t addr, const char *function, uintptr_t offset), void *arg) { const native_code_handler_struct* const t = coffeecatch_get(); if (t != NULL) { #if (defined(USE_CORKSCREW)) t_coffeecatch_backtrace_symbols_fun bt; bt.fun = fun; bt.arg = arg; coffeecatch_backtrace_symbols(t->frames, t->frames_size, coffeecatch_backtrace_symbols_fun, &bt); #elif (defined(USE_UNWIND)) size_t i; for(i = 0; i < t->frames_size; i++) { const uintptr_t pc = t->frames[i]; format_pc_address_cb(pc, fun, arg); } #else (void) fun; (void) arg; #endif } }
/** * Get the signal associated with the crash. */ int coffeecatch_get_signal() { const native_code_handler_struct* const t = coffeecatch_get(); if (t != NULL) { return t->code; } else { return -1; } }
/** * Returns 1 if we are already inside a coffeecatch block, 0 otherwise. */ int coffeecatch_inside() { native_code_handler_struct *const t = coffeecatch_get(); if (t != NULL && t->reenter > 0) { t->reenter++; return 1; } return 0; }
void coffeecatch_abort(const char* exp, const char* file, int line) { native_code_handler_struct *const t = coffeecatch_get(); if (t != NULL) { t->expression = exp; t->file = file; t->line = line; } abort(); }
int coffeecatch_cancel_pending_alarm() { native_code_handler_struct *const t = coffeecatch_get(); if (t != NULL && t->alarm) { t->alarm = 0; /* "If seconds is 0, a pending alarm request, if any, is canceled." */ alarm(0); return 0; } return -1; }
/** * Calls coffeecatch_handler_setup(1) to setup a crash handler, mark the * context as valid, and return 0 upon success. */ int coffeecatch_setup() { if (coffeecatch_handler_setup(1) == 0) { native_code_handler_struct *const t = coffeecatch_get(); assert(t != NULL); t->ctx_is_set = 1; return 0; } else { return -1; } }
/** * Calls coffeecatch_handler_cleanup() */ void coffeecatch_cleanup() { native_code_handler_struct *const t = coffeecatch_get(); assert(t != NULL); assert(t->reenter > 0); t->reenter--; if (t->reenter == 0) { t->ctx_is_set = 0; coffeecatch_handler_cleanup(); } }
/** * Release the resources allocated by a previous call to * coffeecatch_handler_setup(). * This function must be called as many times as * coffeecatch_handler_setup() was called to fully release allocated * resources. **/ static int coffeecatch_handler_cleanup() { /* Cleanup locals. */ native_code_handler_struct *const t = coffeecatch_get(); if (t != NULL) { DEBUG(print("removing thread alternative stack\n")); /* Erase thread-specific value now (detach). */ if (pthread_setspecific(native_code_thread, NULL) != 0) { assert(! "pthread_setspecific() failed"); } /* Free handler and reset slternate stack */ if (coffeecatch_native_code_handler_struct_free(t) != 0) { return -1; } DEBUG(print("removed thread alternative stack\n")); } /* Cleanup globals. */ if (pthread_mutex_lock(&native_code_g.mutex) != 0) { assert(! "pthread_mutex_lock() failed"); } assert(native_code_g.initialized != 0); if (--native_code_g.initialized == 0) { size_t i; DEBUG(print("removing global signal handlers\n")); /* Restore signal handler. */ for(i = 0; native_sig_catch[i] != 0; i++) { const int sig = native_sig_catch[i]; assert(sig < SIG_NUMBER_MAX); if (sigaction(sig, &native_code_g.sa_old[sig], NULL) != 0) { return -1; } } /* Free old structure. */ free(native_code_g.sa_old); native_code_g.sa_old = NULL; /* Delete thread var. */ if (pthread_key_delete(native_code_thread) != 0) { assert(! "pthread_key_delete() failed"); } DEBUG(print("removed global signal handlers\n")); } if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { assert(! "pthread_mutex_unlock() failed"); } return 0; }
/** * Get the backtrace size. Returns 0 if no backtrace is available. */ size_t coffeecatch_get_backtrace_size(void) { #ifdef USE_UNWIND const native_code_handler_struct* const t = coffeecatch_get(); if (t != NULL) { return t->frames_size; } else { return 0; } #else return 0; #endif }
/** * Acquire the crash handler for the current thread. * The coffeecatch_handler_cleanup() must be called to release allocated * resources. **/ static int coffeecatch_handler_setup(int setup_thread) { int code; DEBUG(print("setup for a new handler\n")); /* Initialize globals. */ if (pthread_mutex_lock(&native_code_g.mutex) != 0) { return -1; } code = coffeecatch_handler_setup_global(); if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { return -1; } /* Global initialization failed. */ if (code != 0) { return -1; } /* Initialize locals. */ if (setup_thread && coffeecatch_get() == NULL) { native_code_handler_struct *const t = coffeecatch_native_code_handler_struct_init(); if (t == NULL) { return -1; } DEBUG(print("installing thread alternative stack\n")); /* Set thread-specific value. */ if (pthread_setspecific(native_code_thread, t) != 0) { coffeecatch_native_code_handler_struct_free(t); return -1; } DEBUG(print("installed thread alternative stack\n")); } /* OK. */ return 0; }
/** * Get the <index>th element of the backtrace, or 0 upon error. */ uintptr_t coffeecatch_get_backtrace(ssize_t index) { #ifdef USE_UNWIND const native_code_handler_struct* const t = coffeecatch_get(); if (t != NULL) { if (index < 0) { index = t->frames_size + index; } if (index >= 0 && (size_t) index < t->frames_size) { #ifdef USE_CORKSCREW return t->frames[index].absolute_pc; #else return t->frames[index]; #endif } } #else (void) index; #endif return 0; }
sigjmp_buf* coffeecatch_get_ctx() { native_code_handler_struct* t = coffeecatch_get(); assert(t != NULL); return &t->ctx; }
/** * Calls coffeecatch_handler_cleanup() */ void coffeecatch_cleanup() { native_code_handler_struct *const t = coffeecatch_get(); assert(t != NULL); t->ctx_is_set = 0; coffeecatch_handler_cleanup(); }
/** * Get the full error message associated with the crash. */ const char* coffeecatch_get_message() { const int error = errno; const native_code_handler_struct* const t = coffeecatch_get(); /* Found valid handler. */ if (t != NULL) { char * const buffer = t->stack_buffer; const size_t buffer_len = t->stack_buffer_size; size_t buffer_offs = 0; const char* const posix_desc = coffeecatch_desc_sig(t->si.si_signo, t->si.si_code); /* Assertion failure ? */ if ((t->code == SIGABRT #ifdef __ANDROID__ /* See Android BUG #16672: * "C assert() failure causes SIGSEGV when it should cause SIGABRT" */ || (t->code == SIGSEGV && (uintptr_t) t->si.si_addr == 0xdeadbaad) #endif ) && t->expression != NULL) { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "assertion '%s' failed at %s:%d", t->expression, t->file, t->line); buffer_offs += strlen(&buffer[buffer_offs]); } /* Signal */ else { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "signal %d", t->si.si_signo); buffer_offs += strlen(&buffer[buffer_offs]); /* Description */ snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " (%s)", posix_desc); buffer_offs += strlen(&buffer[buffer_offs]); /* Address of faulting instruction */ if (t->si.si_signo == SIGILL || t->si.si_signo == SIGSEGV) { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " at address %p", t->si.si_addr); buffer_offs += strlen(&buffer[buffer_offs]); } } /* [POSIX] If non-zero, an errno value associated with this signal, as defined in <errno.h>. */ if (t->si.si_errno != 0) { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, ": "); buffer_offs += strlen(&buffer[buffer_offs]); if (strerror_r(t->si.si_errno, &buffer[buffer_offs], buffer_len - buffer_offs) == 0) { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "unknown error"); buffer_offs += strlen(&buffer[buffer_offs]); } } /* Sending process ID. */ if (t->si.si_signo == SIGCHLD && t->si.si_pid != 0) { snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " (sent by pid %d)", (int) t->si.si_pid); buffer_offs += strlen(&buffer[buffer_offs]); } /* Faulting program counter location. */ if (coffeecatch_get_pc_from_ucontext(&t->uc) != 0) { const uintptr_t pc = coffeecatch_get_pc_from_ucontext(&t->uc); snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " "); buffer_offs += strlen(&buffer[buffer_offs]); format_pc_address(&buffer[buffer_offs], buffer_len - buffer_offs, pc); buffer_offs += strlen(&buffer[buffer_offs]); } /* Return string. */ buffer[buffer_offs] = '\0'; return t->stack_buffer; } else { /* Static buffer in case of emergency */ static char buffer[256]; #ifdef _GNU_SOURCE return strerror_r(error, &buffer[0], sizeof(buffer)); #else const int code = strerror_r(error, &buffer[0], sizeof(buffer)); errno = error; if (code == 0) { return buffer; } else { return "unknown error during crash handler setup"; } #endif } }