当前位置: 首页 > news >正文

Android JNI Crash定位步骤

今天讲的是纯干货,目的就是为了指导Android开发者如何根据JNI Crash日志顺藤摸瓜,最后直捣黄龙定位磨人的JNI Crash。所以废话不多,直接开干吧。

1. 巧妇内为无米之炊,找到未strip的, 符号表完整的so库文件

在Android Studio 3.2.1: 

strip之前的文件所在目录:

app/build/intermediaters/transforms/mergejniLibs/debug

或者根据Crash的APP是debug还是release版本选择

app/build/intermediates/cmake/debug/obj
或
app/build/intermediates/cmake/release/obj

如果依赖的是Native module或者aar, 那么

strip之前的文件所在目录:

yourNativeLibModule/build/intermediates/transforms/mergeJniLibs/debug

由于CMake/CXX_FLAGS的配置等原因,以上目录下的文件可能还是被strip了。如何准确判断so有没有被strip请参照文章下面提到的readelf工具。

如果发现so被strip,尝试在CMake添加如下配置:

# 这几行代码表示debug版本的so文件保留so保留符号库,这样会导致so文件很大,
# 如果要让release版本保留符号库文件,就替换成CMAKE_C_FLAGS_RELEASE和CMAKE_CXX_FLAGS_RELEASE
# 但务必在正式对外发布的时候去掉release 配置的-g选项,以免增加文件size
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
# R16之前版本的NDK默认是编译时加-g的,新版本不确定,所以需要不strip的 so文件,最好在CMake里配置一下-g

strip之后的文件所在目录:

app/build/intermediaters/transforms/stripDebugSymbol/debug

2. 打蛇要打七寸,确定发生Crash的设备对应的CPU架构

在JNI Crash的日志里

如果有lib/arm, 则是armeabi-v7a架构; 

如果有lib/arm64, 则是arm64-v8a架构

然后根据CPU架构找相应的toolchain:

arm64-v8a对应的是aarch64-linux-android-4.9

armeabi-v7a对应的是arm-linux-androideabi-4.9

4. 工欲善其事必先利其器,使用add2line 和ndk-stack等工具分析JNI Crash的log

addr2line

作用是根据内存地址找到对应的报错代码的文件名和行号

所在目录是toolchain的bin文件夹,

比如 aarch64-linux-android-4.9对应的bin文件夹是

/Android/Sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin

arm-linux-androideabi-4.9,对应的bin文件夹是

/media/kyle/a393d005-ebe5-42a0-8c6a-c86fdfb185c1/Android/Sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin

用法:

# -f表示显示函数名, -e表示execution,后面是包含符号库的文件 以及报错的内存地址(即Crash log里pc后的字段)
arm-linux-androideabi-addr2line -f -e xxx.so 0x8eb09258

ndk-stack

作用是一键生成更可读的Crash 日志

所在目录是

/media/kyle/a393d005-ebe5-42a0-8c6a-c86fdfb185c1/Android/Sdk/ndk-bundle/ndk-stack

用法:

# -sym表示symbols
ndk-stack -sym App/build/intermediates/transforms/mergeJniLibs/release/0/lib/对应的abi目录 -dump jniCrash.log

或者

adb logcat | ndk-stack -sym  App/build/intermediates/transforms/mergeJniLibs/release/0/lib/对应的abi目录

------------------------分隔符------------------------

其他工具补充

toolchain下的:

arm-linux-androideabi-readelf

1. 有时候用addr2line发现能显示函数名但行号是乱码??,有可能是因为这个so被strip了。被strip的so的

readelf结果里“p headers”的个数会比未strip后的少,所以可以根据readelf来判断so是否是真的被strip了

命令格式:

arm-linux-androideabi-readelf -S xx.so

2. 可以用于查看so文件中的所有函数。所以如果遇到JNI方法找不到的错误,就可以使用该工具查看so库中的所有函数,然后搜索对应的JNI方法,看到底有没有被编译到动态库中。

命令格式:

arm-linux-androideabi-readelf -a xx.so > fun.txt 
# 注意:仍需要使用未strip之前的so文件, 上面的命令会把结果写入fun.txt

arm-linux-androideabi-objdump

可以获取so文件的符号表信息,可以看到编译进来的所有方法以及调用堆栈的地址.

命令格式:

arm-linux-androideabi-objdump -dx xx.so > stacktrace.txt 
或
aarch64-linux-android-objdump -dx xx.so > stacktrace.txt

arm-linux-androideabi-nm

可以查看静态库中的符号,比如查看所有方法的声明。

如果在用静态库编译so动态库的过程中碰到undefined reference类型的错误, 或者

duplicated reference, 可以使用这条指令将对应静态库的所有方法都导出来, 然后看一下是否有某方法

命令格式:

arm-linux-androideabi-nm xx.a > symbol.txt 

常用信号量的含义:

#define SIGABRT 6 // abort() 调用abort函数生成的信号,表示程序运行异常被中止
#define SIGSEGV 11 // segmentation violation 指针所对应的地址是无效或非法地址,比如访问越界/stack overflow/文件操作不被允许( fault addr 0x0一般是空指针错误)
#define SIGILL 4 // Illegal instruction 执行了非法指令,比如第三方库的兼容性问题,权限问题
#define SIGSYS 31 // bad argument to system call 非法的系统调用
#define SIGBUS 7 // 非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
#define SIGFPE 8 // 进程执行了一个错误的算术操作,比如除0、溢出
#define SIGKILL 9 // 强制结束程序,本信号不能被捕获
#define SIGPIPE 13 // write on a pipe with no one to read it 管道破裂,通常在进程间通信产生

参考文章:

《音视频开发进阶指南》 by  展晓凯 魏晓红

http://product.dangdang.com/25211329.html

Android NDK开发Crash错误定位 

https://blog.csdn.net/xyang81/article/details/42319789

Can anyone explain the gcc cross-compiler naming convention?

https://stackoverflow.com/questions/5731495/can-anyone-explain-the-gcc-cross-compiler-naming-convention

NDK toolchain对应ABI 

https://blog.csdn.net/lxlmycsdnfree/article/details/81664443

Android基础开发实践:如何分析Native Crash

https://cloud.tencent.com/developer/article/1192001

Android Stability - Native Crash问题概述

https://www.jianshu.com/p/d02523d2db82

相关文章:

  • 2019年的第三场LiveVideoStackCon有何不同?
  • 笑死人不偿命的知乎沙雕问题排行榜
  • Android MediaCodec 硬编码 H264 文件
  • 原创|Android Jetpack Compose 最全上手指南
  • 一场微秒级的同步事故
  • 在HTML5上开发音视频应用的五种思路
  • 如何把微信打造成一个学习利器|微信阅读与笔记技巧
  • 「圣诞特辑」纯前端实现人脸识别自动佩戴圣诞帽
  • LearnOpenGL 源码在 MAC 上的编译与调试
  • 在 iOS 中使用 OpenGL ES 实现绘画板
  • 技术开发故事会连载
  • OpenGL ES 学习资源分享
  • 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置
  • GLSL加载纹理颠倒的六种解决方案
  • 内存都没了,还能运行程序?
  • (三)从jvm层面了解线程的启动和停止
  • Java多态
  • JS专题之继承
  • React Native移动开发实战-3-实现页面间的数据传递
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • underscore源码剖析之整体架构
  • unity如何实现一个固定宽度的orthagraphic相机
  • ViewService——一种保证客户端与服务端同步的方法
  • Vue小说阅读器(仿追书神器)
  • webpack项目中使用grunt监听文件变动自动打包编译
  • win10下安装mysql5.7
  • 飞驰在Mesos的涡轮引擎上
  • 蓝海存储开关机注意事项总结
  • 每天10道Java面试题,跟我走,offer有!
  • 免费小说阅读小程序
  • 面试总结JavaScript篇
  • 前端面试题总结
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 使用putty远程连接linux
  • 通信类
  •  一套莫尔斯电报听写、翻译系统
  • 移动端解决方案学习记录
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #Lua:Lua调用C++生成的DLL库
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #前后端分离# 头条发布系统
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $jQuery 重写Alert样式方法
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (C++17) std算法之执行策略 execution
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (二)linux使用docker容器运行mysql
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (译) 函数式 JS #1:简介
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)树状数组
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿