【问题标题】:How can I catch SIGSEGV (segmentation fault) and get a stack trace under JNI on Android?如何捕获 SIGSEGV(分段错误)并在 Android 上的 JNI 下获取堆栈跟踪?
【发布时间】:2010-11-08 03:54:53
【问题描述】:

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

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

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

【问题讨论】:

  • 如果可以通过包装脚本启动Java VM,可以检查应用程序是否异常退出,并进行报错。这将允许您干净地捕获各种异常退出,无论是 SIGSEGV、SIGKILL 还是其他。但是,我认为这对于现有的 Android 应用程序是不可能的,因此将其发布为评论(从答案转换而来)。
  • 另请参阅:Can't run a Java Android program with Valgrind,了解如何使用包装脚本(在 adb shell 中)启动 Android 应用。
  • 答案需要更新。由于调用非异步信号安全函数,已接受答案中提供的源代码将导致未定义的行为。请看这里:stackoverflow.com/questions/34547199/…

标签: android java-native-interface signals android-ndk segmentation-fault


【解决方案1】:

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

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

  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)

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

【讨论】:

  • 理想情况下,您应该检查一下库中是否发生了崩溃。如果它发生在其他地方(例如,在 VM 内部),来自信号处理程序的 JNI 调用可能会使事情变得非常混乱。这不是世界末日,因为无论如何你都处于崩溃中,但这可能会使 VM 崩溃的诊断变得更加困难(或导致奇怪的 VM 崩溃,最终出现在 Android 错误报告中并让所有人感到困惑)。跨度>
  • @Chris 分享你的研究项目真是太棒了!
  • 谢谢,这对于找出我的 JNI 哪里出了问题很有用。另外,来自 DCS 校友的您好!
  • 从 Service 启动新进程中的 Activity 还需要以下代码:newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  • 这个解决方案在 Jelly Bean 下仍然有效吗?第 6 步不会记录任何 debuggerd 输出吗?
【解决方案2】:

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

更多信息https://github.com/xroche/coffeecatch (代码是BSD 2-Clauses license

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 考虑到绝对缺少文档,几乎不可能配置 Breakpad
    • 真的没那么难,项目wiki上有很多文档。事实上,对于 Android,现在有一个 NDK 构建 Makefile,它应该非常易于使用:code.google.com/p/google-breakpad/source/browse/trunk/…
    • 您还需要为Android编译预处理调试符号文件的模块,您只能在Linux上编译。在 Mac 上编译时 - 它只构建 Mac/iOS dSym 预处理器。
    【解决方案4】:

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

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

    【讨论】:

    • 好吧,在“忽略 [ing] 不是由 kill(2) 或 raise(3) 生成的 SIGFPE、SIGILL 或 SIGSEGV 信号”之后,行为是未定义的,但不一定在捕获这样的信号。目前的计划是尝试一个 C 信号处理程序,它回调 Java,并以某种方式终止线程而不终止进程。这可能会也可能不会。 :-)
    • ...除了我不能使用 backtrace(),因为 Android 不使用 glibc,它使用 Bionic。 :-( 将需要涉及来自unwind.h_Unwind_Backtrace 的东西。
    猜你喜欢
    • 2012-03-02
    • 2019-02-07
    • 1970-01-01
    • 2015-06-15
    • 2019-03-05
    • 2014-08-20
    • 2013-04-18
    • 2023-03-12
    • 2010-10-11
    相关资源
    最近更新 更多