C/C++ 内存治理神器 - Google Sanitizers
Posted 芥末的无奈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++ 内存治理神器 - Google Sanitizers相关的知识,希望对你有一定的参考价值。
文章目录
简介
Google sanitizers 是由 Google 设计的用于动态代码分析的工具。它属于 LLVM 一部分,并且是开源的。它包括
- AddressSanitizer (ASan),检测内存问题,包括了 LeakSanitizer
- LeakSanitizer (LSan),检测内存泄漏问题
- ThreadSanitizer (TSan),检测数据竞争问题
- UndefinedBehaviorSanitizer (UBSsan),检测未定义行为
- MemorySanitizer (MSan),检测未初始化内存问题
Sanitizers 在 Clang(3.1 版本开始)和 GCC(4.8 版本开始)中实现。目前,在 Linux x86_64 上支持所有 sanitizers;在 Windows 10 下可以使用 MSVC 工具链下的 clang-cl 来使用 AddressSanitizer;对于 macOS,支持 AddressSanitizer、ThreadSanitizer 和 UndefinedBehaviorSanitizer
使用
使用非常简单,只需要添加编译 sanitize 编译选项即可,例如你可以在 CMakeLists.txt 中这么写:
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -fsanitize=address")
通过 -fsanitize
来选择开启哪个 sanitizer,选项包括:
address
开启 AddressSanitizerleak
开启 LeakSanitizerthread
开启 ThreadSanitizerundefined
开启 UndefinedBehaviorSanitizermemory
开启 MemorySanitizer
我们通常在 Debug 模式下使用 Sanitizers,这样方便定位出问题的代码位置。
日常开发中,我们最常用的是 Asan,它能够帮助的我们分析内存问题,包括:
- Use after free(dangling pointer dereference):内存释放后继续使用,悬挂指针问题。
- Heap buffer overflow:堆内存溢出
- Stack buffer overflow:栈内存溢出
- Global buffer overflow:全局内存溢出(如全局变量)
- Use after return:局部变量在函数返回后使用
- Use after scope:局部变量在作用范围外使用
- Initialization order bugs:初始化顺序问题
- Memory leaks:内存泄漏
在 AddressSanitizer 列举了上述经典错误的代码片段,例如内存后继续使用:
// RUN: clang -O -g -fsanitize=address %t && ./a.out
int main(int argc, char **argv)
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
内存泄漏的检查,这里需要特别说明一下。AddressSanitizer 本身包含了 LeakSanitizer,你可以在运行程序时添加 ASAN_OPTIONS=detect_leaks=1
开启它。例如
// RUN: clang -O -g -fsanitize=address main.cpp && ASAN_OPTIONS=detect_leaks=1 ./a.out
#include <stdlib.h>
void *p;
int main()
p = malloc(7);
p = 0; // The memory is leaked here.
return 0;
当然,你还可以单独使用 LeakSanitizer 来检测内存泄漏问题,例如:
clang -O -g -fsanitize=leak main.cpp && ./a.out
在 Mac 下使用内存泄漏检查
在 macOS 下,默认使用的 AppleClang,但遗憾的是,AppleClang 不支持 LeakSanitizer,在开启 LeakSanitizer 时它会报错:
解决的办法非常简单,我们不适用 AppleClang 而是之间适用 llvm 中的 clang 就好。具体的,首先安装 llvm,具体版本视情况而定:
brew install llvm@13
安装后,llvm clang++ 在我机器上的位置在 /opt/homebrew/opt/llvm@13/bin/clang++
,
直接适用 llvm clag++ 进行编译即可:
/opt/homebrew/opt/llvm@13/bin/clang++ -O -g -fsanitize=address main.cpp -o main
如果用 cmake 编译,可以在 CMakeLists.txt 中指定 CMAKE_CXX_COMPILER
为 llvm clang++:
set(CMAKE_CXX_COMPILER "/opt/homebrew/Cellar/llvm@13/13.0.1/bin/clang++")
在 CLion 下使用
CLion 中集成了对 Sanitizers 支持,具体参考 CLion - Google sanitizers 。简单来说,你可以在 CMakeLists.txt 中添加一个开关来添加 Asan 编译选项,例如:
cmake_minimum_required(VERSION 3.21)
project(mem_leak_test)
set(CMAKE_CXX_STANDARD 14)
if (ENABLE_ASAN)
message(STATUS "build with ASAN")
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -fsanitize=address")
endif ()
add_executable(mem_leak_test main.cpp)
然后在配置 CMake ,传入 ENABLE_ASAN
即可:
同理,如果你在 macOS 上使用 llvm clang++,也可以在配置中指定 compiler 的路径:
设置完毕后,之间运行代码,如果出现内存问题,CLion 会在 Sanitizers 窗口中提示信息:
在 android 中使用
目前 NDK 中已经支持了 ASAN 的使用,具体参考 Address Sanitizer ,我提供了 Android ASAN demo 可以参考。但并不支持 memory leak 的检查。
当 ASan 检查到内存问题时,app 会 crash,并打印 log,通过 log 信息,你可以定位到崩溃的代码地址,再通过 addr2line 来获取具体的代码行号:
参考
以上是关于C/C++ 内存治理神器 - Google Sanitizers的主要内容,如果未能解决你的问题,请参考以下文章