0 在调试 android 系统时可以通过打印调用堆栈 callback stack 来分析和解决android问题。
1 java 层打印callback stack 可以通过 catch exception 然后 使用 Log.w(LOGTAG, Log.getStackTraceString(throwable)) 打印调用堆栈
- Throwable throwable = new Throwable();
- Log.w(LOGTAG, Log.getStackTraceString(throwable));
或者
- try {
- wait();
- } catch (InterruptedException e) {
- Log.e(LOGTAG, "Caught exception while waiting for overrideUrl");
- Log.e(LOGTAG, Log.getStackTraceString(e));
- }
2 c/c++, 通常情况下,可以通过segment fault 等错误即信号 SIGSEGV(11) 做出相应处理,即设置SIGSEGV的handler调用libc的backtrace,即可打印对于的callback stack;定位问题所在;但在android 中, bionic 不提供类似功能,而且只能通过logcat才能看到log信息,但是我们也可以根据android出错信息获得调用堆栈信息,如以下出错信息:
- D/CallStack( 2029): #00 pc 00008156 /system/lib/hw/audio.primary.tf4.so
- D/CallStack( 2029): #01 pc 000089e8 /system/lib/hw/audio.primary.tf4.so (android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String const&)+139)
- D/CallStack( 2029): #02 pc 0000b2ca /system/lib/hw/audio.primary.tf4.so
- D/CallStack( 2029): #03 pc 0003ac6a /system/lib/libaudioflinger.so (android::AudioFlinger::MixerThread::checkForNewParameters_l()+377)
- D/CallStack( 2029): #04 pc 0003960a /system/lib/libaudioflinger.so (android::AudioFlinger::PlaybackThread::threadLoop()+145)
- D/CallStack( 2029): #05 pc 00011264 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
- D/CallStack( 2029): #06 pc 00010dca /system/lib/libutils.so
- D/CallStack( 2029): #07 pc 0000e3f8 /system/lib/libc.so (__thread_entry+72)
- D/CallStack( 2029): #08 pc 0000dae4 /system/lib/libc.so (pthread_create+160)
2.1 可以使用 arm-linux-addr2line 获得调用堆栈,arm-eabi-addr2line -C -f -e symbols/system/lib/*.so addr, 这样就可以打印出 调用堆栈信息了。
2.2 使用ndk-stack 工具, 保存出错log为 logcat.log, cat logcat..log | ndk-stack -sym ~/[SOURCE-DIR]/out/target/product/[PROJECT]/symbols/system/lib/ 打印调用堆栈
2.3 使用 panic.py(代码在下面) 脚本分析并打印调用堆栈,/panic.py logcat.log, 注意logcat 必须转换成以下格式
- D/CallStack( 2029): #00 pc 00008156 /system/lib/hw/audio.primary.tf4.so
- D/CallStack( 2029): #01 pc 000089e8 /system/lib/hw/audio.primary.tf4.so
- D/CallStack( 2029): #02 pc 0000b2ca /system/lib/hw/audio.primary.tf4.so
- D/CallStack( 2029): #03 pc 0003ac6a /system/lib/libaudioflinger.so
- D/CallStack( 2029): #04 pc 0003960a /system/lib/libaudioflinger.so
- D/CallStack( 2029): #05 pc 00011264 /system/lib/libutils.so
- D/CallStack( 2029): #06 pc 00010dca /system/lib/libutils.so
- D/CallStack( 2029): #07 pc 0000e3f8 /system/lib/libc.so
- D/CallStack( 2029): #08 pc 0000dae4 /system/lib/libc.so
这样执行脚本 ./panic.py setincallpath_l.txt, 打印定位信息:
- w@w:/meizu/JellyBean-4.2.1/trunk/out/target/product/tf4$ ./panic.py ./backtrack/setincall_path.txt
- read file ok
- AudioHardware.cpp:829 android_audio_legacy::AudioHardware::setIncallPath_l(unsigned int)
- AudioHardware.cpp:1537 android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String8 const&)
- audio_hw_hal.cpp:197 out_set_parametersAudioFlinger.cpp:3535 android::AudioFlinger::MixerThread::checkForNewParameters_l()
- AudioFlinger.cpp:2586 android::AudioFlinger::PlaybackThread::threadLoop()
- Threads.cpp:793 android::Thread::_threadLoop(void*)
- Threads.cpp:132 thread_data_t::trampoline(thread_data_t const*)
- pthread.c:204 __thread_entry
- pthread.c:348 pthread_create
2.4 google提供了一个python脚本,可以从 http://code.google.com/p/android-ndk-stacktrace-analyzer/ 下载这个python脚本,然后使用
adb logcat -d > logfile 导出 crash 的log,
使用 arm-eabi-objdump (位于build/prebuilt/linux-x86/arm-eabi-4.2.1/bin下面)把so或exe转换成汇编代码,如:
arm-eabi-objdump -S mylib.so > mylib.asm,
然后使用脚本
python parse_stack.py <asm-file> <logcat-file>
panic.py 需要设置环境,如下:
- #!/usr/bin/python
- # stack symbol parser
-
- import os
- import string
- import sys
-
- #define android product name
- #ANDROID_PRODUCT_NAME = 'generic'
- ANDROID_PRODUCT_NAME = 'ok'
-
- ANDROID_WORKSPACE = os.getcwd()+"/"
-
- # addr2line tool path and symbol path
- addr2line_tool = 'arm-linux-addr2line'
- symbol_dir = ANDROID_WORKSPACE + '/symbols'
- symbol_bin = symbol_dir + '/system/bin/'
- symbol_lib = symbol_dir + '/system/lib/'
-
- class ReadLog:
- def __init__(self,filename):
- self.logname = filename
- def parse(self):
- f = file(self.logname,'r')
- lines = f.readlines()
- if lines != []:
- print 'read file ok'
- else:
- print 'read file failed'
- result =[]
- for line in lines:
- if line.find('stack') != -1:
- print 'stop search'
- break
- elif line.find('system') != -1:
- #print 'find one item' + line
- result.append(line)
- return result
-
- class ParseContent:
- def __init__(self,addr,lib):
- self.address = addr # pc address
- self.exename = lib # executable or shared library
- def addr2line(self):
- cmd = addr2line_tool + " -C -f -s -e " + symbol_dir + self.exename + " " + self.address
- #print cmd
- stream = os.popen(cmd)
- lines = stream.readlines();
- list = map(string.strip,lines)
- return list
-
- inputarg = sys.argv
- if len(inputarg) < 2:
- print 'Please input panic log'
- exit()
-
- filename = inputarg[1]
- readlog = ReadLog(filename)
- inputlist = readlog.parse()
-
- for item in inputlist:
- itemsplit = item.split()
- test = ParseContent(itemsplit[-2],itemsplit[-1])
- list = test.addr2line()
- print "%-30s%s" % (list[1],list[0])
3 出了上述系统主动输出出错信息,我们还可以通过代码在系统不出错的情况下,打印调用信息,然后通过panic.py 打印调用堆栈
3.1 在cpp文件添加如下信息
- #include <utils/CallStack.h>
- ...
- status_t AudioHardware::setIncallPath_l(uint32_t device) {
- ...
- #ifdef _ARM_
- android::CallStack stack;
- stack.update(1, 100);
- stack.dump("");
- #endif
- ...
- }
3.2 在Android.mk中,加入:
LOCAL_CFLAGS += -D_ARM_
LOCAL_SHARED_LIBRARIES += libutils
这样将会打印上面所述的调用信息,便于分析代码,debug,定位问题。