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?
The public interface of LeakSanitizer (sanitizer/lsan_interface.h) has various functions that may fit your needs. Function __lsan_do_leak_check()
performs a check and terminates when a leak is found. There is also __lsan_do_recoverable_leak_check
which does not terminate and can be called multiple times.
Consider this modification to your program:
#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;
}
Output:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With