コード例 #1
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/* 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();
}
コード例 #2
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/* 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();
}
コード例 #3
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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
  }
}
コード例 #4
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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;
  }
}
コード例 #5
0
ファイル: coffeecatch.c プロジェクト: buggins/coolreader
/**
 * 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;
}
コード例 #6
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
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();
}
コード例 #7
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
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;
}
コード例 #8
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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;
  }
}
コード例 #9
0
ファイル: coffeecatch.c プロジェクト: buggins/coolreader
/**
 * 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();
  }
}
コード例 #10
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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;
}
コード例 #11
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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
}
コード例 #12
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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;
}
コード例 #13
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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;
}
コード例 #14
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
sigjmp_buf* coffeecatch_get_ctx() {
  native_code_handler_struct* t = coffeecatch_get();
  assert(t != NULL);
  return &t->ctx;
}
コード例 #15
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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();
}
コード例 #16
0
ファイル: coffeecatch.c プロジェクト: gmetal/coffeecatch
/**
 * 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
  }
}