JNI中如何打印Call Stack

来源:互联网 发布:skype mac版 编辑:程序博客网 时间:2024/04/29 16:32

NDK里面好像没有专门打印Call Stack的函数,正好又要用到这个功能,Google了一翻,可以用stack unwind相关的API实现。


关于什么是stack unwind的解释如下:

http://www.ibm.com/support/knowledgecenter/SSAE4W_9.0.0/com.ibm.xlcpp111.aix.doc/language_ref/cplr155.html

http://www.cnblogs.com/catch/p/3604516.html (中文)

简单的说就是一套专门用来处理异常的函数,通过它就可以拿到当前的Call Stack.

Android hardward层其实也有使用这些函数,比如下面的这个文件:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. hardware/ti/omap4xxx/heaptracker.c  

好了,废话不多说,直接上代码:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #include <unwind.h>  
  2. #include <dlfcn.h>  
  3.   
  4. struct BacktraceState  
  5. {  
  6.     intptr_t* current;  
  7.     intptr_t* end;  
  8. };  
  9.   
  10.   
  11. static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)  
  12. {  
  13.     BacktraceState* state = static_cast<BacktraceState*>(arg);  
  14.     intptr_t ip = (intptr_t)_Unwind_GetIP(context);  
  15.     if (ip) {  
  16.         if (state->current == state->end) {  
  17.             return _URC_END_OF_STACK;  
  18.         } else {  
  19.             state->current[0] = ip;  
  20.             state->current++;  
  21.         }  
  22.     }  
  23.     return _URC_NO_REASON;  
  24.   
  25.   
  26. }  
  27.   
  28. size_t captureBacktrace(intptr_t* buffer, size_t maxStackDeep)  
  29. {  
  30.     BacktraceState state = {buffer, buffer + maxStackDeep};  
  31.     _Unwind_Backtrace(unwindCallback, &state);  
  32.     return state.current - buffer;  
  33. }  
  34.   
  35. void dumpBacktraceIndex(char *out, intptr_t* buffer, size_t count)  
  36. {  
  37.     for (size_t idx = 0; idx < count; ++idx) {  
  38.         intptr_t addr = buffer[idx];  
  39.         const char* symbol = "      ";  
  40.         const char* dlfile="      ";  
  41.   
  42.         Dl_info info;  
  43.         if (dladdr((void*)addr, &info)) {  
  44.             if(info.dli_sname){  
  45.                symbol = info.dli_sname;  
  46.              }  
  47.             if(info.dli_fname){  
  48.                 dlfile = info.dli_fname;  
  49.              }              
  50.         }else{  
  51.            strcat(out,"#                               \n");  
  52.            continue;  
  53.         }  
  54.         char temp[50];  
  55.         memset(temp,0,sizeof(temp));  
  56.         sprintf(temp,"%zu",idx);  
  57.         strcat(out,"#");  
  58.         strcat(out,temp);  
  59.         strcat(out, ": ");  
  60.         memset(temp,0,sizeof(temp));  
  61.         sprintf(temp,"%zu",addr);  
  62.         strcat(out,temp);  
  63.         strcat(out, "  " );  
  64.         strcat(out, symbol);  
  65.         strcat(out, "      ");  
  66.         strcat(out, dlfile);  
  67.         strcat(out, "\n" );  
  68.     }  
  69. }  


然后在想打印Call Stack的地方调用上面的函数:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1.             const size_t maxStackDeep = 20;  
  2.             intptr_t stackBuf[maxStackDeep];  
  3.             char outBuf[2048];  
  4.             memset(outBuf,0,sizeof(outBuf));  
  5.             dumpBacktraceIndex(outBuf, stackBuf, captureBacktrace(stackBuf, maxStackDeep));  
  6.             LogYc(" %s\n", outBuf);  

打印出来的效果:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #13047786506  _Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list      libdvm.so  
  2. #23047787112  _Z13dvmCallMethodP6ThreadPK6MethodP6ObjectP6JValuez      libdvm.so  
  3. #33047840152  _Z18dvmFindClassNoInitPKcP6Object      libdvm.so  
  4. #43047773520  _Z18dvmOptResolveClassP11ClassObjectjP11VerifyError      libdvm.so  
  5. #53047754016  _Z17dvmVerifyCodeFlowP12VerifierData      libdvm.so  
  6. #63047771452              libdvm.so  
  7. #73047771642  _Z14dvmVerifyClassP11ClassObject      libdvm.so  
  8. #83047840596  dvmInitClass      libdvm.so  
  9. #93047844830  dvmResolveMethod      libdvm.so  
  10. #103047590400  _Z20dvmInterpretPortableP6Thread      libdvm.so  
  11. #113047531408  _Z12dvmInterpretP6ThreadPK6MethodP6JValue      libdvm.so  
  12. #123047787002  _Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list      libdvm.so  
  13. #133047704384              libdvm.so  
  14. #143047652026              libdvm.so  
  15. #152851196260              libbaiduprotect.so  
  16. #162851190688              libbaiduprotect.so  
  17. #173047475600  dvmPlatformInvoke      libdvm.so  
  18. #183047711070  _Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread      libdvm.so  
  19. #193047622360  _Z21dvmCheckCallJNIMethodPKjP6JValuePK6MethodP6Thread      libdvm.so  


说说原理:
整个过程的核心就在_Unwind_Backtrace(unwindCallback, &state)这个函数调用上。

它的作用是什么呢?

它会遍历当前的Call stack,并且会在每个call stack上都调用unwindCallback函数,state是传递给给它的参数。

于是我们就在unwindCallback这个回调中通过_Unwind_GetIP(context)获取到当前call stack上正在执行的指令的地址,最后再通过dladdr()获取到对应的so信息。

dladdr()会根据传递给它的addr遍历当前进程中打开的so....

下面的是dladdr()寻找so的具体实现:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. soinfo* find_containing_library(const void* p) {  
  2.   Elf32_Addr address = reinterpret_cast<Elf32_Addr>(p);  
  3.   for (soinfo* si = solist; si != NULL; si = si->next) {  
  4.     if (address >= si->base && address - si->base < si->size) {  
  5.       return si;  
  6.     }  
  7.   }  
  8.   return NULL;  
  9. }  

0 0
原创粉丝点击