打印堆栈分为内核态和用户态
1 内核态:dump_stack
参考博客:
http://kernel.meizu.com/2017/03/18-40-19-dump_stack.html
作用:
打印进程的栈回溯信息。
前提:
内核配置勾选上;make menuconfig -> kernel hacking--> kernel debug
什么情况使用:
1、内核发生panic、内核主动调用dump_stack,打印call trace
2、代码调试,主动调用dump_stack函数。
主要信息内容:
源码分析:
1 /**
2 * dump_stack - dump the current task information and its stack trace
3 *
4 * Architectures can override this implementation by implementing its own.
5 */
6 #ifdef CONFIG_SMP
7 static atomic_t dump_lock = ATOMIC_INIT(-1);
8
9 asmlinkage __visible void dump_stack(void)
10 {
11 unsigned long flags;
12 int was_locked;
13 int old;
14 int cpu;
15
16 /*
17 * Permit this cpu to perform nested stack dumps while serialising
18 * against other CPUs
19 */
20 retry:
21 local_irq_save(flags);
22 cpu = smp_processor_id();
23 old = atomic_cmpxchg(&dump_lock, -1, cpu);
24 if (old == -1) {
25 was_locked = 0;
26 } else if (old == cpu) {
27 was_locked = 1;
28 } else {
29 local_irq_restore(flags);
30 /*
31 * Wait for the lock to release before jumping to
32 * atomic_cmpxchg() in order to mitigate the thundering herd
33 * problem.
34 */
35 do { cpu_relax(); } while (atomic_read(&dump_lock) != -1);
36 goto retry;
37 }
38
39 __dump_stack();
40
41 if (!was_locked)
42 atomic_set(&dump_lock, -1);
43
44 local_irq_restore(flags);
45 }
46 #else
47 asmlinkage __visible void dump_stack(void)
48 {
49 __dump_stack();
50 }
51 #endif
View Code
2、用户态导出堆栈
2.1 参考博客
2.2 如何使用
#include <stdio.h>
#include <execinfo.h>
/*在想查看的函数中使用user_dump_stack()函数即可*/
void user_dump_stack(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(0);
}
for (j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
}
移植代码,使用者只需要调用user_dump_stack函数即可,注意编译带-rdynamic
gcc test_backtrace.c -rdynamic
2.3 详细讲解
#include <execinfo.h> //函数头文件
int backtrace (void **buffer, int size);
/* 获取函数调用栈帧数据。获取的栈帧数据存放在buffer中,最多可保存size个栈帧 */
char **backtrace_symbols (void *const *buffer, int size);
/* 将获取的栈帧地址数据翻译为字符串(包含函数名、函数内偏移地址、以及实际返回地址),只有elf二进制格式的程序才能获取函数名以及偏移地址。
链接-rdynamic编译避免无法翻译函数名。backtrace_symbols()函数返回值是一个malloc的申请字符串指针,使用完后,调用者必需把它释放掉。
注:如果不能为字符串获取足够的空间,该函数的返回值为NULL。传入存放函数调用栈数据的buffer,以及实际存储的栈帧个数。*/
void backtrace_symbols_fd (void *const *buffer, int size, int fd);
/* 与backtrace_symbols()函数具有相同的功能,将结果写入文件描述符为fd的文件中(不会给调用者返回字符串数组),每个函数对应一行。
它不会调用malloc函数,因此,它可以应用在函数调用可能失败的情况下 */
注意:
忽略帧指针(由gcc任何非零优化级别处理了)可能引起这些假设的混乱。
内联函数没有栈帧。
Tail-call(尾调用)优化会导致栈帧被其它调用覆盖。
未编译链接-rdynamic;只有十六进制的返回地址能被获取。
“static”函数名是不会导出的,也不会出现在函数调用列表里,即使指定了-rdynamic链接选项。
2.4 示例
#include <stdio.h>
#include <execinfo.h>
/*在想查看的函数中使用user_dump_stack()函数即可*/
void user_dump_stack(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(0);
}
for (j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
}
int call_function_six(int function_para)
{
printf("xxxxxxxxxxxx\n");
user_dump_stack();
}
int call_function_fiv(int function_para)
{
call_function_six(function_para);
}
int call_function_for(int function_para)
{
call_function_fiv(function_para);
}
int call_function_thr(int function_para)
{
call_function_for(function_para);
}
static int call_function_two(int function_para)
{
call_function_thr(function_para);
}
int call_function_one(int function_para)
{
call_function_two(function_para);
}
int main()
{
int function_para = 1;
call_function_one(function_para);
}
View Code
gcc test_backtrace.c -rdynamic
[root@localhost test_user]#./a.out
xxxxxxxxxxxx
backtrace() returned 10 addresses
./a.out(user_dump_stack+0x1f) [0x400a8f]
./a.out(call_function_six+0x1a) [0x400b35]
./a.out(call_function_fiv+0x15) [0x400b4c]
./a.out(call_function_for+0x15) [0x400b63]
./a.out(call_function_thr+0x15) [0x400b7a]
./a.out() [0x400b91]
./a.out(call_function_one+0x15) [0x400ba8]
./a.out(main+0x19) [0x400bc3]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fdfcdb2b555]
./a.out() [0x4009a9]
call_function_two 由于是static未被打印出来