Android native 崩溃信息捕获实践

本文介绍了在Android平台上捕获和处理native层崩溃的方法,包括设置signal handler、打印siginfo_t和堆栈信息。通过创建后台工作线程来避免在信号处理函数中直接调用Java代码的问题,使用libbacktrace库获取和打印堆栈轨迹。文中提供了相关代码示例和参考资料。
摘要由CSDN通过智能技术生成

本篇是 bugly 一篇关于 native crash 捕获的文章的练习。由于他文章中已经给出了相关的大部分知识点,这里我就仅仅补充一些细节,并给出一个完整的 demo。建议大家先阅读 Android 平台 Native 代码的崩溃捕获机制及实现
,熟悉一下相关的知识。

相关代码可以在 https://github.com/Jekton/NativeCrashCatching(目标平台是 Android 8) 找到。

设置 signal handler 的运行堆栈

static void SetUpStack() {
  stack_t stack{};
  stack.ss_sp = new(std::nothrow) char[SIGSTKSZ];

  if (!stack.ss_sp) {
    LOGW(kTag, "fail to alloc stack for crash catching");
    return;
  }
  stack.ss_size = SIGSTKSZ;
  stack.ss_flags = 0;
  if (stack.ss_sp) {
    if (sigaltstack(&stack, nullptr) != 0) {
      LOGERRNO(kTag, "fail to setup signal stack");
    }
  }
}

SIGSTKSZ 是一个 signal.h 预定义的常量,我们可以直接用它做目标的栈大小。LOGERRNO, LOGD, LOGE 等是我自己定义的打印 Android log 的宏。

设置信号处理函数

static std::map<int, struct sigaction> sOldHandlers;

static void SetUpSigHandler() {
  struct sigaction action{};
  action.sa_sigaction = SignalHandler;
  action.sa_flags = SA_SIGINFO | SA_ONSTACK;
  int signals[] = {
      SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGPIPE
  };
  struct sigaction old_action;
  for (auto signo : signals) {
    if (sigaction(signo, &action, &old_action) == -1) {
      LOGERRNO(kTag, "fail to set signal handler for signo %d", signo);
    } else {
      if (old_action.sa_handler != SIG_DFL && old_action.sa_handler != SIG_IGN) {
        sOldHandlers[signo] = old_action;
      }
    }
  }
}

这里我们把旧的 signal handler 保存起来,执行完我们自己的函数后,再调用它们:

static void SignalHandler(int signo, siginfo_t* info, void* context) {
  DumpSignalInfo(info);
  DumpStacks(context);

  CallOldHandler(signo, info, context);
  exit(0);
}

static void CallOldHandler(int signo, siginfo_t* info, void* context) {
  auto it = sOldHandlers.find(signo);
  if (it != sOldHandlers.end()) {
    if (it->second.sa_flags & SA_SIGINFO) {
      it->second.sa_sigaction(signo, info, context);
    } else {
      it->second.sa_handler(signo);
    }
  }
}

DumpSignalInfo 用来打印 siginfo_tDumpStacks 用来打印堆栈,很快我们就会看到他的实现。

打印 siginfo_t

打印 siginfo_t 没什么技术含量,就只是根据 signosi_code 打印对应的消息。

static void DumpSignalInfo(siginfo_t* info) {
  switch (info->si_signo) {
  case SIGILL:
    LOGI(kTag, "signal SIGILL caught");
    switch (info->si_code) {
    case ILL_ILLOPC:
      LOGI(kTag, "illegal opcode");
      break;
    case ILL_ILLOPN:
      LOGI(kTag, "illegal operand");
      break;
    case ILL_ILLADR:
      LOGI(kTag, "illegal addressing mode");
      break;
    case ILL_ILLTRP:
      LOGI(kTag, "illegal trap");
      break;
    case ILL_PRVOPC:
      LOGI(kTag, "privileged opcode");
      break;
    case ILL_PRVREG:
      LOGI(kTag, "privileged register");
      break;
    case ILL_COPROC:
      LOGI(kTag, "coprocessor error");
      break;
    case ILL_BADSTK:
      LOGI(kTag, "internal stack error");
      break;
    default:
      LOGI(kTag, "code = %d", info->si_code);
      break;
    }
    break;
  case SIGFPE:
    LOGI(kTag, "signal SIGFPE caught");
    switch (info->si_code) {
    case FPE_INTDIV:
      LOGI(kTag, "integer divide by zero");
      break;
    case FPE_INTOVF:
      LOGI(kTag, "integer overflow");
      break;
    case FPE_FLTDIV:
      LOGI(kTag, "floating-point divide by zero");
      break;
    case FPE_FLTOVF:
      LOGI(kTag, "floating-point overflow");
      break;
    case FPE_FLTUND:
      LOGI(kTag, "floating-point underflow");
      break;
    case FPE_FLTRES:
      LOGI(kTag, "floating-point inexact r
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值