6

I have inherited some legacy code and it seems to have a memory leak somewhere. My first instinct was to just compile with

-faddress=sanitize -fno-omit-frame-pointer

and let the Address Sanitizer's family of tools find the leak for me. I was sorely disappointed, however. I was hoping for some sort of run time error message (similar to the address sanitizer's error when you read or write memory you shouldn't). The leak sanitizer doesn't seem to do any leak checking analysis until after the program has finished successfully. My issue is the code I've inherited has several threads and it wasn't designed to join all of them in preparation for a soft landing.

I've simplified my problem in a simple example:

#include <thread>                                           
#include <chrono>                                           
#include <iostream>                                         

bool exit_thread = false;                                   

void threadFunc()                                           
{                                                           
   while(!exit_thread)                                      
   {                                                        
      char* leak = new char[256];                           
      std::this_thread::sleep_for(std::chrono::seconds{1}); 
   }                                                        
}                                                           

int main() {                                                
   std::thread t(threadFunc);                               
   std::cout << "Waiting\n";                                
   std::this_thread::sleep_for(std::chrono::seconds{5});    
   exit_thread = true;                                      
   std::cout << "Exiting\n";                                
   //Without joining here I do not get the leak report.     
   t.join();                                                
   return 0;                                                
}    

I compile this with

clang++ leaker.cpp -fsanitize=address -fno-omit-frame-pointer -g -O0 -std=c++1y -o leaker      

And then ran with

ASAN_OPTIONS='detect_leaks=1' LSAN_OPTIONS='exitcode=55:report_objects=true:log_threads=true:log_pointers=true' ./leaker                                                

(I kinda went wild on the "LSAN_OPTIONS" here because I was playing around ... none of the options did what I wanted however which was to exit upon learning of a leak).

As noted in the code, if I join on the thread then exit the program, I get a pretty leak report. Otherwise I get nothing. As you can imaging tracking down 10-100 threads in a legacy code base and making them all go down nicely is unwieldy.

A few years ago I remember playing around with Visual Leak Detector and had good luck with it because it would generate reports with all the potential memory leaks (and I didn't remember having to take everything down nicely). Problem is this tool is only for Windows and my code only works on Linux. Can I make the LeakSanitizer tool do something similar?

4

1 回答 1

7

LeakSanitizer 的公共接口(sanitizer/lsan_interface.h)具有各种可能满足您需求的功能。函数__lsan_do_leak_check()执行检查并在发现泄漏时终止。还有__lsan_do_recoverable_leak_check一个不会终止,可以多次调用。

考虑对您的程序进行以下修改:

#include <thread>
#include <chrono>
#include <iostream>
#include <sanitizer/lsan_interface.h>

bool exit_thread = false;

void threadFunc()
{
   while(!exit_thread)
   {
      char* leak = new char[256];
      std::this_thread::sleep_for(std::chrono::seconds{1});
   }
}

int main() {
   std::thread t(threadFunc);
   std::cout << "Waiting\n";
   std::this_thread::sleep_for(std::chrono::seconds{5});
   exit_thread = true;
   std::cout << "Exiting\n";
   //Without joining here I do not get the leak report.
   //t.join();
   __lsan_do_recoverable_leak_check();
   std::cout << "Done\n";
   return 0;
}

输出:

Waiting
Exiting

=================================================================
==29240==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1024 byte(s) in 4 object(s) allocated from:
    #0 0x4d9a30 in operator new[](unsigned long) (leaker+0x4d9a30)
    #1 0x4dc663 in threadFunc() leaker.cpp:12:20
    #2 0x4dffe3 in void std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/functional:1530:18
    #3 0x4dff94 in std::_Bind_simple<void (*())()>::operator()() /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/functional:1520:16
    #4 0x4dfcc8 in std::thread::_Impl<std::_Bind_simple<void (*())()> >::_M_run() /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/thread:115:13
    #5 0x7f0a9664034f in execute_native_thread_routine /build/gcc-multilib/src/gcc-5.2.0/libstdc++-v3/src/c++11/thread.cc:84

SUMMARY: AddressSanitizer: 1024 byte(s) leaked in 4 allocation(s).
Done
terminate called without an active exception
Aborted
于 2015-11-06T14:41:20.817 回答