Jakie są najlepsze praktyki wykrywania wycieków pamięci w niezarządzanym kodzie C / C ++? I wskazówek dotyczących kodowania, których należy unikać? (Jakby to było takie proste;)
W przeszłości używaliśmy trochę głupiego sposobu: dysponowania przyrostem licznika dla każdego wywołania alokacji pamięci i zmniejszaniem podczas zwalniania. Pod koniec programu stan licznika powinien wynosić zero.
Wiem, że to nie jest świetny sposób i jest kilka haczyków. (Na przykład, jeśli zwalniasz pamięć, która została przydzielona przez wywołanie interfejsu API platformy, liczba przydziałów nie będzie dokładnie odpowiadać liczbie zwolnień. Oczywiście zwiększaliśmy licznik podczas wywoływania wywołań API, które przydzielają pamięć).
Oczekuję Waszych doświadczeń, sugestii i być może odniesień do narzędzi, które to upraszczają.
źródło
Odpowiedzi:
Jeśli twój kod C / C ++ jest przenośny do * nix, kilka rzeczy jest lepszych niż Valgrind .
źródło
Jeśli korzystasz z programu Visual Studio, firma Microsoft udostępnia przydatne funkcje do wykrywania i debugowania wycieków pamięci.
Zacząłbym od tego artykułu: https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx
Oto krótkie podsumowanie tych artykułów. Najpierw uwzględnij te nagłówki:
Następnie musisz wywołać to, gdy program kończy pracę:
Alternatywnie, jeśli twój program nie kończy się za każdym razem w tym samym miejscu, możesz wywołać to na początku swojego programu:
Teraz, gdy program zakończy pracę, wszystkie alokacje, które nie były wolne, zostaną wydrukowane w oknie wyjściowym wraz z plikiem, w którym zostały przydzielone, oraz wystąpieniem alokacji.
Ta strategia działa w przypadku większości programów. Jednak w niektórych przypadkach staje się to trudne lub niemożliwe. Korzystanie z bibliotek innych firm, które wykonują pewne inicjalizacje podczas uruchamiania, może spowodować pojawienie się innych obiektów w zrzucie pamięci i może utrudnić śledzenie wycieków. Ponadto, jeśli którakolwiek z twoich klas ma członków o takiej samej nazwie jak którakolwiek z procedur alokacji pamięci (takich jak malloc), makra debugowania CRT spowodują problemy.
Istnieją inne techniki wyjaśnione w odnośniku MSDN, o którym mowa powyżej, które również mogą być użyte.
źródło
W C ++: użyj RAII. Inteligentne wskaźniki podoba
std::unique_ptr
,std::shared_ptr
,std::weak_ptr
są twoimi przyjaciółmi.źródło
Jako programista C ++ oto kilka prostych wskazówek:
Jeśli chodzi o wykrywanie wycieków pamięci osobiście, zawsze korzystałem z Visual Leak Detector i uważam, że jest bardzo przydatny.
źródło
Używam DevStudio od zbyt wielu lat i zawsze mnie zadziwia, ilu programistów nie wie o narzędziach do analizy pamięci, które są dostępne w bibliotekach uruchomieniowych debugowania. Oto kilka linków, od których możesz zacząć:
Śledzenie żądań alokacji sterty - w szczególności sekcja dotycząca unikalnych numerów zleceń alokacji
_CrtSetDbgFlag
_CrtSetBreakAlloc
Oczywiście, jeśli nie używasz DevStudio, nie będzie to szczególnie pomocne.
źródło
Jestem zdumiony, że nikt nie wspomniał o DebugDiag dla systemu operacyjnego Windows.
Działa w kompilacjach wydań, a nawet w witrynie klienta.
(Wystarczy zachować PDB wersji wydania i skonfigurować DebugDiag do korzystania z publicznego serwera symboli firmy Microsoft)
źródło
Visual Leak Detector jest bardzo dobrym narzędziem, chociaż nie obsługuje wywołań w środowiskach wykonawczych VC9 (na przykład MSVCR90D.DLL).
źródło
Microsoft VC ++ w trybie debugowania pokazuje wycieki pamięci, chociaż nie pokazuje, gdzie są wycieki.
Jeśli używasz C ++ zawsze można uniknąć stosując nowy wyraźnie: trzeba
vector
,string
,auto_ptr
(pre C ++ 11, zastąpioneunique_ptr
w C ++ 11),unique_ptr
(C ++ 11) ishared_ptr
(C ++ 11) w swoim arsenale.Kiedy nowy jest nieunikniony, spróbuj ukryć go w konstruktorze (i ukryj usuń w destruktorze); to samo dotyczy interfejsów API innych firm.
źródło
Istnieją różne zastępcze biblioteki "malloc", które pozwolą ci wywołać funkcję na końcu i powie ci o całej nieuleczonej pamięci, aw wielu przypadkach, kto ją pobrał (lub nowy) w pierwszej kolejności .
źródło
Jeśli używasz MS VC ++, gorąco polecam to bezpłatne narzędzie z projektu code: leakfinder autorstwa Jochena Kalmbacha.
Po prostu dodajesz klasę do swojego projektu i dzwonisz
przed i po kodzie, który chcesz sprawdzić pod kątem wycieków.
Po skompilowaniu i uruchomieniu kodu Jochen zapewnia zgrabne narzędzie GUI, w którym można załadować wynikowy plik .xmlleaks i nawigować po stosie wywołań, w którym został wygenerowany każdy wyciek, w celu wyszukania niewłaściwego wiersza kodu.
PurifyPlus firmy Rational (obecnie należący do IBM) ilustruje wycieki w podobny sposób, ale uważam, że narzędzie do wykrywania wycieków jest rzeczywiście łatwiejsze w użyciu, a jego premia nie kosztuje kilku tysięcy dolarów!
źródło
Sam nigdy tego nie używałem, ale moi przyjaciele z C mówią mi Purify .
źródło
Jeśli używasz programu Visual Studio, warto przyjrzeć się narzędziu Bounds Checker . To nie jest darmowe, ale było niezwykle pomocne w znajdowaniu wycieków w moim kodzie. Nie tylko powodują wycieki pamięci, ale także wycieki zasobów GDI, błędy użytkowania WinAPI i inne rzeczy. Pokaże nawet, gdzie zainicjowano wyciek pamięci, co znacznie ułatwia wyśledzenie wycieku.
źródło
Myślę, że nie ma łatwej odpowiedzi na to pytanie. To, jak naprawdę możesz podejść do tego rozwiązania, zależy od Twoich wymagań. Czy potrzebujesz rozwiązania wieloplatformowego? Czy używasz nowego / delete lub malloc / free (lub obu)? Czy naprawdę szukasz tylko „wycieków”, czy też potrzebujesz lepszej ochrony, takiej jak wykrywanie przepełnień bufora (lub niedopełnienia)?
Jeśli pracujesz po stronie systemu Windows, biblioteki środowiska uruchomieniowego MS debug mają pewne podstawowe funkcje wykrywania debugowania, a jak inny już wskazał, istnieje kilka opakowań, które można dołączyć do źródła, aby pomóc w wykrywaniu wycieków. Znalezienie pakietu, który może współpracować zarówno z new / delete, jak i malloc / free, oczywiście zapewnia większą elastyczność.
Nie wiem wystarczająco dużo o stronie uniksowej, aby zapewnić pomoc, chociaż znowu inni.
Ale poza samym wykrywaniem wycieków istnieje pojęcie wykrywania uszkodzeń pamięci poprzez przepełnienia bufora (lub niedomiar). Myślę, że ten typ funkcji debugowania jest trudniejszy niż zwykłe wykrywanie wycieków. Ten typ systemu jest również bardziej skomplikowany, jeśli pracujesz z obiektami C ++, ponieważ klasy polimorficzne można usuwać na różne sposoby, co powoduje trudności w określeniu prawdziwego wskaźnika podstawowego, który jest usuwany. Nie znam dobrego „darmowego” systemu, który zapewnia dobrą ochronę przed przekroczeniami. napisaliśmy system (wieloplatformowy) i stwierdziliśmy, że jest to dość trudne.
źródło
Chciałbym zaoferować coś, z czego czasami korzystałem w przeszłości: podstawowy program do sprawdzania wycieków, który jest na poziomie źródła i dość automatyczny. Oddaję to z trzech powodów:
Może ci się to przydać.
Chociaż to trochę krufty, nie pozwalam, żeby mnie to zawstydziło.
Mimo że jest powiązany z niektórymi hakami win32, powinno to być łatwe do złagodzenia.
Są rzeczy, na które musisz uważać podczas korzystania z niego: nie rób niczego, na czym trzeba polegać
new
w kodzie bazowym, uważaj na ostrzeżenia o przypadkach, które mogą przeoczyć na początku leakcheck.cpp, zdaj sobie sprawę, że jeśli włączysz na (i napraw wszelkie problemy z) kodem, który wykonuje zrzuty obrazu, możesz wygenerować ogromny plik.Projekt ma umożliwiać włączanie i wyłączanie kontrolera bez ponownej kompilacji wszystkiego, co zawiera jego nagłówek. Uwzględnij leakcheck.h, gdzie chcesz prześledzić sprawdzanie i raz odbudować. Następnie skompiluj leakcheck.cpp z lub bez definicji LEAKCHECK #, a następnie połącz ponownie, aby go włączyć i wyłączyć. Dołączenie unleakcheck.h spowoduje wyłączenie go lokalnie w pliku. Dostępne są dwa makra: CLEARALLOCINFO () pozwoli uniknąć nieprawidłowego raportowania tego samego pliku i wiersza podczas przeglądania kodu przydzielającego, który nie zawiera przecieku. ALLOCFENCE () po prostu upuszcza wiersz w wygenerowanym raporcie bez dokonywania alokacji.
Ponownie, proszę, uświadom sobie, że nie używałem tego od jakiegoś czasu i być może będziesz musiał z tym trochę popracować. Wrzucam to, żeby zilustrować ten pomysł. Jeśli zainteresowanie okaże się wystarczające, zechciałbym opracować przykład, zaktualizować kod w trakcie i zastąpić zawartość następującego adresu URL czymś ładniejszym, co zawiera listę w przyzwoitym kolorze składni.
Znajdziesz go tutaj: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html
źródło
Linux: wypróbuj narzędzia Google Perftools
Istnieje wiele narzędzi, które wykonują podobne liczenie przydziałów / bezpłatnych zasobów, zalety Goolge Perftools:
źródło
Najlepszą ochroną przed wyciekami jest struktura programu, która minimalizuje użycie malloc. Jest to nie tylko dobre z punktu widzenia programowania, ale także poprawia wydajność i łatwość konserwacji. Nie mówię o używaniu innych rzeczy zamiast malloc, ale pod względem ponownego używania obiektów i utrzymywania bardzo wyraźnych zakładek na wszystkich przekazywanych obiektach, zamiast przydzielania chcąc nie chcąc, jak często się przyzwyczaja w językach ze śmieciami jak Java.
Na przykład program, nad którym pracuję, ma kilka obiektów ramek reprezentujących dane obrazu. Każdy obiekt ramki ma dane podrzędne, które są zwalniane przez destruktor ramki. Program przechowuje listę wszystkich przydzielonych ramek, a gdy potrzebuje nowej, sprawdza listę nieużywanych obiektów ramek, aby zobaczyć, czy może ponownie wykorzystać istniejącą, zamiast przydzielić nową. Po zamknięciu po prostu iteruje listę, zwalniając wszystko.
źródło
Polecam użycie Memory Validator z weryfikacji oprogramowania. To narzędzie okazało się nieocenioną pomocą, pomagając mi wykrywać wycieki pamięci i usprawniać zarządzanie pamięcią aplikacji, nad którymi pracuję.
Bardzo kompletne i szybkie narzędzie.
źródło
Czy liczysz alokacje i zwolnienia, interpolując własne funkcje wywołań systemowych, które rejestrują wywołania, a następnie przekazują je do funkcji rzeczywistej?
Tylko w ten sposób możesz śledzić wywołania pochodzące z kodu, którego nie napisałeś.
Zajrzyj na stronę podręcznika systemowego ld.so. Lub ld.so.1 w niektórych systemach.
Wykonaj również Google LD_PRELOAD, a znajdziesz kilka interesujących artykułów wyjaśniających tę technikę na www.itworld.com.
źródło
Przynajmniej w przypadku MS VC ++ biblioteka C Runtime ma kilka funkcji, które w przeszłości okazały się pomocne. Sprawdź pomoc MSDN dotyczącą
_Crt*
funkcji.źródło
MMG Paula Nettle jest moim ulubionym narzędziem od dawna. Dołączasz mmgr.h do swoich plików źródłowych, definiujesz TEST_MEMORY i dostarcza on plik tekstowy pełen problemów z pamięcią, które wystąpiły podczas uruchamiania aplikacji.
źródło
Ogólne wytyczne dotyczące kodowania:
źródło
Narzędzia do debugowania pamięci są na wagę złota, ale przez lata odkryłem, że można zastosować dwa proste pomysły, aby zapobiec zakodowaniu większości wycieków pamięci / zasobów.
Napisz kod wydania natychmiast po napisaniu kodu nabycia zasobów, które chcesz przydzielić. Dzięki tej metodzie trudniej jest „zapomnieć” i w pewnym sensie zmusza do poważnego myślenia o cyklu życia zasobów wykorzystywanych z góry, a nie tylko na boku.
Używaj powrotu tak oszczędnie, jak to możliwe. To, co zostało przydzielone, powinno zostać uwolnione tylko w jednym miejscu, jeśli to możliwe. Warunkowa ścieżka między nabyciem zasobu a uwolnieniem powinna być tak prosta i oczywista, jak to tylko możliwe.
źródło
Na szczycie tej listy (kiedy ją czytałem) był valgrind. Valgrind jest doskonały, jeśli jesteś w stanie odtworzyć wyciek w systemie testowym. Użyłem go z wielkim sukcesem.
A co, jeśli właśnie zauważyłeś, że system produkcyjny przecieka teraz i nie masz pojęcia, jak go odtworzyć w testach? Pewne dowody na to, co jest nie tak, są uchwycone w stanie tego systemu produkcyjnego i może wystarczyć, aby uzyskać wgląd w to, gdzie jest problem, aby można było go odtworzyć.
W tym miejscu pojawia się samplowanie Monte Carlo. Przeczytaj artykuł na blogu Raymonda Chena „The poor man's way to identifying memory leaks”, a następnie sprawdź moją implementację (zakładam, że Linux, testowany tylko na x86 i x86-64)
http://github.com/tialaramex/leakuesday/tree/master
źródło
Pracując na systemie operacyjnym telefonów komórkowych Motorola, przejęliśmy bibliotekę alokacji pamięci, aby obserwować wszystkie alokacje pamięci. Pomogło to znaleźć wiele problemów z alokacją pamięci. Ponieważ lepiej zapobiegać niż leczyć, zalecałbym użycie narzędzia do analizy statycznej, takiego jak Klockwork lub PC-Lint
źródło
lint
. Ma wiele kontroli specyficznych dla C ++, których nie ma afaik splint. Zobacz link w odpowiedzi (którego nazwę zmieniłem z Lint na PC-Lint).Valgrind to fajna opcja dla Linuksa. W MacOS X możesz włączyć bibliotekę MallocDebug, która ma kilka opcji debugowania problemów z alokacją pamięci (zobacz stronę podręcznika malloc, sekcja "ŚRODOWISKO" zawiera odpowiednie szczegóły). Zestaw OS X SDK zawiera również narzędzie o nazwie MallocDebug (zwykle instalowane w / Developer / Applications / Performance Tools /), które może pomóc w monitorowaniu użycia i wycieków.
źródło
Wykryć:
Debuguj CRT
Uniknąć:
Inteligentne wskaźniki, boehm GC
źródło
Przyjemnym zamiennikiem malloc, calloc i reallloc jest rmdebug, jest całkiem prosty w użyciu. Jest znacznie szybszy niż valgrind, więc możesz szeroko przetestować swój kod. Oczywiście ma to pewne wady, po znalezieniu wycieku prawdopodobnie nadal będziesz musiał użyć valgrind, aby znaleźć miejsce wycieku, i możesz testować tylko mallocs, które robisz bezpośrednio. Jeśli biblioteka wycieknie, ponieważ użyjesz jej nieprawidłowo, rmdebug jej nie znajdzie.
http://www.hexco.de/rmdebug/
źródło
Większość programów profilujących pamięć spowalnia moją dużą, złożoną aplikację Windows do punktu, w którym wyniki są bezużyteczne. Jest jedno narzędzie, które działa dobrze do znajdowania wycieków w mojej aplikacji: UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx
źródło
Wydaje się, że Mtrace jest standardem wbudowanym w Linuksa. Kroki są następujące:
MALLOC_TRACE = / tmp / mtrace.dat
export MALLOC_TRACE;
mtrace your_prog_exe_name /tmp/mtrace.dat
(musiałem najpierw zainstalować skrypt mtrace perl na moim systemie fedora za pomocą yum install glibc_utils )
źródło