文章目录
- 目的
- 常用的辅助工具
- 分析步骤
- 参考
目的
Android NDK 中出现的 crash 日志分析定位,使用 addr2line 对库中定位so 动态库崩溃位置,定位到某个函数的具体的代码行。
常用的辅助工具
add2line,objdump,ndkstack 等等。本文主要介绍 NDK 环境中的 add2line
如何使用。
Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。
一般适用于 debug 版本或带有 symbol 信息的库。
分析步骤
-
找到ndk中安装目录下的 addr2line.exe
示例如下:安装了NDK后的某个目录。
第一个就是我们需要的命令,不过名字太长了,使用时不太方便,于是我就 copy 一个放到最后,并简短的命令一下为
addr2line
。 -
然后在 windows 中添加系统环境下,方便我们直接调用该命令。
-
抓取日志并找到关键日志信息。
Android Studio中的日志不容易抓全,容易一闪而过。可以用bat 命令来抓。
参考命令如下:
@echo off
echo wait device
adb wait-for-deviceecho loging
adb logcat -v time >C:\Users\DELL\Desktop\log\error.logpause
获取崩溃产生的记录堆栈信息的墓碑文件tombstone。 logcat 错误日志如下:
12-22 23:44:46.790 I/crash_dump64(12441): obtaining output fd from tombstoned, type: kDebuggerdTombstone
12-22 23:44:46.790 I//system/bin/tombstoned( 1063): received crash request for pid 12359
12-22 23:44:46.792 I/crash_dump64(12441): performing dump of process 12359 (target tid = 12359)
12-22 23:44:46.794 F/DEBUG (12441): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-22 23:44:46.794 F/DEBUG (12441): Build fingerprint: ‘PANCAKEXR/PANCAKE_1/PANCAKE_1:10/QKQ1.201018.002/11:userdebug/test-keys’
12-22 23:44:46.794 F/DEBUG (12441): Revision: ‘0’
12-22 23:44:46.794 F/DEBUG (12441): ABI: ‘arm64’
12-22 23:44:46.794 F/DEBUG (12441): Timestamp: 2022-12-22 23:44:46+0800
12-22 23:44:46.794 F/DEBUG (12441): pid: 12359, tid: 12359, name: om.ssnwt.camera >>> com.ssnwt.camera <<<
12-22 23:44:46.794 F/DEBUG (12441): uid: 10105
12-22 23:44:46.794 F/DEBUG (12441): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
12-22 23:44:46.794 F/DEBUG (12441): Cause: null pointer dereference
12-22 23:44:46.794 F/DEBUG (12441): x0 0000007238370ccb x1 000000000000002f x2 0000000000000001 x3 0000007238370ccb
12-22 23:44:46.794 F/DEBUG (12441): x4 005b257300000000 x5 0000008000000000 x6 1246040000000000 x7 0000000080044612
12-22 23:44:46.794 F/DEBUG (12441): x8 0000007ff1cc93d0 x9 0000000000000000 x10 000000000000002f x11 0000000000000038
12-22 23:44:46.794 F/DEBUG (12441): x12 0000000000000020 x13 0000000000000002 x14 0000000000000008 x15 000047544b6a35cd
12-22 23:44:46.794 F/DEBUG (12441): x16 000000723837eb60 x17 00000073231c2b2c x18 000000729694e210 x19 0000007ff1cc92c0
12-22 23:44:46.794 F/DEBUG (12441): x20 0000000000000000 x21 0000007325a0cc00 x22 0000007ff1cc9c10 x23 0000007297e12756
12-22 23:44:46.794 F/DEBUG (12441): x24 0000000000000008 x25 0000007325c9e020 x26 0000007325a0ccb0 x27 0000000000000002
12-22 23:44:46.794 F/DEBUG (12441): x28 0000007ff1cc99a0 x29 0000007ff1cc98e0
12-22 23:44:46.794 F/DEBUG (12441): sp 0000007ff1cc92c0 lr 0000007238353544 pc 0000007238353578
12-22 23:44:46.873 D/InvisionDisplayManagerService( 1375): applySetPowerState mPowerOn = true
12-22 23:44:46.874 E/InvisionDisplayManagerService( 1375): Failed to applySetPowerState ivslam = null
12-22 23:44:46.925 F/DEBUG (12441):
12-22 23:44:46.925 F/DEBUG (12441): backtrace:
12-22 23:44:46.926 F/DEBUG (12441): #00 pc 0000000000013578 /data/app/com.ssnwt.camera-hfkg6MwSsTZphlAHWYVnzw==/base.apk!libndk_camera.so (offset 0x651000) (NdkCamera::open(char const*, void ()(char const, unsigned char*, int, int, int, int, long), ANativeWindow**, int)+460) (BuildId: 953887cae84ec067885a1b0c7315d189686e353d)
12-22 23:44:46.926 F/DEBUG (12441): #01 pc 0000000000011980 /data/app/com.ssnwt.camera-hfkg6MwSsTZphlAHWYVnzw==/base.apk!libndk_camera.so (offset 0x651000) (Java_com_ssnwt_camera_NativeCameraActivity_openMultiCamera+468) (BuildId: 953887cae84ec067885a1b0c7315d189686e353d)
12-22 23:44:46.926 F/DEBUG (12441): #02 pc 000000000013f350 /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #03 pc 0000000000136334 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #04 pc 0000000000144fec /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #05 pc 00000000002e204c /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #06 pc 00000000002dd2ac /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+892) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #07 pc 00000000005a1538 /apex/com.android.runtime/lib64/libart.so (MterpInvokeDirect+424) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #08 pc 0000000000130914 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_direct+20) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #09 pc 0000000000003648 /data/app/com.ssnwt.camera-hfkg6MwSsTZphlAHWYVnzw==/base.apk (com.ssnwt.camera.NativeCameraActivity.lambda$initCameraView 2 2 2NativeCameraActivity+4)
12-22 23:44:46.926 F/DEBUG (12441): #10 pc 000000000059f4d0 /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1352) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #11 pc 0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #12 pc 0000000000002538 /data/app/com.ssnwt.camera-hfkg6MwSsTZphlAHWYVnzw==/base.apk (com.ssnwt.camera.-$ L a m b d a Lambda LambdaNativeCameraActivityKaTeX parse error: Expected 'EOF', got '#' at position 80: …(12441): #̲13 pc 000000000…MethodAndArgsCaller.run+22)
12-22 23:44:46.926 F/DEBUG (12441): #41 pc 00000000002b3360 /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.12500273830171183841+240) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #42 pc 0000000000590778 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1032) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #43 pc 000000000013f468 /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #44 pc 00000000009b947c /system/framework/arm64/boot-framework.oat (com.android.internal.os.ZygoteInit.main+2076) (BuildId: 487da90bd032b6e46119f4e28971201c8f9f3457)
12-22 23:44:46.926 F/DEBUG (12441): #45 pc 00000000001365b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #46 pc 000000000014500c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+276) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #47 pc 00000000004af0e4 /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: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #48 pc 00000000004aecd4 /apex/com.android.runtime/lib64/libart.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #49 pc 00000000003b9df4 /apex/com.android.runtime/lib64/libart.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+624) (BuildId: 8fb5976d4465346647f3e5e5870bdc7a)
12-22 23:44:46.926 F/DEBUG (12441): #50 pc 00000000000c199c /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, …)+116) (BuildId: a13dc1a4db7d246c6df31ff4e581e460)
12-22 23:44:46.926 F/DEBUG (12441): #51 pc 00000000000c4878 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vectorandroid::String8 const&, bool)+776) (BuildId: a13dc1a4db7d246c6df31ff4e581e460)
12-22 23:44:46.926 F/DEBUG (12441): #52 pc 00000000000035b0 /system/bin/app_process64 (main+1376) (BuildId: 1a86d090ff0d3dd5f5a671601a88a247)
12-22 23:44:46.926 F/DEBUG (12441): #53 pc 000000000007e898 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108) (BuildId: 21847aa9757f000b0461310a9f5e6e51)
12-22 23:44:46.958 D/SVR_AndroidInterface(12313): Android interface branch: master, version code: 20, version name: 1.0.20-6d81be3
部分信息展示了出错时的运行状态, 当前中断原因是收到SIGSEGV(通常 crash 也都是因为收到这个信号,也有少数是因为 SIGFPE,即除0操作)。
错误码是SEGV_MAPERR,常见的段错误。然后出错地址为 0000000000013578
。
说明:我们常见的信号有下面这些:
信号 | 码值 | 描述 |
---|---|---|
SIGILL | 4 | 非法指令,例如损坏的可执行文件或代码区损坏 |
SIGABRT | 6 | 通过C函数abort()发送;为assert()使用 |
SIGBUS | 7 | 不存在的物理地址,更多为硬件或系统引起 |
SIGFPE | 8 | 浮点数运算错误,如除0操作 |
SIGKILL | 9 | 迅速完全终止进程;不能被捕获 |
SIGSEGV | 11 | 段地址错误,例如空指针、野指针、数组越界等 |
- 最后在堆栈信息中找到 addr 表示栈地址。执行命令,示例如下:
C:\Users\DELL>addr2line.exe -C -f -e E:\demo\India\CameraTest\app\build\intermediates\cmake\debug\obj\arm64-v8a\libndk_camera.so 0000000000013578
NdkCamera::open(char const*, void (*)(char const*, unsigned char*, int, int, int, int, long), ANativeWindow**, int)
E:/demo/India/CameraTest/app/src/main/cpp/NdkCamera.cpp:77
于是问题得到了定位,在 NdkCamera.cpp:77
第 77行的代码有问题,引发了 crash。 于时反向定位去进一步分析。
注意点: so 文件需要用 debug 中的 so 文件, 不要用 dubug.apk 中解压后的 so 文件, 不然会出现???.无法识别到。
命令的参数说明:
参数
-a --addresses:在函数名、文件和行号信息之前,显示地址,以十六进制形式。
-b --target=<bfdname>:指定目标文件的格式为bfdname。
-e --exe=<executable>:指定需要转换地址的可执行文件名。
-i --inlines : 如果需要转换的地址是一个内联函数,则输出的信息包括其最近范围内的一个非内联函数的信息。
-j --section=<name>:给出的地址代表指定section的偏移,而非绝对地址。
-p --pretty-print:使得该函数的输出信息更加人性化:每一个地址的信息占一行。
-s --basenames:仅仅显示每个文件名的基址(即不显示文件的具体路径,只显示文件名)。
-f --functions:在显示文件名、行号输出信息的同时显示函数名信息。
-C --demangle[=style]:将低级别的符号名解码为用户级别的名字。 // 注意此处需要大写
-h --help:输出帮助信息。
-v --version:输出版本号。
参考
Android Native Crash 分析指南