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层其实也有使用这些函数,比如下面的这个文件:
- hardware/ti/omap4xxx/heaptracker.c
好了,废话不多说,直接上代码:
- #include <unwind.h>
- #include <dlfcn.h>
-
- struct BacktraceState
- {
- intptr_t* current;
- intptr_t* end;
- };
-
-
- static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
- {
- BacktraceState* state = static_cast<BacktraceState*>(arg);
- intptr_t ip = (intptr_t)_Unwind_GetIP(context);
- if (ip) {
- if (state->current == state->end) {
- return _URC_END_OF_STACK;
- } else {
- state->current[0] = ip;
- state->current++;
- }
- }
- return _URC_NO_REASON;
-
-
- }
-
- size_t captureBacktrace(intptr_t* buffer, size_t maxStackDeep)
- {
- BacktraceState state = {buffer, buffer + maxStackDeep};
- _Unwind_Backtrace(unwindCallback, &state);
- return state.current - buffer;
- }
-
- void dumpBacktraceIndex(char *out, intptr_t* buffer, size_t count)
- {
- for (size_t idx = 0; idx < count; ++idx) {
- intptr_t addr = buffer[idx];
- const char* symbol = " ";
- const char* dlfile=" ";
-
- Dl_info info;
- if (dladdr((void*)addr, &info)) {
- if(info.dli_sname){
- symbol = info.dli_sname;
- }
- if(info.dli_fname){
- dlfile = info.dli_fname;
- }
- }else{
- strcat(out,"# \n");
- continue;
- }
- char temp[50];
- memset(temp,0,sizeof(temp));
- sprintf(temp,"%zu",idx);
- strcat(out,"#");
- strcat(out,temp);
- strcat(out, ": ");
- memset(temp,0,sizeof(temp));
- sprintf(temp,"%zu",addr);
- strcat(out,temp);
- strcat(out, " " );
- strcat(out, symbol);
- strcat(out, " ");
- strcat(out, dlfile);
- strcat(out, "\n" );
- }
- }
然后在想打印Call Stack的地方调用上面的函数:
- const size_t maxStackDeep = 20;
- intptr_t stackBuf[maxStackDeep];
- char outBuf[2048];
- memset(outBuf,0,sizeof(outBuf));
- dumpBacktraceIndex(outBuf, stackBuf, captureBacktrace(stackBuf, maxStackDeep));
- LogYc(" %s\n", outBuf);
打印出来的效果:
- #1: 3047786506 _Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list libdvm.so
- #2: 3047787112 _Z13dvmCallMethodP6ThreadPK6MethodP6ObjectP6JValuez libdvm.so
- #3: 3047840152 _Z18dvmFindClassNoInitPKcP6Object libdvm.so
- #4: 3047773520 _Z18dvmOptResolveClassP11ClassObjectjP11VerifyError libdvm.so
- #5: 3047754016 _Z17dvmVerifyCodeFlowP12VerifierData libdvm.so
- #6: 3047771452 libdvm.so
- #7: 3047771642 _Z14dvmVerifyClassP11ClassObject libdvm.so
- #8: 3047840596 dvmInitClass libdvm.so
- #9: 3047844830 dvmResolveMethod libdvm.so
- #10: 3047590400 _Z20dvmInterpretPortableP6Thread libdvm.so
- #11: 3047531408 _Z12dvmInterpretP6ThreadPK6MethodP6JValue libdvm.so
- #12: 3047787002 _Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list libdvm.so
- #13: 3047704384 libdvm.so
- #14: 3047652026 libdvm.so
- #15: 2851196260 libbaiduprotect.so
- #16: 2851190688 libbaiduprotect.so
- #17: 3047475600 dvmPlatformInvoke libdvm.so
- #18: 3047711070 _Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread libdvm.so
- #19: 3047622360 _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的具体实现:
- soinfo* find_containing_library(const void* p) {
- Elf32_Addr address = reinterpret_cast<Elf32_Addr>(p);
- for (soinfo* si = solist; si != NULL; si = si->next) {
- if (address >= si->base && address - si->base < si->size) {
- return si;
- }
- }
- return NULL;
- }