使用Backtrace函数打印调用栈 - Debug居家必备

本文介绍了如何使用glibc提供的backtrace函数打印调用栈(callstack),包括在程序中注册信号如SIGSEGV和SIGPIPE,并在信号回调中利用backtrace进行调试。还提供了示例代码说明如何生成和解析调用栈。
glibc提供了backtrace这个库函数,可以用来打印call stack。比如我们可以在程序中注册常见的一些signal,比如SIGSEGMENT, SIGPIPE,然后在这些信号的回调函数中,利用backtrace打印出call stack,这样debug就非常的方便。 
backtrace的使用很简单,使用man手册中的Example代码即可,例如:
 
复制代码
 1 #include <execinfo.h>   
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <stdlib.h>
 5 #include <errno.h>
 6 
 7 #define SIZE        100
 8 
 9 void dump_stack()
10 {
11     int btnum = 0;
12     void *btbuf[SIZE];
13     char **btstrings = NULL;
14     int i;
15 
16     /* Get backtrace */
17     btnum = backtrace(btbuf, SIZE);
18     btstrings = backtrace_symbols(btbuf, btnum);
19     if (btstrings == NULL) {
20         printf("Backtrace failed: %d:%s\n", errno, strerror(errno));
21     } else {
22         printf("Backtraces, total %d items\n", btnum);
23         for (i = 0; i < btnum; i++)
24             printf("%s\n", btstrings[i]);
25 
26         free(btstrings);
27     }
28 }
29 
30 void start_working()
31 {
32     printf("Start working...\n");
33     dump_stack();
34 }
35 
36 int main()
37 {
38     printf("Start backtracing...\n");
39     start_working();
40     return 0;
41 }
复制代码

 

首先用backtrace,最多生成100层的call stack。然后用backtrace_symbols将backtrace返回的一堆地址翻译成函数名称。backtrace的返回值是具体生成了多少层的call stack,填充的btbuf是一个void *的数组,里面每个element都是一个void *,其实就是一个地址。 
backtrace_symbols生成的字符串都是malloc出来的,但是不要最后一个一个的free,因为backtrace_symbols是根据backtrace给出的call stack层数,一次性的malloc出来一块内存来存放结果字符串的,所以,像上面代码一样,只需要在最后,free backtrace_symbols的返回指针就OK了。这一点backtrace的manual中也是特别提到的。
 
此外需要注意的是,使用backtrace来获取调用栈信息,在编译的时候需要加入:-rdynamic,这个option是传递给linker的,linker会将symbol放到.dydym table中,这样backtrace_symbols才能获取到地址对应的symbol。所以即使是使用了-g来编译程序,如果不使用-rdynamic的话,backtrace_symbols也找不到地址对应的symbol。这是backtrace系列函数的一个缺陷。不过有了地址也算是一个很大帮助了,毕竟可以使用gdb来找到对应的symbol。
<think> 嗯,用户这次明确询问内核中打印调用栈函数。看来用户正在做内核开发或调试工作,可能遇到了需要分析函数调用流程的情况。 从用户提供的引用来看,其实已经包含了关键答案:dump_stack()就是最常用的内核调用栈打印函数。不过用户引用的资料有点零散,需要帮ta整合一下。 注意到引用[1]和[2]都明确指出dump_stack()的用法,而引用[4]展示了实际输出效果。用户似乎需要确认性答案,所以应该直接点明核心函数,再补充细节。 用户特意标注了"需要在内核中配置DEBUG_KERNEL",这个细节很重要,说明ta可能遇到过编译问题。要在回复中强调配置依赖,避免用户踩坑。 另外引用[3]提到的backtrace()属于用户空间的方案,虽然与内核无关,但考虑到用户可能混淆,还是需要明确区分说明。不过重点还是要放在内核方案上。 用户没有追问性能或高级用法,暂时不需要展开回溯深度控制等进阶内容。保持回答简洁实用最合适,最后留两个基础问题供ta延伸学习就够了。 </think> 在内核开发中,用于打印调用栈的核心函数是 **`dump_stack()`**。以下是关键信息和使用方法整理: ### 📌 核心函数 ```c #include <linux/kernel.h> void dump_stack(void); ``` ### 🔧 使用说明 1. **头文件依赖** 必须包含头文件: ```c #include <linux/kernel.h> // 基础声明 #include <linux/kprobes.h> // 调试支持(可选但推荐) #include <asm/traps.h> // 架构相关支持 ``` 2. **配置要求** 需在内核编译时启用调试选项: ```makefile CONFIG_DEBUG_KERNEL=y # 基础调试支持 CONFIG_STACKTRACE=y # 调用栈回溯功能 ``` 3. **输出示例** 调用后会在内核日志(`dmesg` 或 `/var/log/kern.log`)打印调用路径: ``` Call Trace: dump_stack_lvl+0x76/0xa0 dump_stack+0x10/0x20 your_function+0x45/0x60 ``` ### ⚠️ 注意事项 - **内核代码专用**:仅在内核模块/驱动中使用(如 `.ko` 文件) - **替代方案**: - `WARN()`:触发警告时附带调用栈(例:`WARN(condition, "message")`) - `BUG()`:触发内核崩溃并打印调用栈(致命错误时使用) ### 🔄 用户空间对比 用户态程序需使用不同机制: ```c #include <execinfo.h> void backtrace(void **buffer, int size); // 获取调用地址 char **backtrace_symbols(void *const *buffer, int size); // 解析符号 ``` (引用[3]展示了用户空间实现) ### ❓ 相关问题 1. 如何解析 `dump_stack()` 输出的符号地址? 2. `CONFIG_DEBUG_INFO` 选项对调用栈输出有何影响? 3. 如何在驱动中动态启用/禁用调用栈打印? > 关键引用: > `dump_stack` 用于内核调试,打印内核堆栈段信息 [^1] > 需包含头文件 `<linux/kernel.h>` 并通过 `dump_stack()` 打印 [^2] > 用户空间需使用 `backtrace()` 系列函数 [^3] > 实际输出效果参考 `/var/log/dmesg` 日志 [^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值