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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1083154/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:40:26  来源:igfitidea点击:

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

androidjava-native-interfacesignalsandroid-ndksegmentation-fault

提问by Chris Boyle

I'm moving a projectto the new Android Native Development Kit (i.e. JNI) and I'd like to catch SIGSEGV, should it occur (possibly also SIGILL, SIGABRT, SIGFPE) in order to present a nice crash reporting dialog, instead of (or before) what currently happens: the immediate unceremonious death of the process and possibly some attempt by the OS to restart it. (Edit:The JVM/Dalvik VM catches the signal and logs a stack trace and other useful information; I just want to offer the user the option to email that info to me really.)

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

The situation is: a large body of C code which I didn't write does most of the work in this application (all the game logic) and although it's well-tested on numerous other platforms, it's entirely possible that I, in my Android port, will feed it garbage and cause a crash in native code, so I want the crash dumps (both native and Java) that currently show up in the Android log (I guess it would be stderr in a non-Android situation). I'm free to modify both C and Java code arbitrarily, although the callbacks (both going in and coming out of JNI) number about 40 and obviously, bonus points for small diffs.

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

I've heard of the signal chaining library in J2SE, libjsig.so, and if I could safely install a signal handler like that on Android, that would solve the catching part of my question, but I see no such library for Android/Dalvik.

我听说过 J2SE 中的信号链库 libjsig.so,如果我可以在 Android 上安全地安装这样的信号处理程序,那将解决我的问题的关键部分,但我没有看到这样的 Android/Dalvik 库.

采纳答案by Chris Boyle

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

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

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:

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

  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 debuggerdto 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 threadtimeand launch an ACTION_SENDwith 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 logcatfailing 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)
  1. 使用sigaction()捕捉到信号并存储旧的处理程序。( android.c:570)
  2. 时间流逝,段错误发生。
  3. 在信号处理程序中,最后一次调用 JNI,然后调用旧的处理程序。( android.c:528)
  4. 在该 JNI 调用中,记录任何有用的调试信息,并调用startActivity()标记为需要在其自己的进程中的活动。( SGTPuzzles.java:962, AndroidManifest.xml:28)
  5. 当您从 Java 返回并调用该旧处理程序时,Android 框架将连接到debuggerd并为您记录一个不错的本机跟踪,然后该进程将终止。( debugger.c, debuggerd.c)
  6. 同时,您的碰撞处理活动正在启动。实际上,您应该将 PID 传递给它,以便它可以等待第 5 步完成;我不这样做。在这里你向用户道歉并询问是否可以发送日志。如果是这样,收集logcat -d -v threadtime并启动一个ACTION_SEND填写了收件人、主题和正文的输出。用户将不得不按发送。( CrashHandler.java, SGTPuzzles.java:462, strings.xml:41
  7. 注意logcat失败或花费超过几秒钟的时间。我遇到过一个设备,T-Mobile Pulse / Huawei U8220,其中 logcat 立即进入T(跟踪)状态并挂起。( 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.

在非 Android 情况下,其中一些会有所不同。您需要收集自己的本机跟踪信息,请参阅其他问题,具体取决于您拥有的 libc 类型。您需要处理转储该跟踪,启动单独的崩溃处理程序进程,并以适合您平台的某些适当方式发送电子邮件,但我认为一般方法应该仍然有效。

回答by xroche

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.Errorexceptions. Bonus, if the client is running on Android >= 4.1.1, the stack trace embeds the resolved backtraceof 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 mostof them. (please report successes and failures, the code is brand new)

我是有点晚,但我有完全相同的需求,我已经开发了一个小型图书馆,以解决这一问题,通过捕捉常见的崩溃(SEGVSIBGUS里面等)JNI代码,并定期更换他们java.lang.Error的例外。额外的好处是,如果客户端在 Android >= 上运行4.1.1,则堆栈跟踪会嵌入已解决的崩溃回溯(包含完整本机堆栈跟踪的伪跟踪)。你不会从恶性崩溃中恢复(例如,如果你破坏了分配器),但至少它应该允许你从大多数崩溃中恢复。(成功和失败请报告,代码是全新的)

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

更多信息请访问https://github.com/xroche/coffeecatch(代码为BSD 2-Clauses 许可

回答by Ted Mielczarek

FWIW, Google Breakpadworks 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).

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

回答by mas90

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.

根据我有限的经验(非 Android),JNI 代码中的 SIGSEGV 通常会在控制权返回给 Java 代码之前使 JVM 崩溃。我依稀记得听说过一些非 Sun JVM 可以让您捕获 SIGSEGV,但 AFAICR 您不能指望能够这样做。

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.

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