资讯动态

sanitizers-cmake:用 CMake 为 Fluent Bit 生态目标开启 AddressSanitizer 等动态分析工具

发布时间:2026/9/16 18:42:29 来源:尧图企业网站定制
sanitizers-cmake用 CMake 为 Fluent Bit 生态目标开启 AddressSanitizer 等动态分析工具【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit导读本文围绕 Fluent Bit 仓库中随 ChunkIO 子项目一同携带的 sanitizers-cmake 说明文档系统讲解如何借助这套 CMake 模块为二进制目标可执行文件与库一键启用 AddressSanitizerASan、MemorySanitizerMSan、ThreadSanitizerTSan与 UndefinedBehaviorSanitizerUBSan。读完本文你将掌握该模块的三种接入方式、四个开关选项的语义与兼容性约束、add_sanitizers()与sanitizer_add_blacklist_file()的用法以及asan-wrapper预加载运行技巧并能结合仓库内 ChunkIO 的CIO_SANITIZE_ADDRESS集成实例把同样的模式复用到自己的 C/C 项目中。一、sanitizers-cmake 是什么sanitizers-cmake 是一个纯粹的 CMake 模块集合核心目标是为 CMake 构建的二进制目标自动追加 sanitizer 编译与链接参数。用户不需要手工往target_compile_options/target_link_options里粘贴-fsanitizeaddress这类标志也不需要为不同编译器维护多套参数全部由模块在配置期探测并注入。在 Fluent Bit 仓库中它被存放在 lib/chunkio/cmake/sanitizers-cmake/ 目录下作为 ChunkIO 子项目的第三方构建工具依赖用于在开启 Address Sanitizer 时给 ChunkIO 的测试与库目标附加 sanitizer 编译能力。该模块以 MIT 协议发布版权归属于 Matthew Arsenault 与亚琛工业大学RWTH Aachen University。模块的文件布局如下目录内共 5 个*.cmake文件加 1 个 shell 包装脚本文件职责FindSanitizers.cmake总入口声明SANITIZE_LINK_STATIC选项统一加载 ASan/TSan/MSan/UBSan 四个子模块定义add_sanitizers()与sanitizer_add_blacklist_file()FindASan.cmakeAddressSanitizer 支持定义SANITIZE_ADDRESS选项与add_sanitize_address()FindTSan.cmakeThreadSanitizer 支持定义SANITIZE_THREAD选项校验平台与位数FindMSan.cmakeMemorySanitizer 支持定义SANITIZE_MEMORY选项校验平台与位数FindUBSan.cmakeUndefinedBehaviorSanitizer 支持定义SANITIZE_UNDEFINED选项sanitize-helpers.cmake内部辅助函数源码语言识别、目标编译器探测、编译器标志探测与注入asan-wrapperASan 预加载包装脚本通过LD_PRELOAD注入 libasantests/自带冒烟测试asan_test 与最短扩展名解析测试二、把模块接入你的项目2.1 方式一Git Submodule 集成官方推荐的做法是把仓库以 submodule 形式挂进自己的工程mkdir externals git submodule add gitgithub.com:arsenm/sanitizers-cmake.git externals/sanitizers-cmake然后把externals/sanitizers-cmake/cmake追加进CMAKE_MODULE_PATHset(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake ${CMAKE_MODULE_PATH})之后即可在任意 CMakeLists 中直接使用find_package(Sanitizers)2.2 方式二直接拷贝文件如果项目不使用 git 或不想引入 submodule可以直接把 cmake 目录 中的全部文件复制进自己的仓库同样将对应目录加入CMAKE_MODULE_PATH即可。需要注意拷贝方式不会随上游更新需要自行跟进维护。2.3 方式三复用本仓库的现成集成ChunkIO 实例本仓库中的 ChunkIO 项目已经完成了整套接入可以直接当作模板参考。在 lib/chunkio/CMakeLists.txt 中可以看到其集成逻辑option(CIO_SANITIZE_ADDRESS Enable address sanitizer Off) # Check if Address Sanitizer is enabled if(CIO_SANITIZE_ADDRESS OR SANITIZE_ADDRESS) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/sanitizers-cmake/cmake ${CMAKE_MODULE_PATH}) if(NOT SANITIZE_ADDRESS) add_definitions(-DSANITIZE_ADDRESSOn) endif() find_package(Sanitizers) ... endif()这里揭示了两个实用细节ChunkIO 对外暴露了自有开关CIO_SANITIZE_ADDRESS同时兼容上游的SANITIZE_ADDRESS当只给了前者时通过add_definitions(-DSANITIZE_ADDRESSOn)将其桥接成模块认识的选项。find_package(Sanitizers)之后模块并不定义Sanitizers_FOUND变量这是上游已知问题所以 ChunkIO 改用SANITIZE_LINK_STATIC是否被定义来判断模块是否成功加载未定义则FATAL_ERROR终止已定义则打印 Enabling address sanitizer。从源码结构可以推断SANITIZE_LINK_STATIC在 FindSanitizers.cmake 中被option()声明因此只要模块被加载该变量就一定存在用它作为加载成功的探针是可靠的。三、四个启用开关与各自的约束模块通过四个 CMake 布尔选项控制是否启用对应 sanitizer在配置阶段传入即可cmake -DSANITIZE_ADDRESSOn .. # 地址越界、use-after-free 等内存错误 cmake -DSANITIZE_MEMORYOn .. # 未初始化内存读取 cmake -DSANITIZE_THREADOn .. # 数据竞争 cmake -DSANITIZE_UNDEFINEDOn .. # 未定义行为整数溢出、空指针偏移等也可以配合图形界面ccmake / cmake-gui勾选。3.1 参数探测与平台限制来自源码的事实AddressSanitizerFindASan.cmake的候选标志依次为MSVC 的/fsanitizeaddressClang 3.2 的-g -fsanitizeaddress -fno-omit-frame-pointer保留栈帧便于回溯以及旧式弃用标志-g -faddress-sanitizer。模块会逐个尝试取第一个被当前编译器接受的标志。ThreadSanitizerFindTSan.cmake仅在 Linux 与 macOS 上支持且只支持 64 位系统不满足条件时模块会打印警告并强制把SANITIZE_THREAD置回Off。MemorySanitizerFindMSan.cmake仅支持 Linux 且仅支持 64 位系统其余情况同样警告并关闭。兼容性硬约束ASan 与 TSan、MSan 互不兼容见 FindASan.cmakeTSan 与 MSan 也互不兼容见 FindTSan.cmake组合启用会直接FATAL_ERROR。3.2 编译器不支持时的行为如果编译器没有 sanitizer 能力例如 Intel 编译器模块会在配置期打印警告如WARNING: AddressSanitizer is not available for XXX compiler但CMake 继续执行构建照常进行只是该目标不带 sanitizer 编译——即尽力而为绝不阻断构建。这一逻辑实现在 sanitize-helpers.cmake 中探测不到可用标志时把对应编译器的 flags 缓存为空字符串并给出警告。3.3 混合编译器目标会自动跳过不同编译器对 sanitizer 的实现不同。如果某个目标同时混用 C 与 Fortran 源码却分别使用 clang 与 gfortran 编译就会在链接期出现 sanitizer 运行时库冲突。模块通过sanitizer_target_compilers()sanitize-helpers.cmake逐个解析目标源文件的扩展名、映射到 CMake 语言、收集实际使用的编译器 ID 集合一旦发现一个目标由多种不兼容编译器编译见 FindSanitizers.cmake会打印警告并对该目标禁用 sanitizer。3.4 静态链接开关SANITIZE_LINK_STATIC对 gcc 构建的目标如果出现 ASan 预加载或动态链接方面的问题例如运行时找不到 libasan可以开启静态链接cmake -DSANITIZE_ADDRESSOn -DSANITIZE_LINK_STATICOn ..该选项定义于 FindSanitizers.cmake。当启用且编译器为 GNU 时sanitize-helpers.cmake 会额外探测-static-libasan、-static-libtsan等-static-libsanitizer标志并合并进编译参数PREFIX会被转为小写例如 ASan →-static-libasan。四、为目标启用 sanitizer4.1 add_sanitizers()最核心的入口函数定义目标之后调用add_sanitizers(TARGET...)即可一次性为若干目标启用全部已开启的 sanitizerfind_package(Sanitizers) add_executable(some_exe foo.c bar.c) add_sanitizers(some_exe) add_library(some_lib foo.c bar.c) add_sanitizers(some_lib)函数内部逻辑见 FindSanitizers.cmake若四个开关全部为Off立即返回不产生任何副作用对每个目标先检查类型INTERFACE_LIBRARY无法被直接编译打印警告后跳过检查该目标是否由多种不兼容编译器编译是则跳过并警告分别调用add_sanitize_address()/add_sanitize_thread()/add_sanitize_memory()/add_sanitize_undefined()每个子函数内部会再次判断对应开关是否打开并把探测好的 flags 注入目标的编译与链接参数target_compile_options(... PUBLIC ...)与target_link_options(... PUBLIC ...)见 sanitize-helpers.cmake。因此只有被add_sanitizers()显式指定的目标才会带 sanitizer 编译其它目标不受影响——这保证了只 sanitize 想 sanitize 的目标。4.2 sanitizer_add_blacklist_file()黑名单文件某些第三方源码或特定文件不宜做 sanitize例如已知会产生误报的代码可以用黑名单排除sanitizer_add_blacklist_file(blacklist.txt)该函数见 FindSanitizers.cmake会把相对路径解析为CMAKE_CURRENT_SOURCE_DIR下的绝对路径再探测编译器是否支持-fsanitize-blacklistFILE标志支持则把该标志同样加入目标的编译参数。4.3 自带测试验证模块自身的冒烟用例模块自带一组冒烟测试tests/CMakeLists.txt其流程本身就是一个绝佳的用法示例set(SANITIZE_ADDRESS TRUE) find_package(Sanitizers) add_sanitized_testcase(asan_test_cpp asan_test.cpp) add_sanitized_testcase(shortest_ext_test_cpp shortest.ext.test.cpp) set_tests_properties(asan_test_cpp shortest_ext_test_cpp PROPERTIES WILL_FAIL TRUE)其中add_sanitized_testcase()先构造可执行目标再调用add_sanitizers()最后通过WILL_FAIL TRUE声明这些测试用例故意写出内存错误期望被 ASan 捕获后失败退出——这正是验证sanitizer 确实在工作的标准手法。shortest.ext.test.cpp则顺带覆盖了sanitizer_lang_of_source()对带多个点号的源文件扩展名解析逻辑。五、运行被 sanitize 的程序5.1 常规运行sanitizer 是在程序运行期进行检测的。构建完成后直接运行目标即可例如./some_exe一旦触发错误ASan/UBSan 等会在终端打印详细的错误报告出错源码位置、调用栈、问题类型并让进程以非零状态退出。5.2 asan-wrapperLD_PRELOAD 预加载某些场景下例如你本来就要LD_PRELOAD另一个库到目标程序里ASan 库需要被最先预加载才能正常工作。此时应使用模块提供的包装脚本 asan-wrapper通过ASan_WRAPPER变量获取其路径${ASan_WRAPPER} myexe arg1 ...脚本行为见 asan-wrapper非 Linux 平台直接透传执行exec $因为 macOS 等平台通常不受影响Linux 下先用ldd $1从目标程序依赖中提取出 libasan 的实际路径找到后把它前置到LD_PRELOAD已有值时拼接为$libasan:$LD_PRELOAD保证 libasan 最先加载最后exec $原样执行程序与参数。该脚本会在启用SANITIZE_ADDRESS时由 FindASan.cmake 通过find_program(ASan_WRAPPER asan-wrapper PATHS ${CMAKE_MODULE_PATH})自动定位。六、在 Fluent Bit / ChunkIO 中的实际打开方式结合前文 ChunkIO 的集成代码要在本仓库构建时开启 AddressSanitizer可以在 ChunkIO 根目录执行cmake -DCIO_SANITIZE_ADDRESSOn -DCIO_TESTSOn ..构建过程中会看到-- Enabling address sanitizer的提示随后 ChunkIO 的库与测试目标都会被注入-g -fsanitizeaddress -fno-omit-frame-pointer等参数。测试目标经add_sanitizers()挂载后运行ctest即可在内存错误发生时获得 ASan 报告。这同样适用于 Fluent Bit 主仓库的调试可在其顶层 CMake 配置中按需传入-DSANITIZE_ADDRESSOn并保证CMAKE_MODULE_PATH指向 lib/chunkio/cmake/sanitizers-cmake/cmake 目录。七、常见问题速查问题现象原因与处理配置时出现 not available for XXX compiler 警告编译器不支持该 sanitizer构建继续目标不带 sanitizer目标被警告跳过目标是INTERFACE_LIBRARY或由多种不兼容编译器编译如 clang gfortran 混用 C/FortranASan 运行时报预加载/动态链接错误加-DSANITIZE_LINK_STATICOn让 gcc 静态链接 libasanTSan/MSan 被自动关闭仅支持 64 位TSan 限 Linux/macOSMSan 限 Linux同时开启 ASan 与 TSan/MSan、或 TSan 与 MSan直接FATAL_ERROR二者互不兼容需要排除部分源码用sanitizer_add_blacklist_file(blacklist.txt)注入-fsanitize-blacklist需预加载场景用${ASan_WRAPPER} myexe arg1 ...运行结语sanitizers-cmake 的价值在于把探测编译器能力、挑选正确标志、注入编译与链接参数、处理平台与编译器组合限制这套繁琐且易错的流程封装成四个开关与两个函数让开发者把精力集中在修复问题而不是配置工具链上。本文所述的一切行为均可在仓库源码中逐行验证入口在 FindSanitizers.cmake探测与注入逻辑在 sanitize-helpers.cmake各 sanitizer 的开关与平台约束分别在 FindASan.cmake、FindTSan.cmake、FindMSan.cmake、FindUBSan.cmake实战集成参考 lib/chunkio/CMakeLists.txt。【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价