1. 程式人生 > >android studio ndk 除錯技巧

android studio ndk 除錯技巧

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<art::mirror::ClassLoader>)+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::mirror::DexCache>, art::Handle<art::mirror::ClassLoader>)+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<art::mirror::ClassLoader>)+30)
Stack frame #09 pc 000d502d  /system/lib/libart.so (art::ClassLinker::ResolveType(art::DexFile const&, unsigned short, art::Handle<art::mirror::DexCache>, art::Handle<art::mirror::ClassLoader>)+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 工具就可以定位到問題位置。