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
2、ndk-stack (android-ndk-r8d\ndk-stack)
#相當於執行多次addr2line, 可以直接針對一份crash log使用,會輸出所有backtrace裡地址對應的symbol
Usage: ndk-stack –sym
3、 objdump (android-ndk-r8d\toolchains\arm-linux-androideabi-4.7\prebuilt\windows\bin)
#Dump the object file. 通過彙編程式碼定位錯誤的原因,大部分複雜的問題可以通過這種方式得到解決。
Usage: objdump -S
貼上一份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 工具就可以定位到問題位置。