如何录制调用栈以获取按执行顺序排列的已执行函数列表用于调试?
无需手动修改函数的函数调用追踪方案
以下是几种不同场景下的低成本实现方案,都可以满足你要的逆序输出(函数返回顺序)、持续追加的需求:
1. GCC/Clang 编译器内置插桩方案(零业务代码修改)
这是改动最小的方案,使用-finstrument-functions编译参数,编译器会自动在所有函数的入口、出口位置插入预设的钩子函数,你只需要单独实现钩子逻辑,完全不需要修改现有业务函数的代码:
#include <stdio.h> #include <dlfcn.h> // 可调整追踪列表最大长度,也可替换为动态数组实现无限追加 #define MAX_TRACE_LEN 2048 static const char* func_trace_list[MAX_TRACE_LEN]; static int trace_cur_idx = 0; // 钩子函数必须加no_instrument_function属性,避免自身被插桩造成死循环 void __cyg_profile_func_enter(void *func_addr, void *call_site) __attribute__((no_instrument_function)); void __cyg_profile_func_exit(void *func_addr, void *call_site) __attribute__((no_instrument_function)); void __cyg_profile_func_enter(void *func_addr, void *call_site) { // 如果需要正向调用顺序可以在此处追加函数名,你要的逆序在出口处理即可 } void __cyg_profile_func_exit(void *func_addr, void *call_site) { if (trace_cur_idx >= MAX_TRACE_LEN) return; Dl_info func_info; // 解析函数地址对应的函数名 if (dladdr(func_addr, &func_info) && func_info.dli_sname) { func_trace_list[trace_cur_idx++] = func_info.dli_sname; } } // 对外提供的打印/获取追踪列表的接口 void print_func_trace() { for (int i = 0; i < trace_cur_idx; i++) { printf("%s\n", func_trace_list[i]); } }
编译时将上述代码和你的业务代码一起编译,额外加两个编译参数-finstrument-functions -ldl即可。你的示例代码运行后调用print_func_trace,输出顺序就是function3、function2、function1,后续所有执行的函数都会自动追加到列表中。
2. 调试器动态追踪方案(无需编译修改)
如果不方便重新编译代码,可以直接用GDB批量打点实现追踪,全程不用改任何代码:
# 给所有函数加断点 rbreak . # 断点触发后打印函数名自动继续运行 commands bt 1 c end # 启动程序 run
如果需要逆序输出,可以把断点设置在函数返回位置即可,输出日志过滤后就是你要的调用顺序列表。
3. 宏封装轻量方案(仅需少量修改)
如果是小型项目不想加编译参数,可以用GCC的cleanup属性封装追踪逻辑,仅需要在每个函数开头加一行宏即可:
// 函数返回时自动触发的入列逻辑 void push_func_to_trace(const char** func_name) { // 此处添加将*func_name追加到列表的逻辑 } // 每个函数开头只需要加一行这个宏 #define TRACE_FUNC const char* __cur_func __attribute__((cleanup(push_func_to_trace))) = __func__;
内容的提问来源于stack exchange,提问作者asta nigem
相关产品推荐
相关产品推荐

