如何将Sanitizer报告与GTest及Jenkins集成?
解决GTest+Sanitizer多平台项目的Jenkins可视化与测试级状态管理问题
一、Jenkins中直观展示Sanitizer报告(无需重编译依赖)
针对依赖库未用Sanitizer编译导致的误报,可通过Jenkins插件实现报告解析与过滤,无需手动查看原始日志:
导出Sanitizer日志到文件
执行测试前设置环境变量,将ASAN/UBSAN报告输出到独立文件,并避免非测试代码的Sanitizer错误直接终止构建:# Linux/macOS export ASAN_OPTIONS="log_path=asan_report:exitcode=0:detect_leaks=1" export UBSAN_OPTIONS="log_path=ubsan_report:print_stacktrace=1:halt_on_error=0" # Windows(PowerShell) $env:ASAN_OPTIONS = "log_path=asan_report:exitcode=0:detect_leaks=1" $env:UBSAN_OPTIONS = "log_path=ubsan_report:print_stacktrace=1:halt_on_error=0"exitcode=0确保依赖库的Sanitizer误报不会让测试进程返回非0码,halt_on_error=0允许测试继续执行。用Warnings Next Generation Plugin解析报告
- 安装Jenkins的
Warnings Next Generation Plugin - 构建后步骤添加「Record compiler warnings and static analysis results」
- 配置解析规则:选择「Clang AddressSanitizer」和「Clang UndefinedBehaviorSanitizer」,指定对应日志文件路径(如
asan_report.*) - 添加过滤规则:在「Filter warnings」中设置排除路径,比如依赖库的安装目录前缀,忽略来自依赖的误报,只保留自有代码的问题。
- 安装Jenkins的
关联xunit测试结果
插件会自动将Sanitizer错误关联到对应的测试用例(如果日志中包含测试名称),结合已有的xunit可视化,实现测试结果与Sanitizer问题的联动展示。
二、基于GTest观察者实现测试级Sanitizer管理
通过自定义GTest观察者,实现每个测试结束后生成报告、标记失败测试并重置Sanitizer状态,避免跨测试的错误污染:
代码实现
#include <gtest/gtest.h> #include <sanitizer/asan_interface.h> #include <sanitizer/ubsan_interface.h> #include <cstdlib> #include <string> // 全局变量记录当前运行的测试名称 std::string g_current_test; // Sanitizer报告回调:捕获错误并标记当前测试失败 void sanitizer_error_callback(const char* report, void*) { // 打印带测试名称的错误信息 fprintf(stderr, "[SANITIZER ERROR IN TEST: %s]\n%s\n", g_current_test.c_str(), report); // 向GTest标记测试属性,用于后续识别失败测试 testing::Test::RecordProperty("has_sanitizer_error", "true"); } class SanitizerTestListener : public testing::TestEventListener { private: testing::TestEventListener* m_original_listener; public: explicit SanitizerTestListener(testing::TestEventListener* original) : m_original_listener(original) {} // 测试开始时记录当前测试名称 void OnTestStart(const testing::TestInfo& test_info) override { g_current_test = std::string(test_info.test_case_name()) + "." + test_info.name(); m_original_listener->OnTestStart(test_info); } // 测试结束时生成报告并重置Sanitizer void OnTestEnd(const testing::TestInfo& test_info) override { // 打印当前测试的Sanitizer统计信息 __asan_print_accumulated_stats(); __ubsan_print_current_report(); // 重置Sanitizer状态,清除之前的错误记录 __asan_reset(); __ubsan_reset(); m_original_listener->OnTestEnd(test_info); } // 测试程序启动时配置Sanitizer void OnTestProgramStart(const testing::UnitTest& unit_test) override { // 设置错误回调 __asan_set_error_report_callback(sanitizer_error_callback); __ubsan_set_error_report_callback(sanitizer_error_callback); // 配置Sanitizer不终止程序,允许后续测试执行 setenv("ASAN_OPTIONS", "halt_on_error=0", 1); setenv("UBSAN_OPTIONS", "halt_on_error=0", 1); m_original_listener->OnTestProgramStart(unit_test); } // 转发其他事件到原始观察者 void OnTestSuiteStart(const testing::TestSuite& suite) override { m_original_listener->OnTestSuiteStart(suite); } void OnTestSuiteEnd(const testing::TestSuite& suite) override { m_original_listener->OnTestSuiteEnd(suite); } void OnTestProgramEnd(const testing::UnitTest& unit_test) override { m_original_listener->OnTestProgramEnd(unit_test); } void OnTestPartResult(const testing::TestPartResult& result) override { m_original_listener->OnTestPartResult(result); } }; int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); // 替换默认观察者,添加自定义Sanitizer观察者 testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); auto default_printer = listeners.Release(listeners.default_result_printer()); listeners.Append(new SanitizerTestListener(default_printer)); return RUN_ALL_TESTS(); }
关键注意事项
- 编译测试代码时需添加Sanitizer编译选项:
-fsanitize=address,undefined,依赖库无需修改编译方式,ASAN/UBSAN兼容未加Sanitizer的二进制。 __asan_reset和__ubsan_reset需编译器版本支持(Clang 8+、GCC 9+),若版本较低可忽略重置步骤,仅通过日志区分不同测试的错误。- 可在
sanitizer_error_callback中添加堆栈路径过滤,忽略来自依赖库的错误报告,进一步减少误报。
内容的提问来源于stack exchange,提问作者Marek R
相关产品推荐
相关产品推荐

