C/C++ Sanitizer工具
应用场景
编译参数通过 -fsanitize 决定开启 sanitizer:
-
-fsanitize=address
开启AddressSanitizer(ASan),包括LeakSanitizer(LSan),检测:地址越界 和 内存泄漏。 -
-fsanitize=leak
开启LeakSanitizer(LSan),检测:内存泄漏。
-fsanitize=address 和 -fsanitize=leak 都能检测 内存泄漏。 -
-fsanitize=thread
开启ThreadSanitizer(TSan),检测:数据竞争和死锁。经检测,并不能检测死锁问题 -
-fsanitize=undefined
开启UndefinedBehaviorSanitizer(UBSsan),检测:未定义行为。 -
-fsanitize=memory
开启MemorySanitizer(MSan),检测:未初始化内存问题。(gcc不支持MemorySanitizer) -
-fno-omit-frame-pointer
检测到内存错误时打印函数调用栈,这个参数一直都带上
AddressSanitizer
g++ test.cpp -o test -Wall -g -fsanitize=address -fno-omit-frame-pointer
-
-Wall
:打开所有的警告信息 -
-fno-omit-frame-pointer
:告诉编译器在生成的机器代码中保留栈帧指针,在函数调用期间,编译器会使用栈帧来管理局部变量、函数参数以及函数调用的返回地址等信息。帧指针(frame pointer)是指向当前栈帧的指针,它提供了对栈帧的访问。默认情况下,GCC会使用优化技术,如寄存器相对地址寻址(register-relative addressing)等,来减少生成的机器代码的大小和执行时间。这种优化可能会导致省略帧指针,因为它可以使用其他寄存器来访问栈帧的内容。 -
-fsanitize=address
:用于检测和调试内存错误。AddressSanitizer是一种运行时工具,可以检测到许多内存错误,包括堆缓冲区溢出、使用已释放的内存、使用未初始化的内存等。它通过在编译时将额外的代码插入到程序中,来跟踪和检测内存访问问题。 -
-g
:开启debug模式
开启ThreadSanitizer(TSan)
检测:数据竞争和死锁
Data Race是指多个线程在没有正确加锁的情况下,同时访问同一块数据,并且至少有一个线程是写操作,对数据的读取和修改产生了竞争,从而导致各种不可预计的问题。
Data Race的问题非常难查,Data Race一旦发生,结果是不可预期的,也许直接就Crash了,也许导致执行流程错乱了,也许把内存破坏导致之后某个时刻突然Crash了
编译指令
g++ ./deadlock.cpp -g -fsanitize=thread -fno-omit-frame-pointer -o deadlock
#include <iostream>
#include <thread>
using namespace std;
void test1(int& date){
for(int i=0;i<5;i++){
date++;
}
std::cout << "test1"<< std::endl;
}
void test2(int& date){
for(int i=0;i<5;i++){
date++;
}
std::cout << "test2"<< std::endl;
}
// g++ ./deadlock.cpp -g -fsanitize=thread -fno-omit-frame-pointer -o deadlock
int main(){
int a=0;
std::thread thread1(test1, std::ref(a));
std::thread thread2(test2, std::ref(a));
thread1.join();
thread2.join();
return 0;
}
输出信息
test1
==================
WARNING: ThreadSanitizer: data race (pid=16915)
Read of size 4 at 0x7ffc9be2705c by thread T2:
#0 test2(int&) deadlock.cpp:36 (deadlock+0x1482)
#1 void std::__invoke_impl<void, void (*)(int&), std::reference_wrapper<int> >(std::__invoke_other, void (*&&)(int&), std::reference_wrapper<int>&&) /usr/include/c++/9/bits/invoke.h:60 (deadlock+0x29ae)
#2 std::__invoke_result<void (*)(int&), std::reference_wrapper<int> >::type std::__invoke<void (*)(int&), std::reference_wrapper<int> >(void (*&&)(int&), std::reference_wrapper<int>&&) /usr/include/c++/9/bits/invoke.h:95 (deadlock+0x2884)
#3 void std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/include/c++/9/thread:244 (deadlock+0x276e)
#4 std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > >::operator()() /usr/include/c++/9/thread:251 (deadlock+0x26e1)
#5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > > >::_M_run() /usr/include/c++/9/thread:195 (deadlock+0x2684)
#6 <null> <null> (libstdc++.so.6+0xdc252)
Previous write of size 4 at 0x7ffc9be2705c by thread T1:
#0 test1(int&) deadlock.cpp:22 (deadlock+0x13f3)
#1 void std::__invoke_impl<void, void (*)(int&), std::reference_wrapper<int> >(std::__invoke_other, void (*&&)(int&), std::reference_wrapper<int>&&) /usr/include/c++/9/bits/invoke.h:60 (deadlock+0x29ae)
#2 std::__invoke_result<void (*)(int&), std::reference_wrapper<int> >::type std::__invoke<void (*)(int&), std::reference_wrapper<int> >(void (*&&)(int&), std::reference_wrapper<int>&&) /usr/include/c++/9/bits/invoke.h:95 (deadlock+0x2884)
#3 void std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/include/c++/9/thread:244 (deadlock+0x276e)
#4 std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > >::operator()() /usr/include/c++/9/thread:251 (deadlock+0x26e1)
#5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(int&), std::reference_wrapper<int> > > >::_M_run() /usr/include/c++/9/thread:195 (deadlock+0x2684)
#6 <null> <null> (libstdc++.so.6+0xdc252)
Location is stack of main thread.
Location is global '<null>' at 0x000000000000 ([stack]+0x00000002005c)
Thread T2 (tid=16918, running) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc328)
#2 main deadlock.cpp:46 (deadlock+0x1579)
Thread T1 (tid=16917, finished) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc328)
#2 main deadlock.cpp:45 (deadlock+0x1552)
SUMMARY: ThreadSanitizer: data race deadlock.cpp:36 in test2(int&)
==================
test2
ThreadSanitizer: reported 1 warnings