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里看以看到。