Android下打印调试堆栈方法(转)

敢说敢作敢为, 无怨无恨无悔。这篇文章主要讲述Android下打印调试堆栈方法(转)相关的知识,希望能为你提供帮助。
转自:http://blog.csdn.net/freshui/article/details/9456889
 
打印堆栈是调试的常用方法,一般在系统异常时,我们可以将异常情况下的堆栈打印出来,这样十分方便错误查找。实际上还有另外一个非常有用的功能:分析代码的行为。Android代码太过庞大复杂了,完全的静态分析经常是无从下手,因此通过打印堆栈的动态分析也十分必要。
 
android打印堆栈的方法,简单归类一下 
1. zygote的堆栈dump
实际上这个可以同时dump  Java线程及native线程的堆栈,对于java线程,java堆栈和native堆栈都可以得到。
使用方法很简单,直接在adb shell或串口中输入:
 
[plain]  view plain  copy    

Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. kill  -3  < pid>    
输出的trace会保存在 /data/anr/traces.txt文件中。这个需要注意,如果没有 /data/anr/这个目录或/data/anr/traces.txt这个文件,需要手工创建一下,并设置好读写权限。
 
 
如果需要在代码中,更容易控制堆栈的输出时机,可以用以下命令获取zygote的core dump:
[java]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. Process.sendSignal(pid,  Process.SIGNAL_QUIT);    
原理和命令行是一样的。
不过需要注意两点:
  1. adb shell可能会没有权限,需要root。
  2. android 4.2中关闭了native thread的堆栈打印,详见 dalvik/vm/Thread.cpp的dumpNativeThread方法:
[cpp]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. dvmPrintDebugMessage(target,   
  2.         "\"%s\"  sysTid=%d  nice=%d  sched=%d/%d  cgrp=%s\n",   
  3.         name,  tid,  getpriority(PRIO_PROCESS,  tid),   
  4.         schedStats.policy,  schedStats.priority,  schedStats.group);    
  5. dumpSchedStat(target,  tid);    
  6. //  Temporarily  disabled  collecting  native  stacks  from  non-Dalvik   
  7. //  threads  because  sometimes  they  misbehave.   
  8. //dvmDumpNativeStack(target,  tid);    
【Android下打印调试堆栈方法(转)】Native堆栈的打印被关掉了!不过对于大多数情况,可以直接将这个注释打开。
2. debuggerd的堆栈dump
debuggerd是android的一个daemon进程,负责在进程异常出错时,将进程的运行时信息dump出来供分析。debuggerd生成的coredump数据是以文本形式呈现,被保存在 /data/tombstone/ 目录下(名字取的也很形象,tombstone是墓碑的意思),共可保存10个文件,当超过10个时,会覆盖重写最早生成的文件。从4.2版本开始,debuggerd同时也是一个实用工具:可以在不中断进程执行的情况下打印当前进程的native堆栈。使用方法是:
 
[plain]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. debuggerd  -b  < pid>    
这可以协助我们分析进程执行行为,但最最有用的地方是:它可以非常简单的定位到native进程中锁死或错误逻辑引起的死循环的代码位置。
 
 
3. java代码中打印堆栈
Java代码打印堆栈比较简单, 堆栈信息获取和输出,都可以通过Throwable类的方法实现。目前通用的做法是在java进程出现需要注意的异常时,打印堆栈,然后再决定退出或挽救。通常的方法是使用exception的printStackTrace()方法:
 
[java]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. try  {   
  2.   ...   
  3. }  catch  (RemoteException  e)  {   
  4.     e.printStackTrace();    
  5.     ...   
  6. }   
 
 
当然也可以只打印堆栈不退出,这样就比较方便分析代码的动态运行情况。Java代码中插入堆栈打印的方法如下:
[java]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. Log.d(TAG,Log.getStackTraceString(new  Throwable()));    
 
4. C++代码中打印堆栈
 
C++也是支持异常处理的,异常处理库中,已经包含了获取backtrace的接口,Android也是利用这个接口来打印堆栈信息的。在Android的C++中,已经集成了一个工具类CallStack,在libutils.so中。使用方法:
[cpp]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. #include  < utils/CallStack.h>    
  2. ...   
  3. CallStack  stack;    
  4. stack.update();    
  5. stack.dump();      
使用方式比较简单。目前Andoid4.2版本已经将相关信息解析的很到位,符号表查找,demangle,偏移位置校正都做好了。
[plain]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1.      
5. C代码中打印堆栈
C代码,尤其是底层C库,想要看到调用的堆栈信息,还是比较麻烦的。 CallStack肯定是不能用,一是因为其实C++写的,需要重新封装才能在C中使用,二是底层库反调上层库的函数,会造成链接器循环依赖而无法链接。不过也不是没有办法,可以通过android工具类CallStack实现中使用的unwind调用及符号解析函数来处理。
这里需要注意的是,为解决链接问题,最好使用dlopen方式,查找需要用到的接口再直接调用,这样会比较简单。如下为相关的实现代码,只需要在要打印的文件中插入此部分代码,然后调用getCallStack()即可,无需包含太多的头文件和修改Android.mk文件:
[cpp]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. #define  MAX_DEPTH                                              31   
  2. #define  MAX_BACKTRACE_LINE_LENGTH      800   
  3. #define  PATH  "/system/lib/libcorkscrew.so"   
  4.    
  5. typedef  ssize_t  (*unwindFn)(backtrace_frame_t*,  size_t,  size_t);    
  6. typedef  void  (*unwindSymbFn)(const  backtrace_frame_t*,  size_t,  backtrace_symbol_t*);    
  7. typedef  void  (*unwindSymbFreeFn)(backtrace_symbol_t*,  size_t);    
  8.    
  9. static  void  *gHandle  =  NULL;    
  10.    
  11. static  int  getCallStack(void){   
  12.         ssize_t  i  =  0;    
  13.         ssize_t  result  =  0;    
  14.         ssize_t  count;    
  15.         backtrace_frame_t  mStack[MAX_DEPTH];    
  16.         backtrace_symbol_t  symbols[MAX_DEPTH];    
  17.    
  18.         unwindFn  unwind_backtrace  =  NULL;    
  19.         unwindSymbFn  get_backtrace_symbols  =  NULL;    
  20.         unwindSymbFreeFn  free_backtrace_symbols  =  NULL;    
  21.    
  22.         //  open  the  so.   
  23.         if(gHandle  ==  NULL)  gHandle  =  dlopen(PATH,  RTLD_NOW);    
  24.    
  25.         //  get  the  interface  for  unwind  and  symbol  analyse   
  26.         if(gHandle  !=  NULL)  unwind_backtrace  =  (unwindFn)dlsym(gHandle,  "unwind_backtrace");    
  27.         if(gHandle  !=  NULL)  get_backtrace_symbols  =  (unwindSymbFn)dlsym(gHandle,  "get_backtrace_symbols");    
  28.         if(gHandle  !=  NULL)  free_backtrace_symbols  =  (unwindSymbFreeFn)dlsym(gHandle,  "free_backtrace_symbols");    
  29.    
  30.         if(!gHandle  ||!unwind_backtrace  ||!get_backtrace_symbols  ||  !free_backtrace_symbols    ){   
  31.                 ALOGE("Error!  cannot  get  unwind  info:  handle:%p  %p  %p  %p",   
  32.                         gHandle,  unwind_backtrace,  get_backtrace_symbols,  free_backtrace_symbols  );    
  33.                 return  result;    
  34.         }   
  35.    
  36.         count=  unwind_backtrace(mStack,  1,  MAX_DEPTH);    
  37.         get_backtrace_symbols(mStack,  count,  symbols);    
  38.    
  39.         for  (i  =  0;   i  <   count;   i++)  {   
  40.                 char  line[MAX_BACKTRACE_LINE_LENGTH];    
  41.    
  42.                 const  char*  mapName  =  symbols[i].map_name  ?  symbols[i].map_name  :  "< unknown> ";    
  43.                 const  char*  symbolName  =symbols[i].demangled_name  ?  symbols[i].demangled_name  :  symbols[i].symbol_name;    
  44.                 size_t  fieldWidth  =  (MAX_BACKTRACE_LINE_LENGTH  -  80)  /  2;    
  45.                    
  46.                 if  (symbolName)  {   
  47.                         uint32_t  pc_offset  =  symbols[i].relative_pc  -  symbols[i].relative_symbol_addr;    
  48.                         if  (pc_offset)  {   
  49.                                 snprintf(line,  MAX_BACKTRACE_LINE_LENGTH,  "#%02d    pc  %08x    %.*s  (%.*s+%u)",   
  50.                                                 i,  symbols[i].relative_pc,  fieldWidth,  mapName,   
  51.                                                 fieldWidth,  symbolName,  pc_offset);    
  52.                         }  else  {   
  53.                                 snprintf(line,  MAX_BACKTRACE_LINE_LENGTH,  "#%02d    pc  %08x    %.*s  (%.*s)",   
  54.                                                 i,  symbols[i].relative_pc,  fieldWidth,  mapName,   
  55.                                                 fieldWidth,  symbolName);    
  56.                         }   
  57.                 }  else  {   
  58.                         snprintf(line,  MAX_BACKTRACE_LINE_LENGTH,  "#%02d    pc  %08x    %.*s",   
  59.                                         i,  symbols[i].relative_pc,  fieldWidth,  mapName);    
  60.                 }   
  61.    
  62.                 ALOGD("%s",  line);    
  63.         }   
  64.    
  65.         free_backtrace_symbols(symbols,  count);    
  66.    
  67.         return  result;    
  68. }   
对sched_policy.c的堆栈调用分析如下,注意具体是否要打印,在哪里打印,还可以通过pid、uid、property等来控制一下,这样就不会被淹死在trace的汪洋大海中。
[plain]  view plain  copy    
Android下打印调试堆栈方法(转)

文章图片
Android下打印调试堆栈方法(转)

文章图片
  1. D/SchedPolicy(  1350):  #00    pc  0000676c    /system/lib/libcutils.so   
  2. D/SchedPolicy(  1350):  #01    pc  00006b3a    /system/lib/libcutils.so  (set_sched_policy+49)   
  3. D/SchedPolicy(  1350):  #02    pc  00010e82    /system/lib/libutils.so  (androidSetThreadPriority+61)   
  4. D/SchedPolicy(  1350):  #03    pc  00068104    /system/lib/libandroid_runtime.so  (android_os_Process_setThreadPriority(_JNIEnv*,  _jobject*,  int,  int)+7)   
  5. D/SchedPolicy(  1350):  #04    pc  0001e510    /system/lib/libdvm.so  (dvmPlatformInvoke+112)   
  6. D/SchedPolicy(  1350):  #05    pc  0004d6aa    /system/lib/libdvm.so  (dvmCallJNIMethod(unsigned  int  const*,  JValue*,  Method  const*,  Thread*)+417)   
  7. D/SchedPolicy(  1350):  #06    pc  00027920    /system/lib/libdvm.so   
  8. D/SchedPolicy(  1350):  #07    pc  0002b7fc    /system/lib/libdvm.so  (dvmInterpret(Thread*,  Method  const*,  JValue*)+184)   
  9. D/SchedPolicy(  1350):  #08    pc  00060c30    /system/lib/libdvm.so  (dvmCallMethodV(Thread*,  Method  const*,  Object*,  bool,  JValue*,  std::__va_list)+271)   
  10. D/SchedPolicy(  1350):  #09    pc  0004cd34    /system/lib/libdvm.so   
  11. D/SchedPolicy(  1350):  #10    pc  00049382    /system/lib/libandroid_runtime.so   
  12. D/SchedPolicy(  1350):  #11    pc  00065e52    /system/lib/libandroid_runtime.so   
  13. D/SchedPolicy(  1350):  #12    pc  0001435e    /system/lib/libbinder.so  (android::BBinder::transact(unsigned  int,  android::Parcel  const& ,  android::Parcel*,  unsigned  int)+57)   
  14. D/SchedPolicy(  1350):  #13    pc  00016f5a    /system/lib/libbinder.so  (android::IPCThreadState::executeCommand(int)+513)   
  15. D/SchedPolicy(  1350):  #14    pc  00017380    /system/lib/libbinder.so  (android::IPCThreadState::joinThreadPool(bool)+183)   
  16. D/SchedPolicy(  1350):  #15    pc  0001b160    /system/lib/libbinder.so   
  17. D/SchedPolicy(  1350):  #16    pc  00011264    /system/lib/libutils.so  (android::Thread::_threadLoop(void*)+111)   
  18. D/SchedPolicy(  1350):  #17    pc  000469bc    /system/lib/libandroid_runtime.so  (android::AndroidRuntime::javaThreadShell(void*)+63)   
  19. D/SchedPolicy(  1350):  #18    pc  00010dca    /system/lib/libutils.so   
  20. D/SchedPolicy(  1350):  #19    pc  0000e3d8    /system/lib/libc.so  (__thread_entry+72)   
  21. D/SchedPolicy(  1350):  #20    pc  0000dac4    /system/lib/libc.so  (pthread_create+160)   
  22. D/SchedPolicy(  1350):  #00    pc  0000676c    /system/lib/libcutils.so   
  23. D/SchedPolicy(  1350):  #01    pc  00006b3a    /system/lib/libcutils.so  (set_sched_policy+49)   
  24. D/SchedPolicy(  1350):  #02    pc  00016f26    /system/lib/libbinder.so  (android::IPCThreadState::executeCommand(int)+461)   
  25. D/SchedPolicy(  1350):  #03    pc  00017380    /system/lib/libbinder.so  (android::IPCThreadState::joinThreadPool(bool)+183)   
  26. D/SchedPolicy(  1350):  #04    pc  0001b160    /system/lib/libbinder.so   
  27. D/SchedPolicy(  1350):  #05    pc  00011264    /system/lib/libutils.so  (android::Thread::_threadLoop(void*)+111)   
  28. D/SchedPolicy(  1350):  #06    pc  000469bc    /system/lib/libandroid_runtime.so  (android::AndroidRuntime::javaThreadShell(void*)+63)   
  29. D/SchedPolicy(  1350):  #07    pc  00010dca    /system/lib/libutils.so   
  30. D/SchedPolicy(  1350):  #08    pc  0000e3d8    /system/lib/libc.so  (__thread_entry+72)   
  31. D/SchedPolicy(  1350):  #09    pc  0000dac4    /system/lib/libc.so  (pthread_create+160)   
  32.      
6. 其它堆栈信息查询
 

    推荐阅读