Android ndk开发,出现内存溢出或别的问题需要调试时,如何快速定位到源码位置,可以使用addr2line 和 ndk-stack 两个工具。在程序
内容主要分为一下几个部分:
1.Library Symbols (共享库的符号)
2.Analyze Tools (可用到的分析工具)
3.CrashLog – Header
4.CrashLog – Backtrace(For most crashes)
5.CrashLog – Registers
6.CrashLog – Memory
7.CrashLog – Stack
8.Library Base Address (共享库在内存中基地址)
1.Library Symbols (共享库的符号)
ndk提供了一些工具可以供程序员直接获取到出错的文件,函数以及行数。 但是这部分工具都需要没有去符号的共享库(通常是放在main/obj/local/armeabi-v7a)。而main/libs/armeabi-v7a中的共享库是去掉了符号的,所以直接从设备上抓下来的lib是不能够通过工具来找到对应的符号(而且没有去symbol的库比去掉的空间占用会大许多)。所以如果想要分析一份native crash,那么unstripped lib几乎不可缺少,但是即使是strip过的库也同样会包含少量的symbol。
2.Analyze Tools
即常用的辅助工具
1、addr2line ((ANDROID_NDK)\toolchains\arm-linux-androideabi-4.7\prebuilt\windows\bin)
#通过backtrace一栏提供的地址查询对应的符号,可以定位到文件,函数,行数.
Usage: addr2line –aCfe libs(trace_address)
2、ndk-stack (android-ndk-r8d\ndk-stack)
#相当于执行多次addr2line, 可以直接针对一份crash log使用,会输出所有backtrace里地址对应的symbol
Usage: ndk-stack –sym (libdirectory)–dump (crash_log_file)
3、 objdump (android-ndk-r8d\toolchains\arm-linux-androideabi-4.7\prebuilt\windows\bin)
#Dump the object file. 通过汇编代码定位错误的原因,大部分复杂的问题可以通过这种方式得到解决。
Usage: objdump -S (objfile)> (output_file)
贴上一份crash log ,app crash时,打开android device moniter 查看log 找到 如下log
06-15 14:23:38.335: I/DEBUG(631): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-15 14:23:38.335: I/DEBUG(631): Build fingerprint: 'Xiaomi/hermes/hermes:5.0.2/LRX22G/V7.3.2.0.LHMCNDD:user/release-keys'
06-15 14:23:38.335: I/DEBUG(631): Revision: '0'
06-15 14:23:38.335: I/DEBUG(631): ABI: 'arm'
06-15 14:23:38.335: I/DEBUG(631): pid: 30939, tid: 612, name: Thread-221 >>> sdk.live.com.xysdk <<<
06-15 14:23:38.335: I/DEBUG(631): signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
06-15 14:23:38.355: V/AudioTrackShared(268): mAvailToClient=1024 stepCount=1024 minimum=1536
06-15 14:23:38.359: I/DEBUG(631): Abort message: 'art/runtime/thread.cc:1115] No pending exception expected: android.media.MediaCodec$CodecException: Error 0x80001001'
06-15 14:23:38.359: I/DEBUG(631): r0 00000000 r1 00000264 r2 00000006 r3 00000000
06-15 14:23:38.359: I/DEBUG(631): r4 e0574dd8 r5 00000006 r6 00000000 r7 0000010c
06-15 14:23:38.359: I/DEBUG(631): r8 00000000 r9 ab3150b0 sl ab7909f8 fp 00000001
06-15 14:23:38.359: I/DEBUG(631): ip 00000264 sp e0574878 lr f71fa4df pc f7220094 cpsr 60070010
06-15 14:23:38.359: I/DEBUG(631): backtrace:
06-15 14:23:38.359: I/DEBUG(631): #00 pc 0003c094 /system/lib/libc.so (tgkill+12)
06-15 14:23:38.359: I/DEBUG(631): #01 pc 000164db /system/lib/libc.so (pthread_kill+66)
06-15 14:23:38.359: I/DEBUG(631): #02 pc 000170a7 /system/lib/libc.so (raise+10)
06-15 14:23:38.359: I/DEBUG(631): #03 pc 00013997 /system/lib/libc.so (__libc_android_abort+34)
06-15 14:23:38.360: I/DEBUG(631): #04 pc 000120c8 /system/lib/libc.so (abort+4)
06-15 14:23:38.360: I/DEBUG(631): #05 pc 002179a9 /system/lib/libart.so (art::Runtime::Abort()+132)
06-15 14:23:38.360: I/DEBUG(631): #06 pc 000a6e0d /system/lib/libart.so (art::LogMessage::~LogMessage()+1292)
06-15 14:23:38.360: I/DEBUG(631): #07 pc 0022883f /system/lib/libart.so (art::Thread::AssertNoPendingException() const+350)
06-15 14:23:38.360: I/DEBUG(631): #08 pc 000d3093 /system/lib/libart.so (art::ClassLinker::FindClass(art::Thread*, char const*, art::Handle)+30)
06-15 14:23:38.360: I/DEBUG(631): #09 pc 000d502d /system/lib/libart.so (art::ClassLinker::ResolveType(art::DexFile const&, unsigned short, art::Handle, art::Handle)+136)
06-15 14:23:38.360: I/DEBUG(631): #10 pc 00070ba5 /system/lib/libart.so (_ZN3art11ClassLinker11ResolveTypeEtPNS_6mirror9ArtMethodE.part.112+104)
06-15 14:23:38.360: I/DEBUG(631): #11 pc 0015f561 /system/lib/libart.so (art::MethodHelper::GetClassFromTypeIdx(unsigned short, bool)+104)
06-15 14:23:38.360: I/DEBUG(631): #12 pc 0007277f /system/lib/libart.so (art::CheckMethodArguments(art::mirror::ArtMethod*, unsigned int*)+190)
06-15 14:23:38.360: I/DEBUG(631): #13 pc 00214d75 /system/lib/libart.so (art::InvokeVirtualOrInterfaceWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408)
06-15 14:23:38.360: I/DEBUG(631): #14 pc 0019f8e7 /system/lib/libart.so (art::JNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+274)
06-15 14:23:38.360: I/DEBUG(631): #15 pc 000baedb /system/lib/libart.so (art::CheckJNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+90)
06-15 14:23:38.360: I/DEBUG(631): #16 pc 000cd17c /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...)+52)
**06-15 14:23:38.360: I/DEBUG(631): #17 pc 000d1a78** /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (JMediacodecDecoder::decoder(unsigned char*, int, unsigned int)+128)
06-15 14:23:38.360: I/DEBUG(631): #18 pc 000d0a38 /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (MediaStreamReceiver::VideoDecoderFunc(void*)+124)
06-15 14:23:38.360: I/DEBUG(631): #19 pc 00015c5b /system/lib/libc.so (__pthread_start(void*)+30)
06-15 14:23:38.360: I/DEBUG(631): #20 pc 00013ceb /system/lib/libc.so (__start_thread+6)
3.Crash Log - Header
信息头,包含当前系统版本有关的信息,如果是做平台级的开发,这将有助于定位当前的系统的开发版本。
I/DEBUG(631): Build fingerprint: 'Xiaomi/hermes/hermes:5.0.2/LRX22G/V7.3.2.0.LHMCNDD:user/release-keys'
06-15 14:23:38.335: I/DEBUG(631): Revision: '0'
06-15 14:23:38.335: I/DEBUG(631): ABI: 'arm'
06-15 14:23:38.335: I/DEBUG(631): pid: 30939, tid: 612, name: Thread-221 >>> sdk.live.com.xysdk <<<
4.CrashLog – Backtrace(For most crashes)
看上面log
06-15 14:23:38.359: I/DEBUG(631): backtrace:
06-15 14:23:38.359: I/DEBUG(631): #00 pc 0003c094 /system/lib/libc.so (tgkill+12)
06-15 14:23:38.359: I/DEBUG(631): #01 pc 000164db /system/lib/libc.so (pthread_kill+66)
06-15 14:23:38.359: I/DEBUG(631): #02 pc 000170a7 /system/lib/libc.so (raise+10)
06-15 14:23:38.359: I/DEBUG(631): #03 pc 00013997 /system/lib/libc.so (__libc_android_abort+34)
从上面这份backtrace可以看到包含一个pc地址和后面的symbol。部分错误可以通过只看这里的symbol发现问题所在。而如果想要更准确的定位,则需要借助ndk工具。 查看 pc d1a78
$ ~/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line -aCfe obj/local/armeabi-v7a/libxiaoyao_live.so d1a78
0x000d1a78
如下是输出结果:定位到源码的位置
JMediacodecDecoder::decoder(unsigned char*, int, unsigned int)
/Users/apple/ycWorkSpace/workspace/kakaPushTemp/XYsdk/app/src/main/jni/src/android/mediacodec/JMediacodecDecoder.cpp:191
ndk-stack:
~/Library/Android/sdk/ndk-bundle/ndk-stack -sym obj/local/armeabi-v7a/libxiaoyao_live.so -dump ~/log.txt
其分析结果如下:
********** Crash dump: **********
Build fingerprint: 'Xiaomi/hermes/hermes:5.0.2/LRX22G/V7.3.2.0.LHMCNDD:user/release-keys'
pid: 30939, tid: 612, name: Thread-221 >>> sdk.live.com.xysdk <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Stack frame #00 pc 0003c094 /system/lib/libc.so (tgkill+12)
Stack frame #01 pc 000164db /system/lib/libc.so (pthread_kill+66)
Stack frame #02 pc 000170a7 /system/lib/libc.so (raise+10)
Stack frame #03 pc 00013997 /system/lib/libc.so (__libc_android_abort+34)
Stack frame #04 pc 000120c8 /system/lib/libc.so (abort+4)
Stack frame #05 pc 002179a9 /system/lib/libart.so (art::Runtime::Abort()+132)
Stack frame #06 pc 000a6e0d /system/lib/libart.so (art::LogMessage::~LogMessage()+1292)
Stack frame #07 pc 0022883f /system/lib/libart.so (art::Thread::AssertNoPendingException() const+350)
Stack frame #08 pc 000d3093 /system/lib/libart.so (art::ClassLinker::FindClass(art::Thread*, char const*, art::Handle)+30)
Stack frame #09 pc 000d502d /system/lib/libart.so (art::ClassLinker::ResolveType(art::DexFile const&, unsigned short, art::Handle, art::Handle)+136)
Stack frame #10 pc 00070ba5 /system/lib/libart.so (_ZN3art11ClassLinker11ResolveTypeEtPNS_6mirror9ArtMethodE.part.112+104)
Stack frame #11 pc 0015f561 /system/lib/libart.so (art::MethodHelper::GetClassFromTypeIdx(unsigned short, bool)+104)
Stack frame #12 pc 0007277f /system/lib/libart.so (art::CheckMethodArguments(art::mirror::ArtMethod*, unsigned int*)+190)
Stack frame #13 pc 00214d75 /system/lib/libart.so (art::InvokeVirtualOrInterfaceWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408)
Stack frame #14 pc 0019f8e7 /system/lib/libart.so (art::JNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+274)
Stack frame #15 pc 000baedb /system/lib/libart.so (art::CheckJNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+90)
Stack frame #16 pc 000cd17c /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...)+52): Routine _JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...) at /Users/apple/Library/Android/sdk/ndk-bundle/platforms/android-17/arch-arm/usr/include/jni.h:650
Stack frame #17 pc 000d1a78 /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (JMediacodecDecoder::decoder(unsigned char*, int, unsigned int)+128): Routine JMediacodecDecoder::decoder(unsigned char*, int, unsigned int) at /Users/apple/ycWorkSpace/workspace/kakaPushTemp/XYsdk/app/src/main/jni/src/android/mediacodec/JMediacodecDecoder.cpp:191
Stack frame #18 pc 000d0a38 /data/app/sdk.live.com.xysdk-1/lib/arm/libxiaoyao_live.so (MediaStreamReceiver::VideoDecoderFunc(void*)+124): Routine MediaStreamReceiver::VideoDecoderFunc(void*) at /Users/apple/ycWorkSpace/workspace/kakaPushTemp/XYsdk/app/src/main/jni/src/android/look/networkStreamReceiver/MediaStreamReceiver.cpp:230
Stack frame #19 pc 00015c5b /system/lib/libc.so (__pthread_start(void*)+30)
Stack frame #20 pc 00013ceb /system/lib/libc.so (__start_thread+6)
5.CrashLog – Registers
寄存器信息,可以通过这部分信息基本确定系统为什么会错。
06-15 14:23:38.359: I/DEBUG(631): r0 00000000 r1 00000264 r2 00000006 r3 00000000
06-15 14:23:38.359: I/DEBUG(631): r4 e0574dd8 r5 00000006 r6 00000000 r7 0000010c
06-15 14:23:38.359: I/DEBUG(631): r8 00000000 r9 ab3150b0 sl ab7909f8 fp 00000001
06-15 14:23:38.359: I/DEBUG(631): ip 00000264 sp e0574878 lr f71fa4df pc f7220094 cpsr 60070010
这部分信息展示了出错时的运行状态, 当前中断原因是收到SIGSEGV(通常crash也都是因为收到这个信号,也有少数是因为SIGFPE,即除0操作)。错误码是SEGV_MAPERR,常见的段错误。
~/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump -S obj/local/armeabi-v7a/objs/xiaoyao_live/android/mediacodec/JMediacodecDecoder.o > ~/demo
反汇编,把.o 文件反编译为.s 文件
基本上配合log 及 addr2line 工具就可以定位到问题位置。