我怎样才能捕获 SIGSEGV (内存区段错误)并在安卓系统的 JNI 下获得堆栈跟踪?

我正在将 一个项目移动到新的 Android 原生开发工具包(即 JNI) ,我想捕捉 SIGSEGV,如果它发生(可能还有 SIGILL,SIGABRT,SIGFPE) ,为了呈现一个漂亮的崩溃报告对话框,而不是(或之前)当前发生的情况: 进程立即突然死亡,可能操作系统尝试重新启动它。(编辑: JVM/Dalvik VM 捕获信号并记录堆栈跟踪和其他有用信息; 我只是想提供给用户一个选项,让他们把这些信息通过电子邮件发送给我。)

情况是这样的: 一大堆我没有编写的 C 代码完成了这个应用程序的大部分工作(所有的游戏逻辑) ,尽管它已经在许多其他平台上进行了很好的测试,但是我在 Android 端口中给它添加垃圾代码并导致本机代码崩溃是完全有可能的,所以我希望当前显示在 Android 日志中的崩溃转储(包括本机代码和 Java 代码)(我想在非 Android 情况下应该是 stderr)。我可以自由地任意修改 C 和 Java 代码,尽管回调(进出 JNI 的回调)大约为40,而且很明显,小的差异会得到额外的分数。

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

64932 次浏览

In my limited experience (non-Android), SIGSEGV in JNI code will generally crash the JVM before control is returned to your Java code. I vaguely recall hearing about some non-Sun JVM which lets you catch SIGSEGV, but AFAICR you can't expect to be able to do so.

You can try to catch them in C (see sigaction(2)), although you can do very little after a SIGSEGV (or SIGFPE or SIGILL) handler as the ongoing behaviour of a process is officially undefined.

Edit: From Jelly Bean onwards you can't get the stack trace, because READ_LOGS went away. :-(

I actually got a signal handler working without doing anything too exotic, and have released code using it, which you can see on github (edit: linking to historical release; I removed the crash handler since then). Here's how:

  1. Use sigaction() to catch the signals and store the old handlers. (android.c:570)
  2. Time passes, a segfault happens.
  3. In the signal handler, call up to JNI one last time and then call the old handler. (android.c:528)
  4. In that JNI call, log any useful debugging info, and call startActivity() on an activity that is flagged as needing to be in its own process. (SGTPuzzles.java:962, AndroidManifest.xml:28)
  5. When you come back from Java and call that old handler, the Android framework will connect to debuggerd to log a nice native trace for you, and then the process will die. (debugger.c, debuggerd.c)
  6. Meanwhile, your crash-handling activity is starting up. Really you should pass it the PID so it can wait for step 5 to complete; I don't do this. Here you apologise to the user and ask if you can send a log. If so, gather the output of logcat -d -v threadtime and launch an ACTION_SEND with recipient, subject and body filled in. The user will have to press Send. (CrashHandler.java, SGTPuzzles.java:462, strings.xml:41
  7. Watch out for logcat failing or taking more than a few seconds. I have encountered one device, the T-Mobile Pulse / Huawei U8220, where logcat immediately goes into the T (traced) state and hangs. (CrashHandler.java:70, strings.xml:51)

In a non-Android situation, some of this would be different. You'd need to gather your own native trace, see this other question, depending on what sort of libc you have. You'd need to handle dumping that trace, launching your separate crash-handler process, and sending the email in some appropriate ways for your platform, but I imagine the general approach should still work.

FWIW, Google Breakpad works fine on Android. I did the porting work, and we're shipping it as part of Firefox Mobile. It requires a little setup, since it doesn't give you stack traces on the client-side, but sends you the raw stack memory and does the stack walking server-side (so you don't have to ship debug symbols with your app).

I'm a little bit late, but I had the exact same need, and I've developed a small library to address it, by catching common crashes (SEGV, SIBGUS, etc.) inside JNI code, and replace them by regular java.lang.Error exceptions. Bonus, if the client is running on Android >= 4.1.1, the stack trace embeds the resolved backtrace of the crash (a pseudo-trace containing the full native stack trace). You will not recover from vicious crashes (ie. if you corrupt the allocator, for example), but at least it should allows you to recover from most of them. (please report successes and failures, the code is brand new)

More info at https://github.com/xroche/coffeecatch (code is BSD 2-Clauses license)