1 介绍
AddressSanitizer(ASan)是一种内存错误检测工具,可以帮助我们发现C/C++程序中的内存错误,如缓冲区溢出、使用已释放的内存、内存泄露等。
ThreadSanitizer(简称TSan)是一种用于检测并发程序中数据竞争的工具。可以帮助我们多线程数据竞争、死锁检测等。
GCC从4.8版本开始支持Address和Thread Sanitizer,简单的在makefile中追加相关的编译选项,就可以通过Address和Thread Sanitizer帮我们查找各种隐藏的bug。
2 ASan/TSan和另一个工具Valgrind/Helgrind的对比
优势:
- 性能开销:
ASan/TSan的性能开销通常比Valgrind/Helgrind低,ASan/TSan是通过编译时插入的代码来检测内存错误,因此在运行时的性能开销相对较小。相比之下,Valgrind/Helgrind通过将被检测程序的二进制代码解释执行,因此通常会引入更大的性能开销。
- 精确性:
ASan/TSan在编译时会直接插入检测代码到目标程序中,能够提供更准确的内存错误位置信息和更好的报告。Valgrind/Helgrind精度也很高,但由于其工作原理是基于程序外挂的方式,可能会产生一些误报或漏报。
劣势:
- 便利性:ASan/TSan需要对被测试程序和模块重新编译,而Valgrind/Helgrind可以以外挂方式运行
3 AddressSanitizer使用方法
CMakeLists.txt中启用ASan:
target_compile_options(hash_table_test PRIVATE -fsanitize=address -g1 -O0)
target_link_libraries(hash_table_test ${depend_libs} asan pthread)
启动时通过LD_PRELOAD预加载asan避免so加载顺序问题:
export LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/9/libasan.so
3.1 内存越界
内存越界示例代码
内存越界检测报告
3.2 内存泄漏
内存泄露示例代码
内存泄露检测报告
3.3 use after free
非法指针访问示例代码
非法指针访问检测报告
3.4 double free
double free示例代码
double free检测报告
4 ThreadSanitizer使用方法
4.1 简单的代码示例:
CMakeLists.txt中启用TSan:
target_compile_options(hash_table_test PRIVATE -fsanitize=thread -g1 -O0)
target_link_libraries(hash_table_test ${depend_libs} tsan pthread)
启动时通过LD_PRELOAD预加载tsan避免so加载顺序问题:
export LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/9/libtsan.so
4.2 data race检测
测试测序启动后,tsan能检测到share data的数据竞争(data race)。从输出的信息能看出,T1/T2线程对share_data的操作时机点,并且没有同步操作。
对于data race可以通过mutex或atomic改进。
4.3 死锁检测
上面的示例代码,虽然thread1在thread2运行前就会退出,实际上不会发生死锁,但代码逻辑上的潜在死锁问题,tsan能帮我们检查出来。
从输出的信息能看到M28和M29两把锁的持有状况和相关的线程T1/T2的创建信息。
注意:tsan的死锁检测是基于运行态(data race也一样),只有当thread2_main函数被执行到时,死锁问题才能被发现,所以需要完备的测试用例覆盖尽可能多的分支。