You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Sanitizer报告与GTest及Jenkins集成?

解决GTest+Sanitizer多平台项目的Jenkins可视化与测试级状态管理问题

一、Jenkins中直观展示Sanitizer报告(无需重编译依赖)

针对依赖库未用Sanitizer编译导致的误报,可通过Jenkins插件实现报告解析与过滤,无需手动查看原始日志:

  1. 导出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允许测试继续执行。

  2. 用Warnings Next Generation Plugin解析报告

    • 安装Jenkins的Warnings Next Generation Plugin
    • 构建后步骤添加「Record compiler warnings and static analysis results」
    • 配置解析规则:选择「Clang AddressSanitizer」和「Clang UndefinedBehaviorSanitizer」,指定对应日志文件路径(如asan_report.*)
    • 添加过滤规则:在「Filter warnings」中设置排除路径,比如依赖库的安装目录前缀,忽略来自依赖的误报,只保留自有代码的问题。
  3. 关联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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 14:15:34