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)); }
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_create2.4 google提供了一个python脚本,可以从 http://code.google.com/p/android-ndk-stacktrace-analyzer/ 下载这个python脚本,然后使用
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中,加入:
这样将会打印上面所述的调用信息,便于分析代码,debug,定位问题。