C/C++ Sanitizer工具

发布时间 2023-08-07 16:34:17作者: 小小灰迪

应用场景

编译参数通过 -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