Android 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/objapp/build/intermediates/cmake/release/obj
由于CMake/CXX_FLAGS的配置等原因,以上目录下的文件可能还是被strip了。如何准确判断so有没有被strip请参照文章下面提到的readelf工具。或者参考:file查看strip状况
如果发现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
//并且不要有 -fvisibility=hidden 和 -s 选项!!

如果依赖的是Native module或者aar, 那么
strip之前的文件所在目录:
yourNativeLibModule/build/intermediates/transforms/mergeJniLibs/debug

strip之后的文件所在目录:
app/build/intermediaters/transforms/stripDebugSymbol/debug

2. 确定发生Crash的设备对应的CPU架构

在JNI Crash的日志里
如果有lib/arm, 则是armeabi-v7a架构;
如果有lib/arm64, 则是arm64-v8a架构

3. 根据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

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

ndk-stack

作用是一键生成更可读的Crash 日志
所在目录是/media/kyle/a393d005-ebe5-42a0-8c6a-c86fdfb185c1/Android/Sdk/ndk-bundle/ndk-stack
用法:
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目录

-sym表示symbols

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

其他工具补充

toolchain下的:

arm-linux-androideabi-readelf
  1. 有时候用addr2line发现能显示函数名但行号是乱码??,有可能是因为这个so被strip了。被strip的so的
    readelf结果里“section 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 或者其他小地址 fault addr 0x0000008 一般是空指针错误,访问为null的结构体的成员变量时,报错地址会是小地址)
#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 管道破裂,通常在进程间通信产生
用AddressSanitizer检测内存问题

谷歌官方出品的AddressSanitizer,使用也比较简单。目前还不支持内存泄漏的检测,但支持检测以下内存问题:

tip
  1. 有时候堆栈里有offset信息,比如
#09  pc 0000000000087050  /data/app/com.ufotosoft.justshot-o0tTYIIuxWN-zbg7o3aW_g==/oat/arm64/base.odex (offset 0x85000) (com.tencent.apollo.ApolloVoiceEngine.Pause [DEDUPED]+144)t

这里的offset指的是so文件的偏移量

  1. 在许多情况下,故障地址将不会是 0,而是其他一些小数字。两位或三位地址尤其常见,而六位地址几乎肯定不是 Null 指针解引用(它需要 1 MiB 的偏移量)
    一个充分的低位故障地址通常意味着您可以跳过堆栈中的任意 libc.so 帧,并直接归咎于调用的代码。不过,情况并非总是如此,这些例外将是您用作展示的绝佳机会。
    您可以使用 crasher fprintf-NULL 或 crasher readdir-NULL 重现此类崩溃问题的实例
本文参考文章

Android NDK开发Crash错误定位
Can anyone explain the gcc cross-compiler naming convention?
NDK toolchain对应ABI
Android基础开发实践:如何分析Native Crash(文字介绍的“当前符号表so与实际出现Crash的so不匹配,但当前出问题的native函数没有进行过修改时仍可以解析”的方法很好用,记得加地址偏移时是16进制)
Android Stability - Native Crash问题概述
诊断原生代码崩溃问题

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350