ndk-stack使用及符号表还原

在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(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(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.png

3、native crash的捕获

主要通过注册信号处理函数。通过sigaction()捕获native异常。

#include  
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

image.png

执行命令如下:

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仓库中搜索对应的版本,找到对应的文件夹

image.png

参考
https://blog.csdn.net/lqf19921217/article/details/109201295

image.png

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/

你可能感兴趣的:(ndk-stack使用及符号表还原)