导出堆栈(user、kernel)
打印堆栈分为内核态和用户态
1 内核态:dump_stack
参考博客:
http://kernel.meizu.com/2017/03/18-40-19-dump_stack.html
https://blog.csdn.net/xj178926426/article/details/79235952
作用:
打印进程的栈回溯信息。
前提:
内核配置勾选上;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
2、用户态导出堆栈
2.1 参考博客
https://www.cnblogs.com/muahao/p/7610645.html2.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); }
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未被打印出来