Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android Native Crash问题排查思路

Android Native Crash问题排查思路

作者头像
看书的小蜗牛
发布于 2021-11-24 07:04:00
发布于 2021-11-24 07:04:00
2.2K00
代码可运行
举报
运行总次数:0
代码可运行

背景:定位难

对于Android APP而言,native层Crash相比于Java层更难捕获与定位,因为so的代码通常不可见,而且,一些第三方so的crash或者系统的更难定位,堆栈信息非常少:参考下面的几个native crash实例

甚至即时全量打印Log信息,也只能得到一些不太方便定位的日志,无法直接定位问题

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
09-14 10:14:36.590  1361  1361 I /system/bin/tombstoned: received crash request for pid 5908
09-14 10:14:36.591  5944  5944 I crash_dump64: performing dump of process 5687 (target tid = 5908)
09-14 10:14:36.607  5944  5944 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-14 10:14:36.608  5944  5944 F DEBUG   : Build fingerprint: 'Xiaomi/vangogh/vangogh:10/QKQ1.191222.002/V12.0.6.0.QJVCNXM:user/release-keys'
09-14 10:14:36.608  5944  5944 F DEBUG   : Revision: '0'
09-14 10:14:36.608  5944  5944 F DEBUG   : ABI: 'arm64'
09-14 10:14:36.608  5944  5944 F DEBUG   : Timestamp: 2021-09-14 10:14:36+0800
09-14 10:14:36.608  5944  5944 F DEBUG   : pid: 5687, tid: 5908, name: nioEventLoopGro  >>> com.netxx.xaxxxn <<<
09-14 10:14:36.608  5944  5944 F DEBUG   : uid: 10312
09-14 10:14:36.608  5944  5944 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
09-14 10:14:36.608  5944  5944 F DEBUG   : Cause: null pointer dereference
09-14 10:14:36.608  5944  5944 F DEBUG   :     x0  0000000000000000  x1  0000000014d85fb0  x2  0000000015100bf8  x3  0000000000000000
09-14 10:14:36.608  5944  5944 F DEBUG   :     x4  0000000015100c18  x5  000000000000005a  x6  0000000015100c30  x7  0000000000000018
09-14 10:14:36.608  5944  5944 F DEBUG   :     x8  0000000000000000  x9  20454cc47a8eade3  x10 00000000005c0000  x11 000000000000004b
09-14 10:14:36.608  5944  5944 F DEBUG   :     x12 000000000000001f  x13 0000000000000000  x14 00000000a2018668  x15 0000000000000010
09-14 10:14:36.608  5944  5944 F DEBUG   :     x16 0000000000000000  x17 0000000000054402  x18 00000077328bc000  x19 00000077616e0c00
09-14 10:14:36.608  5944  5944 F DEBUG   :     x20 0000000000000001  x21 00000000151004a0  x22 0000000014d85fb0  x23 00000000a1f03180
09-14 10:14:36.608  5944  5944 F DEBUG   :     x24 0000000000000001  x25 0000000000000000  x26 0000000000000003  x27 00000000151000b8
09-14 10:14:36.608  5944  5944 F DEBUG   :     x28 0000000000000000  x29 00000000151009b0
09-14 10:14:36.608  5944  5944 F DEBUG   :     sp  000000773536e4f0  lr  000000779431b80c  pc  0000007794240260

如上,虽然能看到 Cause: null pointer dereference,但是到底是什么代码导致的,没有非常明确的消息,不像Java层Crash有非常清晰堆栈,这就让Native的crash定位非常头痛。

如何定位native crash

对于Crash而言,精确的定位等于成功的一半。如何通过工具定位到native crash呢,如果是自己实现的so库,一般而言还是会有相应的日志打印出来的,本文主要针对一些特殊的so,尤其是不存在源码的so,对于这种场景如何定位,最重要当然还是复现:匹配对应的机型、环境、不断重试复现线上问题,一旦发生Crash后就些蛛丝马迹可查,本文以线上偶发的一个ARM64升级为例子,分析下定位流程:通过归纳,重试,复现场景后,便可以去查找问题日志,这个时候有一个挺好用的方法:bugreport命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ adb bugreport  ~\  

app crash 的时候,系统会保存一个tombstone文件到/data/tombstones目录,该命令会导出最近的crash相关信息,我们可以通过bugreport导出,导出后它是一个zip包的形式,解压后如下

对于每个tombstone,如果是native crash,打开后大概会看到如下日志:

最上面的这些日志是最重要的,它包含了发生crash的线程是哪个,发的日志调用帧是哪个,到这里基本能很大程度上帮助我们实现问题的定位了,也就是基于bugreport + tombstone。

问题分析

线上ARM64升级的Crash只发生在Android10的系统上,具体到我们这个BUG,最终归宿到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arm64/base.odex (BakerReadBarrierThunkAcquire_r15_r0_2)   

Cause: null pointer dereference

但是上述的问题看起来跟如下帧似乎没有任何关系

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arm64/base.odex (com.netease.mail.profiler.handler.BaseHandler.stopTrace+360)

Java层的代码,怎么忽然就跑到arm64/base.odex (BakerReadBarrierThunk中去了呢?不防分析一下完整的调用帧:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
backtrace:
      #00 pc 00000000008ee260  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (BakerReadBarrierThunkAcquire_r15_r0_2)
      #01 pc 00000000009c9808  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (com.netease.mail.profiler.handler.BaseHandler.stopTrace+360)
      #02 pc 00000000009b3cc4  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (com.netease.mail.profiler.handler.TailHandler$1.operationComplete+212)
      #03 pc 00000000009b3b8c  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (com.netease.mail.android.wzp.util.Util$1.operationComplete [DEDUPED]+108)
      #04 pc 0000000000b93180  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.util.concurrent.DefaultPromise.notifyListener0+80)
      #05 pc 0000000000b9370c  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.util.concurrent.DefaultPromise.notifyListeners+988)
      #06 pc 0000000000b94e3c  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.util.concurrent.DefaultPromise.trySuccess+92)
      #07 pc 0000000000ba499c  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.DefaultChannelPromise.trySuccess+44)
      #08 pc 0000000000b90ef4  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise+84)
      #09 pc 0000000000b91850  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect+192)
      #10 pc 0000000000bb390c  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.nio.NioEventLoop.processSelectedKey+444)
      #11 pc 0000000000bb3bf8  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized+312)
      #12 pc 0000000000bb55b8  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.channel.nio.NioEventLoop.run+824)
      #13 pc 0000000000ae1580  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.util.concurrent.SingleThreadEventExecutor$2.run+128)
      #14 pc 0000000000adf068  /data/app/com.netease.yanxuan-YLeR3gwwgd3DyIUBNJZ8cA==/oat/arm64/base.odex (io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run+72)
      #15 pc 00000000004afbb8  /system/framework/arm64/boot.oat (java.lang.Thread.run+72) (BuildId: 65cd48ea51183eb3b4cdfeb64ca2b90a9de89ffe)
      #16 pc 0000000000137334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: fc24b8afa1bd5f1872cc1a38bcfa1cdc)
      #17 pc 0000000000145fec  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: fc24b8afa1bd5f1872cc1a38bcfa1cdc)
      #18 pc 00000000004b0d98  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: fc24b8afa1bd5f1872cc1a38bcfa1cdc)
      #19 pc 00000000004b1eac  /apex/com.android.runtime/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue const*)+416) (BuildId: fc24b8afa1bd5f1872cc1a38bcfa1cdc)
      #20 pc 00000000004f2868  /apex/com.android.runtime/lib64/libart.so (art::Thread::CreateCallback(void*)+1176) (BuildId: fc24b8afa1bd5f1872cc1a38bcfa1cdc)
      #21 pc 00000000000e69e0  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) (BuildId: 1eb18e444251dc07dff5ebd93fce105c)
      #22 pc 0000000000084b6c  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 1eb18e444251dc07dff5ebd93fce105c)

从#22帧开始看出这个是一个ART解释执行的过程,Android中基本所有线程栈都是这种形式,那么最终就可以认为是解释BaseHandler.stopTrace这句的时候,出现了null pointer dereference这样一个异常,为甚会这样呢?由于在系统上有共性:只有Android10系统的ARM64设备上出现,所以有理由怀疑Android10的源码在BakerReadBarrierThunkAcquire_r15_r0_2这里的处理上有什么不对劲,通过检索akerReadBarrierThunkAcquire_r15_r0_2字符串,发现code_generator_arm64.cc源码CompileBakerReadBarrierThunk函数最终输出了这段日志:

对比Android10与Android 11源码发现有一处很明确的不同,在Field Load使用之前,多加了一个空检查的Case:

解释执行代码实在是看不懂:摘录了下这条记录的log Fix null checks on volatile reference field loads on ARM64.如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Fix null checks on volatile reference field loads on ARM64.

ART's compiler adds a null check HIR instruction before each field
load HIR instruction created in the instruction builder phase. When
implicit null checks are allowed, the compiler elides the null check
if it can be turned into an implicit one (i.e. if the offset is within
a system page range).

On ARM64, the Baker read barrier thunk built for field reference loads
needs to check the lock word of the holder of the field, and thus
includes an explicit null check if no null check has been done before.
However, this was not done for volatile loads (implemented with a
load-acquire instruction on ARM64). This change adds this missing null
check.

意思就是:对于volatile修饰的变量(映射为load-acquire instruction),加上空检查,避免运行时空指针。Android 10没有做这个空检查,该commit就是为修复该BUG,回到业务中发现,确实有地方用了多线程及volatile,并且有一定概率出现空指针,处理掉这段逻辑即可。

总结

最主要的是结合bugreport及tombstone文件做好定位,定位问题后,才方便解决。

参考文档

https://wufengxue.github.io/2020/06/22/wechat-voice-codec-SEGV_MAPERR.html 有效参考分析工具

https://developer.android.com/ndk/guides/ndk-stack

作者:看书的小蜗牛

原文链接: Android Native Crash问题排查思路

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/10/29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Android】NDK开发Crash分析
手机user版本还是userdebug或是eng版本:adb shell getprop ro.build.type
后端码匠
2022/12/05
1.5K0
【Android】NDK开发Crash分析
写了个 AS 插件:SmartNDKStack,快速定位 Android Native 开发 Crash
对于Android Native开发的人员而言,可能经常会在开发过程及线上环境中遇到Native Crash的问题,对于这类native crash,我们一般都会直接addr2line,或使用ndk中附带的ndk-stack脚本分析。
字节流动
2021/11/26
2.4K0
Android基础开发实践:如何分析Native Crash
Native Crash常常发生在带有Jni代码的APP中,或者系统的Native服务中。作为比较难分析的一类问题,Native Crash其实还是有较多的方法去定位。
天天P图攻城狮
2018/08/21
18.6K5
Android基础开发实践:如何分析Native Crash
frida native hook
这时候就以hook liblog 为例子,打印log一般用的接口是__android_log_print, 那就hook下这个接口, 首先准备好hook 脚本:
一只小虾米
2022/10/25
1K0
Android tombstone文件是如何生成的
本节内容我们聚焦到androidQ上,分析android中一个用于debug的功能,那就是tombstone,俗称“墓碑”。现实生活中墓碑一般是给死人准备的,而在android系统中“墓碑”则是给进程准备的。
DragonKingZhu
2020/03/24
6K1
NDK 开发中快速定位 crash 问题
在 NDK 开发中,排查问题遇到的最熟悉的关键字非 backtrace 莫属,Linux 系统中进程 crash 后通过 backtrace 输出堆栈信息,开发者就是基于这些堆栈信息来定位代码问题。当然定位 Native 层代码问题最优的方式还是通过 IDE(AS、VS)或者 GDB 进行 debug 断点调试,本文针对的是使用第三方 C/C++ SDK 出现 crash 的场景。
字节流动
2020/06/03
1.2K0
[精彩总结] 🦀️Rust 移动端开发体验
因为rust.cc不支持一些github支持的markdown语法,想要有更好的体验,可以跳到这里
MikeLoveRust
2022/11/28
2.4K0
[精彩总结] 🦀️Rust 移动端开发体验
WebView 常见 Crash 分析及解决方案
App 出现意外闪退我们称之为 Crash,Crash 率是衡量 App 稳定性的一个重要指标,App 的稳定性不仅影响使用体验,甚至会造成用户流失。由于导致 App Crash 的原因和类型众多,一篇文章无法阐述清楚,也不是本文的重点,我们不做过多赘述。
深度学习与Python
2021/11/11
5.8K0
Android Native Crash 分析
版本发布后,收集到到异常上报,有部分记录到是native crash。 而上报的native信息,无法直接定位到错误位置。
艳龙
2021/12/16
6350
Android N混合编译与对热补丁影响解析
首先非常抱歉Tinker没有按期内测,这主要因为开源的代码需要通过公司内部审核与评测,这项工作大约还需要一个月左右。当前Tinker已经在公司内部开源,我们会努力让它以更完善的姿态与大家见面。 大约在六月底,Tinker在微信全量上线了一个补丁版本,随即华为反馈在Android N上微信无法启动。冷汗冒一地,Android N又搞了什么东东?为什么与instant run保持一致的补丁方式也跪了?talk is cheap,show me the code。趁着台风妮妲肆虐广东,终于有时间总结一把。在此非常
微信终端开发团队
2018/01/29
3.4K0
Android N混合编译与对热补丁影响解析
【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 获取 tombstone_0X 崩溃日志信息 )
Tombstone 报错信息日志文件被保存在了 /data/tombstones/ 目录下 , 先 ROOT 再说 , 没有 ROOT 权限无法访问该目录中的信息 ;
韩曙亮
2023/03/28
1.5K0
【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 获取 tombstone_0X 崩溃日志信息 )
NDK生成的so动态库怎么调试
    很多时候Android业务层开发,和SDK开发是分开的,SDK更多与NDK生成so,这期间双方代码不共享,对调试带来很大难度,所以把一些方法汇总下:
TSINGEYE清眸物联
2023/01/04
1K0
深入理解iOS Crash Log
USB连接设备,接着在XCode菜单栏依次选择:Window -> Devices And Simulators,接着选择View Device Logs
用户2932962
2019/07/31
4.6K1
深入理解iOS Crash Log
Android NDK开发完全剖析
之前的两篇文章主要介绍了音视频SDK中的线程设计和消息队列,其实对那些想从Android转向音视频开发的同学来说,NDK方面的知识是不得不提的“前置条件”,因为音视频开发的主要是C/C++开发,也许有些同学会反驳,Android不是提供了很多音视频相关的工具吗?比如MediaCodec、MediaExtractor等等,且不说这些版本的兼容性,单单是这些工具的格式支持度如何呢?如果遇到不支持的音视频格式怎么办呢?这些工具我们应该学会怎么使用,但是它并不能支持我们深入学习音视频技术,很多跨平台和使用广泛的库都是C/C++的,所以NDK开发是音视频技术学习的“门槛”,本文的目的就是带你从0开始开始学习NDK相关的知识点。
马上就说
2022/05/25
2K0
Android NDK开发完全剖析
Android NDK开发基础
NDK即Native Development Kit,是Android上用来开发c/c++的开发工具包。 安装步骤:developer.android.com/studio/proj…
没关系再继续努力
2021/12/03
2.1K0
Android N 混合编译与对热补丁影响解析
大约在六月底,Tinker在微信全量上线了一个补丁版本,随即华为反馈在Android N上微信无法启动。冷汗冒一地,Android N又搞了什么东东?为什么与instant run保持一致的补丁方式也
张绍文
2017/07/18
3.7K0
Android N 混合编译与对热补丁影响解析
介绍一种性能较好的 Android native unwind 技术
介绍一种有点不同于目前 Android 平台上常用的 native backtrace 技术,在支持 Android ART unwind 的情况下,通过损失少数可回溯场景换取性能提升。方案有一些优势和局限性,适用于部分场景。 通常如何在 Android native 中进行栈回溯 其实 Android 上实现 native 栈回溯的方式并没有很多,罗列一下大概就两种:一种是基于函数栈帧基地址(fp=frame pointer)寄存器的栈回溯,另一种是基于异常处理(EH=Exception Handli
微信终端开发团队
2021/06/08
6.9K0
AudioTrack引发的应用Crash分析
还有一个信息是对应的场景是音频焦点丢失情况下。 本地尝试复现发现复现不出来,压测也没有复现。 google上搜了下,也有对应的issue,不过没有fix:https://issuetracker.google.com/issues/234934924
一只小虾米
2023/02/17
1.3K0
得物 Android Crash 治理实践
通过修复历史遗留的Crash漏报问题(包括端侧SDK采集的兼容性优化及Crash平台的数据消费机制完善),得物Android端的Crash监控体系得到显著增强,使得历史Crash数据的完整捕获能力得到系统性改善,相应Crash指标也有所上升,经过架构以及各团队的共同努力下,崩溃率已从最高的万2降至目前的万1.1到万1.5,其中疑难问题占比约90%、因系统bug导致的Crash占比约40%,在本文中将简要介绍一些较典型的系统Crash的治理过程。
得物技术
2025/03/13
3200
得物 Android Crash 治理实践
Android NDK开发入门
JNI (Java Native Interface英文缩写),译为Java本地接口。是Java众多开发技术中的一门技术,意在利用本地代码,为Java程序提供更高效、更灵活的拓展。尽管Java一贯以其良好的跨平台性而著称,但真正的跨平台非C/C++莫属,因为当前世上90%的系统都是基于C/C++编写的。同时,Java的跨平台是以牺牲效率换来对多种平台的兼容性,因而JNI就是这种跨平台的主流实现方式之一。
xiangzhihong
2022/11/30
1.9K0
相关推荐
【Android】NDK开发Crash分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档