如何在Android上的JNI下捕获SIGSEGV(分段错误)并获得堆栈跟踪?

时间:2009-07-04 23:18:19

标签: android java-native-interface signals android-ndk sigsegv

我正在移动a project到新的Android Native Development Kit(即JNI),我想抓住SIGSEGV,如果它发生(可能还有SIGILL,SIGABRT,SIGFPE)以呈现一个不错的崩溃报告对话框,而不是(或之前)当前发生的事情:该过程立即毫不客气地死亡,并且可能是操作系统重启它的一些尝试。 (编辑: JVM / Dalvik VM捕获信号并记录堆栈跟踪和其他有用信息;我只想为用户提供将该信息通过电子邮件发送给我的选项。)

情况是:我没有编写的大量C代码完成了这个应用程序中的大部分工作(所有游戏逻辑),虽然它在很多其他平台上都经过了很好的测试,但我完全有可能在我的Android端口中,它会导致垃圾并导致本机代码崩溃,因此我想要当前显示在Android日志中的故障转储(本机和Java)(我想在非Android情况下它会是stderr )。我可以随意修改C和Java代码,虽然回调(进入和退出JNI)的数量大约为40,显然是小差异的奖励积分。

我听说过J2SE中的信号链接库libjsig.so,如果我可以安全地在Android上安装这样的信号处理程序,这将解决我的问题中的捕获部分,但我看不到这样的库机器人/的Dalvik。

4 个答案:

答案 0 :(得分:79)

编辑:从Jelly Bean开始,您无法获得堆栈跟踪,因为READ_LOGS went away。 : - (

我实际上有一个信号处理程序在没有做任何异国情调的情况下工作,并且已经使用它发布了代码,你可以看到on github(编辑:链接到历史版本;从那时起我删除了崩溃处理程序)。方法如下:

  1. 使用sigaction()捕获信号并存储旧处理程序。 (android.c:570
  2. 时间过去了,发生了一个段错误。
  3. 在信号处理程序中,最后一次调用JNI,然后调用旧处理程序。 (android.c:528
  4. 在该JNI调用中,记录任何有用的调试信息,并在标记为需要在其自己的进程中的活动上调用startActivity()。 (SGTPuzzles.java:962AndroidManifest.xml:28
  5. 当你从Java回来并调用那个旧处理程序时,Android框架将连接到debuggerd为你记录一个漂亮的本机跟踪,然后该进程将会死亡。 (debugger.cdebuggerd.c
  6. 与此同时,您的崩溃处理活动正在启动。真的,你应该把PID传递给它,这样它就可以等到第5步完成;我不这样做。在这里,您向用户道歉并询问您是否可以发送日志。如果是,请收集logcat -d -v threadtime的输出并启动ACTION_SEND,其中包含收件人,主题和正文。用户必须按发送。 (CrashHandler.javaSGTPuzzles.java:462strings.xml:41
  7. 注意logcat失败或超过几秒钟。我遇到过一个设备,T-Mobile Pulse /华为U8220,其中logcat立即进入T(跟踪)状态并挂起。 (CrashHandler.java:70strings.xml:51
  8. 在非Android情况下,其中一些情况会有所不同。您需要收集自己的本机跟踪,请参阅this other question,具体取决于您拥有的libc类型。您需要处理转储跟踪,启动单独的崩溃处理程序进程,以及以适当的方式为您的平台发送电子邮件,但我认为一般方法仍然有效。

答案 1 :(得分:14)

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

https://github.com/xroche/coffeecatch的更多信息 (代码是 BSD 2条款许可

答案 2 :(得分:6)

FWIW,Google Breakpad在Android上正常运行。我做了移植工作,我们将它作为Firefox Mobile的一部分发布。它需要一些设置,因为它不会在客户端提供堆栈跟踪,但会向您发送原始堆栈内存并执行堆栈服务器端(因此您不必使用应用程序发送调试符号) )。

答案 3 :(得分:5)

在我有限的经验(非Android)中,JNI代码中的SIGSEGV通常会在控制返回到Java代码之前使JVM崩溃。我模糊地回忆起一些非Sun JVM,它可以让你捕获SIGSEGV,但AFAICR你不能指望能够这样做。

您可以尝试在C中捕获它们(请参阅sigaction(2)),尽管在SIGSEGV(或SIGFPE或SIGILL)处理程序之后您可以做很少的事情,因为进程的持续行为是正式未定义的。