在android开发中,对于native产生的异常,很可能会产生闪退。对于ndk和native(c、c++)开发中,指针和内存管理是最重要也是最容易出问题的地方,稍有不慎就会遇到诸如内存地址访问错误、野针对、内存泄露、堆栈溢出、初始化错误、类型转换错误、数字除0等常见的问题。
Android NDK安装包中提供了三个调试工具:addr2line、objdump和ndk-stack可用于native异常的分析。add2line、objdump的使用可以参考https://www.jianshu.com/p/dd0b0a09aa78。本文着重分析ndk-stack。
一、ndk-stack
1、人为制造native异常
image.png此处使用了一个指针,但并未给指针分配空间。
运行后app会闪退,产生如下报错:
2021-09-21 22:10:06.990 11638-11638/aom.example.dj.appgl E/dj------: dj---------- nativeInit
--------- beginning of crash
2021-09-21 22:10:06.990 11638-11638/aom.example.dj.appgl A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 11638 (xample.dj.appgl), pid 11638 (xample.dj.appgl)
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: Process name is aom.example.dj.appgl, not key_process
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: Build fingerprint: 'OPPO/PBEM00/PBEM00:10/QKQ1.190918.001/2021060001:user/release-keys'
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: Revision: '0'
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: ABI: 'arm'
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: Timestamp: 2021-09-21 22:10:07+0800
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: pid: 11638, tid: 11638, name: xample.dj.appgl >>> aom.example.dj.appgl <<<
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: uid: 11069
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: Cause: null pointer dereference
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: r0 00000022 r1 00000000 r2 00000001 r3 00000003
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: r4 010d9528 r5 010d9528 r6 00ebd4c7 r7 ff8ffa28
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: r8 00000000 r9 ebdf1e00 r10 ff8ffa50 r11 ebdf1e00
2021-09-21 22:10:07.168 11786-11786/? A/DEBUG: ip ff8ff518 sp ff8ff9f8 lr beb167a1 pc beb167b6
2021-09-21 22:10:07.235 549-549/? E/SELinux: avc: denied { find } for interface=vendor.qti.hardware.servicetracker::IServicetracker sid=u:r:system_server:s0 pid=2325 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0
backtrace信息如下:
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: backtrace:
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #00 pc 0000c7b6 /data/app/aom.example.dj.appgl-ds9TsdpSuM_eCCnaRssZOw==/lib/arm/libnative-lib.so (nativeStringInit+50) (BuildId: 5d55b80f27765026e62fbfb60bdfe78ae321f480)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #01 pc 000dc519 /apex/com.android.runtime/lib/libart.so (art_quick_generic_jni_trampoline+40) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #02 pc 000d7bc5 /apex/com.android.runtime/lib/libart.so (art_quick_invoke_stub_internal+68) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #03 pc 0043d75f /apex/com.android.runtime/lib/libart.so (art_quick_invoke_static_stub+246) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #04 pc 000dff95 /apex/com.android.runtime/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+188) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #05 pc 002146f3 /apex/com.android.runtime/lib/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+270) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #06 pc 002108e7 /apex/com.android.runtime/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+738) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #07 pc 00434f4f /apex/com.android.runtime/lib/libart.so (MterpInvokeStatic+326) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #08 pc 000d2994 /apex/com.android.runtime/lib/libart.so (mterp_op_invoke_static+20) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #09 pc 0002ffee [anon:dalvik-classes2.dex extracted in memory from /data/app/aom.example.dj.appgl-ds9TsdpSuM_eCCnaRssZOw==/base.apk!classes2.dex] (com.example.dj.appgl.MainActivity.onClick+482)
2021-09-21 22:10:07.600 11786-11786/? A/DEBUG: #10 pc 0043413d /apex/com.android.runtime/lib/libart.so (MterpInvokeInterface+1536) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
···
可以看到出错位置在/libnative-lib.so (nativeStringInit+50)。但是具体信息不详,只知道出错的汇编指令地址在:#00 pc 0000c7b6
2、使用ndk-stack恢复异常堆栈
adb logcat | /Users/daijun/Library/Android/sdk/ndk/20.0.5594570/ndk-stack -sym /Users/daijun/Documents/dj_code/jb_imp/opengl/github/DOpenglTest/nativelibrary/build/intermediates/cmake/debug/obj/armeabi-v7a/
执行后得到的结果如下:
********** Crash dump: **********
Build fingerprint: 'OPPO/PBEM00/PBEM00:10/QKQ1.190918.001/2021060001:user/release-keys'
#00 0x0000c7b6 /data/app/aom.example.dj.appgl-ds9TsdpSuM_eCCnaRssZOw==/lib/arm/libnative-lib.so (nativeStringInit+50) (BuildId: 5d55b80f27765026e62fbfb60bdfe78ae321f480)
nativeStringInit
/Users/daijun/Documents/dj_code/jb_imp/opengl/github/DOpenglTest/nativelibrary/src/main/cpp/native-lib.cpp:36:8
#01 0x000dc519 /apex/com.android.runtime/lib/libart.so (art_quick_generic_jni_trampoline+40) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
#02 0x000d7bc5 /apex/com.android.runtime/lib/libart.so (art_quick_invoke_stub_internal+68) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
#03 0x0043d75f /apex/com.android.runtime/lib/libart.so (art_quick_invoke_static_stub+246) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
#04 0x000dff95 /apex/com.android.runtime/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+188) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
#05 0x002146f3 /apex/com.android.runtime/lib/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+270) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
#06 0x002108e7 /apex/com.android.runtime/lib/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+738) (BuildId: 1bc1b47bcd2822802d756e5ab4666019)
可以看出出错的位置在
native-lib.cpp:36:8
即native-lib.cpp的第36行。
二、native崩溃捕获机制
native的异常捕获,开源方案有 coffeecatch 、 breakpad。普通项目可以继承bugly。
native异常发生时,CPU通过异常中断的方式,触发异常处理流程。linux把这些中断处理,统一为信号量,可以注册信号量向量进行处理。信号(全称:软中断信号)机制是进程之间相互传递消息的一种方法。
1、信号机制
image.png函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间的转换。信号处理机制示意图如上所示。
信号的处理:信号处理函数是运行在用户态的,调用处理函数前,内核会将当前内核栈的内容备份拷贝到用户栈上。接下来进程返回到用户态中,执行相应的信号处理函数。信号处理函数执行完成后,还需要返回内核态,检查是否还有其它信号未处理。如果所有信号都处理完成,就会将内核栈恢复。
2、常见信号量类型
image.png3、native crash的捕获
主要通过注册信号处理函数。通过sigaction()捕获native异常。
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
详细处理参见文章:
image.png
三、集成bugly后的native符号表分析
未上传so符号表时的出错堆栈如下:
image.png
此时由于并未上传符号表,所以并不能还原。
符号表:
符号表是内存地址与函数名、文件名、行号的映射表。符号表元素如下所示:
<起始地址> <结束地址> <函数> [<文件名:行号>]
bugly最新版3.3.4符号表上传工具,需要java 8来执行。所以执行前,确认电脑里已经安装了jdk 1.8,否则会执行错误。
查看当前Mac已安装jdk目录
/usr/libexec/java_home -V
执行命令如下:
java -jar buglyqq-upload-symbol.jar -appid e4c59fc9cf
-appkey 99bb0e4e-910d-4247-85e3-ea026cef03a2
-bundleid aom.example.dj.appgl
-version 1.0
-platform Android
-inputSymbol /Users/daijun/Documents/dj_code/jb_imp/opengl/github/DOpenglTest/nativelibrary/build/intermediates/cmake/debug/obj/armeabi-v7a/
命令执行成功后,对应的native crash的符号表tab页下,会显示so符号表文件已上传。
image.png
出错堆栈的解析结果,跟本地使用ndk-stack的还原结果一致。
image.png
四、flutter 符号表获取
Flutter的引擎部分全部使用C/C++实现,为了减少包大小,所有的SO库在发布时都会去除符号表信息。和其他的JNI崩溃堆栈一样,我们上报的堆栈信息中只能看到内存地址偏移量等信息。
通过上面的分析知道可以使用ndk-stack来进行还原,前提是得能下载到带有符号表的SO文件。查看自己使用的flutter版本,可以在官方flutter_infra页面直接下载。
4.1 查看flutter版本
在终端中执行
flutter --version
Flutter 1.20.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision bbfbf1770c (1 year, 2 months ago) • 2020-08-13 08:33:09
-0700
Engine • revision 9d5b21729f
Tools • Dart 2.9.1
4.2 查找engine版本
1、在本地的flutter安装目录中,在/bin/internal/engine.version中查看当前版本对应的engine版本。如我的版本是9d5b21729ff53dbf8eadd8bc97e0e30d77abec95。
2、在flutter仓库中搜索对应的版本,找到对应的文件夹
参考
https://blog.csdn.net/lqf19921217/article/details/109201295
https://tech.meituan.com/2018/08/09/waimai-flutter-practice.html
https://github.com/flutter/flutter/wiki/Crashes
https://blog.csdn.net/qq_30379689/article/details/90813052
https://console.cloud.google.com/storage/browser/flutter_infra/flutter/
网友评论