如何转储C++程序执行流程、控制流及局部变量定位版本回归
C++版本回归定位:执行过程记录对比方案
需求背景
现有C++程序的v1与v1.01版本,v1.01引入回归问题。需要通过相同输入下的执行过程记录(包含函数调用信息、局部变量变化等),对比两个版本的行为差异来自动定位回归点。开发环境优先Windows MSVC,其次Linux Clang,倾向使用通用、跨语言工具,若工具方案受限则考虑代码插桩。
示例代码
原始待分析代码:
int test(int n) { n++; std::cout << n << std::endl; n += 15; return n + 1; } int main(int argc, char** argv) { auto result = test(std::stoi(argv[1])); std::cout << result << std::endl; }
期望执行记录格式
Visited Function: main with arguments (argc: 2, argv ["path", 7]) Visited Function test with arguments (n: 7) test: n set to 8 test: n set to 23 test: n set to 24 Exit function test Main: result set to 24
一、Windows时间旅行调试(MSVC环境)
针对你遇到的「记录格式无法转成人类可读内容」问题,可按以下步骤操作:
1. 准备工作
编译程序时开启调试符号:在MSVC项目属性中设置C/C++ -> 常规 -> 调试信息格式为程序数据库(/Zi),确保生成PDB符号文件。
2. 生成时间旅行记录
- 打开WinDbg,加载目标程序(v1或v1.01版本)
- 输入命令
.ttrace record开始执行记录 - 运行程序并传入相同测试参数(如
7),待程序执行完成后输入.ttrace stop保存记录
3. 导出可读执行记录
- 回放记录:输入
.ttrace replay加载之前保存的记录 - 配置日志输出:输入
.logopen output_v1.txt,将所有调试输出写入文件 - 跟踪函数与变量:
- 在函数入口/出口设置断点:
bp test、bp main - 函数断点触发时,用
dx @$curstack查看调用栈与参数,用dx n查看变量当前值 - 用
watch n设置变量监视,每次变量修改时自动触发日志 - 用
p(单步过函数)或t(单步进函数)遍历执行流程,日志会自动记录所有操作
- 在函数入口/出口设置断点:
- 分别导出v1和v1.01的日志文件,直接对比文本差异即可定位回归点
二、rr调试器(Linux Clang环境)
rr是Linux下的轻量级时间旅行调试工具,适合精准对比执行轨迹:
1. 编译程序
用Clang编译时带上调试符号:
clang++ -g test.cpp -o test
2. 记录执行过程
rr record ./test 7
3. 回放与导出记录
- 启动回放:
rr replay,自动进入gdb交互环境 - 导出函数调用与变量变化:
- 用
info args查看当前函数参数 - 用
watch n监视变量修改,每次变化都会打印日志 - 用
rr dump将完整执行轨迹导出为文本文件
- 用
- 对比v1和v1.01的dump文件,筛选变量值或函数行为不一致的节点
三、代码插桩方案(通用兼容)
若通用工具无法满足需求,可通过手动插桩直接输出符合期望的执行记录:
插桩后示例代码
#include <iostream> #include <string> // 记录函数入口及参数 #define LOG_FUNC_ENTRY(func_name, args_str) \ std::cout << "Visited Function: " << func_name << " with arguments (" << args_str << ")" << std::endl // 记录变量修改 #define LOG_VAR_CHANGE(func_name, var_name, new_val) \ std::cout << func_name << ": " << var_name << " set to " << new_val << std::endl int test(int n) { LOG_FUNC_ENTRY("test", "n: " << n); n++; LOG_VAR_CHANGE("test", "n", n); std::cout << n << std::endl; n += 15; LOG_VAR_CHANGE("test", "n", n); int ret = n + 1; LOG_VAR_CHANGE("test", "n (return value)", ret); std::cout << "Exit function test" << std::endl; return ret; } int main(int argc, char** argv) { int input = std::stoi(argv[1]); LOG_FUNC_ENTRY("main", "argc: " << argc << ", argv [\"" << argv[0] << "\", " << input << "]"); auto result = test(input); LOG_VAR_CHANGE("Main", "result", result); std::cout << result << std::endl; return 0; }
使用方式
- 分别编译v1和v1.01的插桩版本,传入相同参数运行
- 直接对比两个版本的控制台输出,快速定位变量变化或函数行为的差异点
内容的提问来源于stack exchange,提问作者EUV
相关产品推荐
相关产品推荐

