Jak wykrywać / unikać wycieków pamięci w (niezarządzanym) kodzie? [Zamknięte]


125

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ą.


Jeśli chodzi o unikanie wycieków, poniższy post zawiera kilka porad: http://stackoverflow.com/questions/27492/c-memory-management
tonylo


Użyłem tego z programem Visual Studio do wykrywania wycieku memów. codeproject.com/KB/applications/visualleakdetector.aspx
tiboo

1
wyszukujesz valgrin (dla linuxa) lub deleaker (dla Windows), spójrz też na wizualny wykrywacz nieszczelności ...
John Smith

aby znaleźć wycieki pamięci, sprawdź tutaj: theunixshell.blogspot.com/2013/11/...
Vijay

Odpowiedzi:


78

Jeśli twój kod C / C ++ jest przenośny do * nix, kilka rzeczy jest lepszych niż Valgrind .


1
Valgrind działa teraz również na OS X, więc Linux nie jest jedyną opcją.
Michael Anderson,

1
Valgrind dla systemu Linux (i OS X). Jeśli używasz windose - deleaker - co najlepsze!
John Smith

@JordiBunster: Świetnie! Ale oparte na środowisku wykonawczym. Z dużą bazą kodu (napisaną w Cly przypadku) będziesz głównie testować swój program pod kątem sposobu, w jaki został zaprojektowany. Osoba atakująca może poświęcić kilka tysięcy godzin na odczytanie kodu w celu znalezienia exploita umożliwiającego wyciek pamięci. Spodziewałbym się zautomatyzowanego narzędzia do analizy kodu źródłowego podobnego do tego, które istnieje dla JavaScript.
user2284570

65

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:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

Następnie musisz wywołać to, gdy program kończy pracę:

_CrtDumpMemoryLeaks();

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:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

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.


Uwaga na temat tej metody: wygląda na to, że działa to tylko wtedy, gdy używasz czystego C z malloc i free. Szczegółowy raport, który zawiera numery wierszy, nie jest tworzony, jeśli używasz języka C ++ new i delete.
Zach,

2
@Zach: tak naprawdę możesz sprawić, by to zadziałało (w każdym razie dla dowolnego kodu, który faktycznie sam skompilujesz) - zobacz zaakceptowaną odpowiedź na social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/…
Roman Starkov

Czy to zadziała również w trybie wydania?
JV

1
@ user3152463 Nie. Zgodnie z dokumentacją, będzie działać tylko dla kompilacji debugowania: msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.71).aspx
Dusty Campbell

Ta linia jest błędna: #define CRTDBG_MAP_ALLOC Powinna to być: #define _CRTDBG_MAP_ALLOC
Fallso

37

W C ++: użyj RAII. Inteligentne wskaźniki podoba std::unique_ptr, std::shared_ptr, std::weak_ptrsą twoimi przyjaciółmi.


1
i std: vector jest doskonałym zamiennikiem, gdy tablice (bufory) są zwalniane w tej samej funkcji, do której są przydzielone.
KJAWolf

4
Przynajmniej std :: auto_ptr i boost :: shared_ptr są nadal podatne na wycieki.
Jasper Bekkers

5
Tylko jeśli używasz ich niepoprawnie, chociaż muszę przyznać, że dla std :: auto_ptr użycie go niepoprawnie jest dość łatwe.
Leon Timmermans

2
Chociaż jest to dobra rada dla standardów kodowania, nie odpowiada na pytanie. Nawet użycie shared_ptr może prowadzić do wycieków z zależnościami cyklicznymi. Możesz mieć „przecieki” z nieograniczonymi strategiami buforowania, które mają zastosowanie nawet do języków ze śmieciami.
CashCow

@CashCow: masz rację. Chociaż nie widziałem tego jeszcze w praktyce, prawdopodobnie dlatego, że używam ich oszczędnie. Cytując poniżej odpowiedź: „Używaj wskaźników tylko wtedy, gdy jest to absolutnie konieczne”.
Leon Timmermans

28

Jako programista C ++ oto kilka prostych wskazówek:

  1. Używaj wskaźników tylko wtedy, gdy jest to absolutnie konieczne
  2. Jeśli potrzebujesz wskaźnika, dwukrotnie sprawdź, czy SmartPointer jest możliwy
  3. Użyj wzorca GRASP Creator .

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.


2
Visual Leak Detectore został przeniesiony do nowej witryny vld.codeplex.com
KindDragon

VLD to NAPRAWDĘ fajny wykrywacz nieszczelności - całkowicie polecam go każdemu, kto używa VC ++
Javid

1
+1 za punkt # 1. To jest absolutnie podstawowa sprawa. Niestety, wydaje mi się, że niektóre z największych bibliotek „C ++” unikają alokacji stosu i / lub RAII na rzecz Pointers Everywhere, często bez dostrzegalnego powodu. W efekcie kończą jako „C z klasami”, a nie rzeczywiste C ++.
podkreślenie_d

16

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.



7

Visual Leak Detector jest bardzo dobrym narzędziem, chociaż nie obsługuje wywołań w środowiskach wykonawczych VC9 (na przykład MSVCR90D.DLL).


1
To narzędzie jest naprawdę doskonałe! Oszczędza ci to kłopotów z używaniem _CrtDumpMemoryLeaks (); i przyjaciele, zgodnie z opisem w MSDN. Tylko jeden włącz i wszystko pokaże! Działa nawet w starych bibliotekach C.
m_pGladiator,

Nowa wersja (dla VS2013) jest tutaj: vld.codeplex.com
Dženan

7

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ąpione unique_ptrw C ++ 11), unique_ptr(C ++ 11) i shared_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.


1
i nie zapomnij o regule 3 lub 5 wtedy
Humam Helfawi

4

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 .


4

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

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

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!


1
Sprawdziłem wyciekacz i wygląda dobrze, ale po prostu FYI nie będzie działać tak, jak jest na x64, ponieważ zawiera wbudowany montaż.
Zach,


3

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.


2

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.


2

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:

  1. Może ci się to przydać.

  2. Chociaż to trochę krufty, nie pozwalam, żeby mnie to zawstydziło.

  3. 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ć neww 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


2

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:

  • Dość szybko (w porównaniu do valgrind: bardzo szybko)
  • Posiada przyjemną graficzną prezentację wyników
  • Posiada inne przydatne możliwości: profilowanie procesora, profilowanie zużycia pamięci ...


2

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.


2

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.


Memory Validator udostępnia również nazwę pliku i numer wiersza dla języka C #, który wywołuje kod natywny. Wersja x64 jest w fazie beta
Stephen Kellett

2

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.


1

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.


1

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.


1

Ogólne wytyczne dotyczące kodowania:

  • Zasoby powinny zostać zwolnione w tej samej „warstwie” (funkcja / klasa / biblioteka), w której są przydzielane.
  • Jeśli nie jest to możliwe, spróbuj użyć jakiejś automatycznej zwalniania (zwiększ współdzielony wskaźnik ...)

1

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.

  1. 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.

  2. 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.


1

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


1

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


szyna jest nowszym zamiennikiem kłaczków.
Mark Kegel,

@ user14788: Produkt PC-Lint firmy Gimpel jest znacznie nowocześniejszy niż stary Unix 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).
Dan,

0

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.



0

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/


0

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


Nie rozumiem, dlaczego spowolnienie sprawia, że ​​wyniki są bezużyteczne. Z pewnością wyciek pamięci wycieka niezależnie od szybkości działania programu. Celem tych narzędzi jest znajdowanie wycieków, więc gdzie jest problem? Czy działał tak wolno, że nie można było fizycznie zmusić go do pokrycia wszystkich ścieżek kodu w celu ich profilowania?
underscore_d

-1

Wydaje się, że Mtrace jest standardem wbudowanym w Linuksa. Kroki są następujące:

  1. ustaw zmienną środowiskową MALLOC_TRACE w bash
    MALLOC_TRACE = / tmp / mtrace.dat
    export MALLOC_TRACE;
  2. Dodaj #include <mcheck.h> na początku głównego pliku źródłowego
  3. Dodaj mtrace (); na początku main i muntrace ();na dole (przed instrukcją powrotu)
  4. skompiluj swój program z przełącznikiem -g, aby uzyskać informacje o debugowaniu
  5. uruchom swój program
  6. wyświetl informacje o wycieku za pomocą
    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   )

mtrace nie jest super pomocny w C ++
Erin,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.