如何使用LeakSanitizer (LSan) 在运行时检测c++内存泄漏? (独立工具)

12次阅读

LeakSanitizer必须与AddressSanitizer联用,因LSan是libasan内部组件而非独立库;单独使用-fsanitize=leak无效,需配合-fsanitize=address及ASAN_OPTIONS=detect_leaks=1。

如何使用LeakSanitizer (LSan) 在运行时检测c++内存泄漏? (独立工具)

LeakSanitizer 不是独立工具,它必须与 AddressSanitizer(ASan)一起编译并运行;单独启用 -fsanitize=leak 无效,链接时会报错或静默忽略泄漏检测。

为什么 -fsanitize=leak 单独不工作

LSan 是 LLVM/Clang 中 ASan 运行时库的一部分,不是可单独加载的 sanitizer。即使你写 -fsanitize=leak,Clang 实际仍会链接 libasan,且仅当 ASAN_OPTIONS=detect_leaks=1(默认开启)时才激活泄漏扫描。GCC 的 LSan 支持更弱,仅在较新版本(≥9)中作为 ASan 的子功能存在,且依赖 libasan

  • -fsanitize=leak 在 Clang 中只是个“占位符”,不触发独立链接行为
  • GCC 8 及更早版本完全不支持 LSan;GCC 9+ 需同时启用 -fsanitize=address
  • 没有 liblsan.so 独立库 —— 所有逻辑都在 libasan 内部,通过环境变量开关

正确启用 LSan 的编译与运行命令

以 Clang 为例,必须启用 ASan 并确保泄漏检测未被禁用:

clang++ -g -O1 -fsanitize=address -fno-omit-frame-pointer main.cpp ASAN_OPTIONS=detect_leaks=1 ./a.out

关键点:

立即学习C++免费学习笔记(深入)”;

  • -fno-omit-frame-pointer 强烈建议加上,否则追踪可能为空或错乱
  • -O1 是推荐优化级:更高(如 -O2)可能导致内联掩盖泄漏源头
  • detect_leaks=1 是默认值,但显式写出更可靠(尤其 CI 环境中 ASAN_OPTIONS 可能被覆盖)
  • 若程序调用 exit() 或 _Exit(),LSan 可能来不及报告 —— 应让 main() 正常返回

常见误报、漏报与绕过场景

LSan 不是万能的,它只扫描进程退出时仍可达的堆内存块,且依赖符号与调试信息:

  • 全局指针指向 new 出的内存 → 被视为“仍存活”,**不算泄漏**(这是设计行为,非 bug
  • 使用 mmap(MAP_ANONYMOUS)malloc 后被 dlclose 卸载的共享库中分配的内存 → **可能漏报**(LSan 不扫描 dlopen 区域)
  • 未带 -g 编译 → 泄漏报告只有地址,无文件/行号,难以定位
  • 程序被 kill -9 终止 → LSan 完全无机会运行,**零报告**
  • c++ Static 对象析构函数中发生的 new → 若析构顺序导致指针丢失,可能被误判为泄漏

如何确认 LSan 实际生效

最直接方式:制造一个明确泄漏,看是否报出 indirect leakdirect leak

#include  int main() {     new int[100];  // 故意泄漏     return 0;      // 让 LSan 有机会扫描 }

运行后应看到类似输出:

================================================================= ==12345==ERROR: LeakSanitizer: detected memory leaks Direct leak of 400 byte(s) in 1 object(s) allocated from:     #0 0x... in operator new[](unsigned long) .../libasan.so.5     #1 0x... in main (.../test.cpp:3)

若没输出,检查:

  • 是否用了 ASAN_OPTIONS=detect_leaks=0(包括 shell 环境中残留设置)
  • 是否链接了 -fsanitize=address,而非仅 =leak
  • 是否在 main 返回前调用了 _exit()std::quick_exit()

LSan 的“泄漏”定义严格依赖程序终止时的可达性分析,不是所有未 delete 的内存都会报 —— 它只报那些既没被/寄存器/全局变量引用,又没被其他活对象间接持有的内存。这点容易误解,但恰恰是它低误报率的基础。

text=ZqhQzanResources