分享

跟踪Android callback 调用堆栈

 quasiceo 2014-09-04
分类: Android 2013-08-31 11:40 1648人阅读 评论(0) 收藏 举报

0 在调试 android  系统时可以通过打印调用堆栈 callback stack 来分析和解决android问题。

1 java 层打印callback stack 可以通过 catch exception 然后 使用 Log.w(LOGTAG, Log.getStackTraceString(throwable)) 打印调用堆栈

  1. Throwable throwable = new Throwable();     
  2.         Log.w(LOGTAG, Log.getStackTraceString(throwable));  
或者
  1. try {  
  2.     wait();  
  3. catch (InterruptedException e) {  
  4.     Log.e(LOGTAG, "Caught exception while waiting for overrideUrl");  
  5.     Log.e(LOGTAG, Log.getStackTraceString(e));  
  6. }  

2 c/c++, 通常情况下,可以通过segment fault 等错误即信号 SIGSEGV(11) 做出相应处理,即设置SIGSEGV的handler调用libc的backtrace,即可打印对于的callback stack;定位问题所在;但在android 中, bionic 不提供类似功能,而且只能通过logcat才能看到log信息,但是我们也可以根据android出错信息获得调用堆栈信息,如以下出错信息:
  1. D/CallStack( 2029): #00  pc 00008156  /system/lib/hw/audio.primary.tf4.so  
  2. D/CallStack( 2029): #01  pc 000089e8  /system/lib/hw/audio.primary.tf4.so (android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String const&)+139)  
  3. D/CallStack( 2029): #02  pc 0000b2ca  /system/lib/hw/audio.primary.tf4.so  
  4. D/CallStack( 2029): #03  pc 0003ac6a  /system/lib/libaudioflinger.so (android::AudioFlinger::MixerThread::checkForNewParameters_l()+377)  
  5. D/CallStack( 2029): #04  pc 0003960a  /system/lib/libaudioflinger.so (android::AudioFlinger::PlaybackThread::threadLoop()+145)  
  6. D/CallStack( 2029): #05  pc 00011264  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)  
  7. D/CallStack( 2029): #06  pc 00010dca  /system/lib/libutils.so  
  8. D/CallStack( 2029): #07  pc 0000e3f8  /system/lib/libc.so (__thread_entry+72)  
  9. 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 必须转换成以下格式

  1. D/CallStack( 2029): #00  pc 00008156  /system/lib/hw/audio.primary.tf4.so  
  2. D/CallStack( 2029): #01  pc 000089e8  /system/lib/hw/audio.primary.tf4.so  
  3. D/CallStack( 2029): #02  pc 0000b2ca  /system/lib/hw/audio.primary.tf4.so  
  4. D/CallStack( 2029): #03  pc 0003ac6a  /system/lib/libaudioflinger.so  
  5. D/CallStack( 2029): #04  pc 0003960a  /system/lib/libaudioflinger.so  
  6. D/CallStack( 2029): #05  pc 00011264  /system/lib/libutils.so  
  7. D/CallStack( 2029): #06  pc 00010dca  /system/lib/libutils.so  
  8. D/CallStack( 2029): #07  pc 0000e3f8  /system/lib/libc.so  
  9. D/CallStack( 2029): #08  pc 0000dae4  /system/lib/libc.so  
这样执行脚本 ./panic.py setincallpath_l.txt, 打印定位信息:
  1. w@w:/meizu/JellyBean-4.2.1/trunk/out/target/product/tf4$ ./panic.py ./backtrack/setincall_path.txt  
  2. read file ok  
  3. AudioHardware.cpp:829         android_audio_legacy::AudioHardware::setIncallPath_l(unsigned int)  
  4. AudioHardware.cpp:1537        android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String8 const&)  
  5. audio_hw_hal.cpp:197          out_set_parametersAudioFlinger.cpp:3535         android::AudioFlinger::MixerThread::checkForNewParameters_l()  
  6. AudioFlinger.cpp:2586         android::AudioFlinger::PlaybackThread::threadLoop()  
  7. Threads.cpp:793               android::Thread::_threadLoop(void*)  
  8. Threads.cpp:132               thread_data_t::trampoline(thread_data_t const*)  
  9. pthread.c:204                 __thread_entry  
  10. 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 需要设置环境,如下:

  1. #!/usr/bin/python  
  2. # stack symbol parser  
  3.   
  4. import os  
  5. import string  
  6. import sys  
  7.   
  8. #define android product name  
  9. #ANDROID_PRODUCT_NAME = 'generic'  
  10. ANDROID_PRODUCT_NAME = 'ok'  
  11.   
  12. ANDROID_WORKSPACE = os.getcwd()+"/"  
  13.   
  14. # addr2line tool path and symbol path  
  15. addr2line_tool = 'arm-linux-addr2line'  
  16. symbol_dir = ANDROID_WORKSPACE + '/symbols'  
  17. symbol_bin = symbol_dir + '/system/bin/'  
  18. symbol_lib = symbol_dir + '/system/lib/'  
  19.   
  20. class ReadLog:  
  21.     def __init__(self,filename):  
  22.         self.logname = filename  
  23.     def parse(self):  
  24.         f = file(self.logname,'r')  
  25.         lines = f.readlines()  
  26.         if lines != []:  
  27.             print 'read file ok'  
  28.         else:  
  29.             print 'read file failed'  
  30.         result =[]  
  31.         for line in lines:  
  32.             if line.find('stack') != -1:  
  33.                 print 'stop search'  
  34.                 break  
  35.             elif line.find('system') != -1:  
  36.                 #print 'find one item' + line  
  37.                 result.append(line)  
  38.         return result  
  39.   
  40. class ParseContent:  
  41.     def __init__(self,addr,lib):  
  42.             self.address = addr # pc address  
  43.             self.exename = lib  # executable or shared library  
  44.     def addr2line(self):  
  45.         cmd = addr2line_tool + " -C -f -s -e " + symbol_dir + self.exename + " " + self.address  
  46.         #print cmd  
  47.         stream = os.popen(cmd)  
  48.         lines = stream.readlines();  
  49.         list = map(string.strip,lines)  
  50.         return list  
  51.       
  52. inputarg = sys.argv  
  53. if len(inputarg) < 2:  
  54.     print 'Please input panic log'  
  55.     exit()  
  56.   
  57. filename = inputarg[1]  
  58. readlog = ReadLog(filename)  
  59. inputlist = readlog.parse()  
  60.   
  61. for item in inputlist:  
  62.     itemsplit = item.split()  
  63.     test = ParseContent(itemsplit[-2],itemsplit[-1])  
  64.     list = test.addr2line()  
  65.     print "%-30s%s" % (list[1],list[0])  
3 出了上述系统主动输出出错信息,我们还可以通过代码在系统不出错的情况下,打印调用信息,然后通过panic.py 打印调用堆栈

3.1 在cpp文件添加如下信息

  1. #include <utils/CallStack.h>  
  2. ...  
  3. status_t AudioHardware::setIncallPath_l(uint32_t device) {  
  4. ...  
  5. #ifdef _ARM_  
  6.     android::CallStack stack;  
  7.     stack.update(1, 100);  
  8.     stack.dump("");  
  9. #endif  
  10. ...  
  11. }  
3.2 在Android.mk中,加入:
    LOCAL_CFLAGS += -D_ARM_
    LOCAL_SHARED_LIBRARIES += libutils

这样将会打印上面所述的调用信息,便于分析代码,debug,定位问题。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多