关于java native interface:如何捕获SIGSEGV(分段错误)并在Android下的JNI下获取堆栈跟踪?

How can I catch SIGSEGV (segmentation fault) and get a stack trace under JNI on Android?

我正在将一个项目转移到新的Android本机开发工具包(即JNI)中,我想捕获sigsegv,如果它发生(也可能是sigill、sigabrt、sigfpe),以便呈现一个很好的崩溃报告对话框,而不是(或之前)当前发生的事情:进程立即不确定地死亡,操作系统可能试图重新开始吧。(编辑:jvm/dalvik-vm捕获信号并记录堆栈跟踪和其他有用信息;我只想为用户提供将该信息通过电子邮件发送给我的选项。)

情况是:一个很大的C代码,我没有编写,在这个应用程序(大部分游戏逻辑)做大部分的工作,尽管它在许多其他平台上被很好的测试,但是在我的Android端口中,我完全有可能为它提供垃圾并导致本地代码崩溃,所以我希望CurrE的崩溃转储(本地和Java)tly出现在android日志中(我想在非android情况下是stderr)。我可以随意地修改C和Java代码,尽管回调(JNI的输入和输出)都是40左右,很明显是小差别的加分。

我听说过j2se,libjsig.so中的信号链库,如果我能在android上安全地安装这样的信号处理器,这就解决了我的问题,但是我没有看到android/dalvik这样的库。


编辑:从JellyBean开始,你就不能得到堆栈跟踪,因为READ_LOGS消失了。:

实际上,我有一个信号处理程序在工作时没有做任何太奇怪的事情,并且使用它发布了代码,您可以在GitHub上看到(编辑:链接到历史版本;从那时起我就删除了崩溃处理程序)。以下是如何:

  • 使用sigaction()捕捉信号并存储旧的处理程序。(安卓C:570)
  • 时间过了,就会发生一个分段故障。
  • 在信号处理程序中,最后一次调用JNI,然后调用旧的处理程序。(安卓C:528)
  • 在该JNI调用中,记录任何有用的调试信息,并对标记为需要处于其自身进程中的活动调用startActivity()。(SjtSimuls.java:962,ANDROIDMANDS.XML:28)
  • 当您从Java返回并调用旧的处理程序时,Android框架将连接到EDCOX1 OR 3,以便为您记录一个好的本地跟踪,然后该进程将死亡。(debugger.c、debuggerd.c)
  • 同时,您的碰撞处理活动正在启动。实际上,您应该将PID传递给它,以便它可以等待第5步完成;我不这样做。在这里,您向用户道歉并询问是否可以发送日志。如果是这样的话,收集logcat -d -v threadtime的输出并发射一个ACTION_SEND,填写接收者、主题和主体。用户必须按"发送"。(CRASHANTHLLR.java,SGTSimple):Java:462,StR.xml:41
  • 当心logcat失败或需要几秒钟以上。我遇到过一个设备,T-Mobile Pulse/Huawei U8220,logcat立即进入T状态并挂起。(CRASHANTHANDL.java:70,SCOR.XML: 51)
  • 在非Android环境中,有些情况会有所不同。你需要收集你自己的本地跟踪,看看另一个问题,这取决于你有什么类型的libc。您需要处理转储跟踪、启动单独的崩溃处理程序进程,并以适合您的平台的某些适当方式发送电子邮件,但我认为一般的方法仍然有效。


    我有点晚了,但我有同样的需求,我开发了一个小库来解决这个问题,通过捕获JNI代码中的常见崩溃(SEGVSIBGUS等),并用常规java.lang.Error异常替换它们。另外,如果客户机在android上运行>=4.1.1,那么堆栈跟踪将嵌入崩溃的解析后跟踪(包含完整本机堆栈跟踪的伪跟踪)。您将无法从恶性崩溃中恢复(例如,如果您损坏了分配器),但至少它应该允许您从大多数崩溃中恢复。(请报告成功和失败,代码是全新的)

    更多信息请访问https://github.com/xroche/coffeeecatch(代码为BSD 2条款许可证)


    fwiw,谷歌BreakPad在Android上运行良好。我做了移植工作,我们将把它作为Firefox移动的一部分进行运送。它需要一些设置,因为它不提供客户端的堆栈跟踪,而是向您发送原始堆栈内存,并执行堆栈遍历服务器端操作(因此您不必在应用程序中附带调试符号)。


    在我有限的经验(非Android)中,JNI代码中的SIGSEGV通常会在控制返回到Java代码之前崩溃JVM。我隐约记得听说过一些非Sun的JVM可以让你抓到Sigsegv,但在很小的范围内,你不能指望它能做到。

    您可以尝试在C语言中捕获它们(参见sigaction(2)),尽管在sigsegv(或sigfpe或sigill)处理程序之后可以做很少的事情,因为进程的持续行为是官方定义的。