导出堆栈(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
View Code

 

2、用户态导出堆栈

2.1 参考博客

https://www.cnblogs.com/muahao/p/7610645.html

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未被打印出来

 

posted on 2021-08-23 23:02  红旗kernel  阅读(576)  评论(0编辑  收藏  举报

导航