LeakSanitizer: получать отчеты об утечках во время выполнения?

Я унаследовал некоторый устаревший код, и кажется, что где-то произошла утечка памяти. Мой первый инстинкт был просто скомпилировать

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

и позвольте семейству инструментов Address Sanitizer найти утечку для меня. Однако я был сильно разочарован. Я надеялся на какое-то сообщение об ошибке во время выполнения (похоже на ошибку адресного дезинфицирующего средства, когда вы не читаете или не записываете память). Дезинфицирующее средство для устранения утечек, по-видимому, не выполняет никакого анализа на наличие утечек, пока программа не будет успешно завершена. Моя проблема в том, что у меня есть несколько потоков, которые я унаследовал, и он не был предназначен для объединения их всех в рамках подготовки к мягкой посадке.

Я упростил свою проблему на простом примере:

#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;                                                
}    

Я собираю это с

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

А потом побежал с

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

(Я как-то с ума сошел о "LSAN_OPTIONS" здесь, потому что я играл вокруг... ни один из вариантов не сделал то, что я хотел, а должен был выйти при обнаружении утечки).

Как отмечено в коде, если я присоединяюсь к потоку, а затем выхожу из программы, я получаю довольно отчет об утечке. Иначе я ничего не получу. Поскольку вы можете визуализировать отслеживание 10-100 потоков в унаследованной кодовой базе и заставить их все хорошо работать, это громоздко.

Несколько лет назад я помню, как играл с Visual Leak Detector, и мне повезло с ним, потому что он генерировал отчеты со всеми потенциальными утечками памяти (и я не помнил, чтобы все улаживалось хорошо). Проблема в том, что этот инструмент предназначен только для Windows, а мой код работает только на Linux. Можно ли заставить инструмент LeakSanitizer сделать что-то подобное?

1 ответ

Решение

Открытый интерфейс 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
Другие вопросы по тегам