Linux打印函数调用栈
方法一:
#include <stdio.h> #include <stdlib.h> #include <execinfo.h> /* Obtain a backtrace and print it to stdout. */ void print_trace (void) { void *array[10]; size_t size; char **strings; size_t i; size = backtrace (array, 10); strings = backtrace_symbols (array, size); printf ("Obtained %zd stack frames.\n", size); for (i = 0; i < size; i++) printf ("%s\n", strings); free (strings); } int main() { print_trace (); }编译:g++ stack.c -g -o stack -rdynamic
方法二:
#include <unistd.h> #include <stdio.h> #include <execinfo.h> #include <stdlib.h> #include <string.h> void backtrace() { const int maxLevel = 200; void* buffer[maxLevel]; int level = backtrace(buffer, maxLevel); const int SIZE = 1024; char cmd[SIZE] = "addr2line -C -f -e "; // let prog point to the end of "cmd" char* prog = cmd + strlen(cmd); int r = readlink("/proc/self/exe", prog, sizeof(cmd) - (prog-cmd)-1); FILE* fp = popen(cmd, "w"); if (!fp) { perror("popen"); return; } for (int i = 0; i < level; ++i) { fprintf(fp, "%p\n", buffer[i]); } fclose(fp); } void foo(int, char*) { backtrace(); } void bar(double) { foo(0, NULL); } int main() { bar(0.0); //A a; return 0; }编译:g++ stack1.c -g -o stack1 -rdynamic
注意:execinfo.h文件
Android 打印函数调用栈
为什么要打印函数调用堆栈?
打印调用堆栈可以直接把问题发生时的函数调用关系打出来,非常有利于理解函数调用关系。比如函数A可能被B/C/D调用,如果只看代码,B/C/D谁调用A都有可能,如果打印出调用堆栈,直接就把谁调的打出来了。
不仅如此,打印函数调用堆栈还有另一个好处。在Android代码里,函数命名很多雷同的,虚函数调用,几个类里的函数名相同等。如果用了堆栈打印,很容易看到函数调用逻辑。
那么一个问题来了,Android/kernel系统运行的境况下,打印出某个情形下的堆栈信息,这个对源代码逻辑研究很有帮助。
Linux Kernel
Kernel里最简单,直接有几现成的函数可以使用:
dump_stack() 这个函数打出当前堆栈和函数调用backtrace后接着运行。
> 需要包含的头文件:
#include <asm/ptrace.h>
> 在函数中调用:
dump_stack();
WARN_ON(x) 这个函数跟dump_stack很像,他需要满足一定的条件才把stack打出来。
打印出来的结果都在kernel log里面,一般使用dmesg命令就可以看到了
$ dmesg
Native C++
Android在新版(至少5.0, 6.0)里加入了CallStack类,这个类可以打印出当前的backtrace。用法很简单:
> 前面确保包含头文件#include <utils/CallStack.h>
> Android.mk的库依赖列表(LOCAL_SHARED_LIBRARIES)里包含libutils,一般都已经包含了。
> 然后在要打印堆栈处加入android::CallStack cs(“TAG”);
> “TAG”是logcat输出的TAG,这里可以自己定义。如果上下文已经在android namespace里,“android::”前缀就不必加了。
Native C++输出的log可以在logcat里看到。
注意,在网上的一些文档里说要这么用:
CallStack stack;
stack.update();
stack.dump();
这样做已经不行了,在新版Android里编译不过。
Native C
Andorid对C的堆栈打印支持不太好,过去一般推荐libcorkscrew.so,并加入大段代码来umwind_backtrace。新版Android上libcorkscrew已经被去掉,加载libcorkscrew库的方法自然就不能用了。
一个简单方法是用C语言调用C++的函数,就是extern "C"。
> 先在项目里加入一个c++,如callstack.cpp。里面是:
#include <utils/CallStack.h> extern "C" void dumping_callstack(void); void dumping_callstack(void) { android::CallStack cs("Audio"); }
> 在项目里再加入一个c++的头文件,如callstack.h,里面是:
void dumping_callstack(void);
> 在将要添加函数调用栈的源文件的编译文件Android.mk中,源代码列表(LOCAL_SRC_FILES)里加入callstack.cpp,同时确保libutils在依赖列表里(LOCAL_SHARED_LIBRARIES)。
> 在native C里include callstack.h后,就可以直接调用dumping_callstack()打印函数调用栈了。
#include "callstack.h" ... dumping_callstack(); ...
这个log可以在logcat里看到。
Java
Java最详细,它的backtrace最详细,连文件名和行号都打出来了:
> 在需要打印函数调用栈的位置添加如下代码:
Exception e = new Exception("Audio"); e.printStackTrace();
log在logcat里看以看到。