Korupcja sterty pod Win32; jak zlokalizować?

Pracuję nad wielowątkową aplikacją C++, która psuje stertę. Zwykłe narzędzia lokalizowania tej korupcji wydają się nie mieć zastosowania. Stare Kompilacje (18 miesięcy) kodu źródłowego wykazują takie samo zachowanie, jak najnowsze wydanie, więc jest to już od dawna i po prostu nie zostało zauważone; z drugiej strony, delty źródłowe nie mogą być użyte do identyfikacji, kiedy błąd został wprowadzony - jest wiele zmian w kodzie repozytorium.

Monit o zachowanie awarii polega na wygenerowaniu przepustowości w tym transferie danych z gniazda systemowego, który jest montowany w wewnętrznej reprezentacji. Mam zestaw danych testowych, które okresowo będą powodować wyjątki aplikacji (różne miejsca, różne przyczyny - w tym awaria heap alloc, a więc: korupcja heap).

[21]}zachowanie wydaje się być związane z mocą procesora lub przepustowością pamięci; im więcej ma każda maszyna, tym łatwiej jest się rozbić. Wyłączanie a rdzeń hyper-threading lub dwurdzeniowy zmniejsza szybkość (ale nie eliminuje) uszkodzeń. Sugeruje to problem związany z czasem.

Teraz jest rub:
Gdy jest uruchamiany w lekkim środowisku debugowania (powiedzmy Visual Studio 98 / AKA MSVC6), uszkodzenie sterty jest dość łatwe do odtworzenia - dziesięć lub piętnaście minut mija, zanim coś nie powiedzie się horrendalnie i wyjątki, jak {[1] } gdy działa w wyrafinowanym środowisku debugowania (Rational Purify, VS2008/MSVC9 lub nawet aplikacja Microsoft Verifier) system staje się związany z szybkością pamięci i nie ulega awarii (Memory-bound: CPU nie jest powyżej 50%, Disk light nie jest włączony, program działa tak szybko, jak może, box zużywa 1.3G 2G pamięci RAM). Więc, mam wybór pomiędzy byciem w stanie odtworzyć problem (ale nie zidentyfikować przyczyny) lub byciem w stanie zidentyfikować przyczynę lub problem, którego nie mogę odtworzyć.

Moje obecne najlepsze domysły co do tego gdzie dalej jest:

  1. Get an insanely grunty box (to zamiast aktualnego dev box: 2GB RAM w E6550 Core2 Duo); umożliwi to powtórzenie awarii powodującej nieprawidłowe zachowanie podczas pracy w wydajnym środowisku debugowania; lub
  2. Przepisz operatory new i delete aby używać VirtualAlloc i VirtualProtect do oznaczania pamięci jako Tylko do odczytu, gdy tylko to się skończy. Uruchom Pod {[10] } i niech OS złapie złego kolesia, który pisze do freed memory. Tak, to znak desperacji: kto do cholery przepisuje new i delete?! Zastanawiam się, czy to zrobi to jako powoli jak pod oczyszczenie et al.

I nie: Wysyłka z wbudowanym oprzyrządowaniem Purify nie wchodzi w grę.

Kolega właśnie przeszedł obok i zapytał " Stack Overflow? Mamy teraz przepełnienie stosu?!?"

I teraz, pytanie: Jak mogę zlokalizować sterty corruptor?


Update: balancing new[] i delete[] wydaje się mieć długą drogę do rozwiązania problemu. Zamiast 15mins, aplikacja teraz trwa około dwie godziny przed upaść. Jeszcze nie. Jakieś inne sugestie? Korupcja nie ustaje.

Update: wydanie kompilacji pod Visual Studio 2008 wydaje się znacznie lepsze; obecne podejrzenia opierają się na STL implementacji, że statki z VS98.


  1. Dr Watson wytworzy zrzut, który może być pomocny w dalszej analizie.

Zanotuję to, ale obawiam się, że Dr Watson potknie się dopiero po fakt, Nie kiedy sterta jest deptana.

Kolejną próbą może być użycie WinDebug jako narzędzia debugującego, które jest dość potężne, a jednocześnie lekkie.

Mam to w tej chwili, znowu: niewiele pomocy, dopóki coś nie pójdzie nie tak. Chcę złapać wandala na gorącym uczynku.

Być może te narzędzia pozwolą ci przynajmniej zawęzić problem do określonego komponentu.

Nie mam wiele nadziei, ale zdesperowany czasy wymagają...

A czy jesteś pewien, że wszystkie komponenty projektu mają poprawne ustawienia biblioteki uruchomieniowej (C/C++ tab, Kategoria generowania kodu w ustawieniach projektu VS 6.0)?

Nie jestem, a jutro spędzę kilka godzin przeglądając przestrzeń roboczą (58 projektów w niej) i sprawdzając, czy wszystkie kompilują i łączą się z odpowiednimi flagami.


Aktualizacja: to trwało 30 sekund. Zaznacz wszystkie projekty w oknie dialogowym Settings, usuń zaznaczenie do znajdziesz Projekt(y), które nie mają odpowiednich ustawień (wszystkie miały odpowiednie ustawienia).
Author: Josh, 2008-08-04

15 answers

Moim pierwszym wyborem byłoby dedykowane narzędzie sterty, takie jak pageheap.exe .

Przepisywanie new I delete może być przydatne, ale to nie wychwytuje allocs popełnionych przez kod niższego poziomu. Jeśli to jest to, co chcesz, lepiej objechać low-level alloc API s za pomocą Microsoft objazdy.

Również sanity sprawdza, takie jak: sprawdzić, czy Twoje biblioteki w czasie pracy pasują (release vs. debug, multi-threaded vs. single-threaded, dll vs. static lib), szukać złych usunięć (np. używane), upewnij się, że nie mieszasz i nie pasujesz do swoich allocs.

Spróbuj również selektywnie wyłączyć wątki i zobaczyć, kiedy / jeśli problem zniknie.

Jak wygląda stos wywołań etc w czasie pierwszego wyjątku?

 27
Author: Michael Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-01 17:32:32

Mam te same problemy w pracy (czasami używamy też VC6). I nie ma na to łatwego rozwiązania. Mam tylko kilka podpowiedzi:

  • Wypróbuj automatyczne zrzuty awaryjne na maszynie produkcyjnej(zobacz Wywrotka procesowa ). Moje doświadczenie mówi, że doktor Watson nie jest idealny do rzucania.
  • Usuń wszystkie catch(...) z twojego kodu. Często ukrywają poważne wyjątki pamięci.
  • Sprawdź zaawansowane debugowanie Windows - jest wiele świetnych wskazówek dla problemy jak twoje. Polecam to z całego serca.
  • Jeśli używasz STL spróbuj STLPort i zaznacz buduje. Nieprawidłowy iterator to piekło.
Powodzenia. Rozwiązywanie takich problemów zajmuje nam miesiące. Bądź na to gotowa...
 11
Author: Michal Sznajder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 12:30:58

Uruchom oryginalną aplikację za pomocą ADplus -crash -pn appnename.exe Kiedy pojawi się problem z pamięcią, dostaniesz ładny duży zrzut.

Możesz przeanalizować zrzut, aby dowiedzieć się, które Miejsce Pamięci zostało uszkodzone. Jeśli masz szczęście, że pamięć nadpisania jest unikalnym ciągiem, możesz dowiedzieć się, skąd pochodzi. Jeśli nie masz szczęścia, musisz zagłębić się w win32 stertę i dowiedzieć się, jakie były oryginalne cechy pamięci. (heap-x może pomóc)

Po tym, co było popaprane, można zawęzić appverifier użycie ze specjalnymi ustawieniami sterty. np. możesz określić, co DLL monitorujesz, lub jaki rozmiar alokacji chcesz monitorować.

Miejmy nadzieję, że to przyspieszy monitorowanie na tyle, by złapać winowajcę.

Z mojego doświadczenia nigdy nie potrzebowałem trybu pełnego weryfikatora sterty, ale spędziłem dużo czasu analizując zrzuty awaryjne i przeglądając źródła.

P. S: Możesz użyć DebugDiag do analizy zrzutów. Może wskazać DLL posiadanie skorumpowanej sterty i dać inne przydatne szczegóły.

 8
Author: Tal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 12:33:55

Mieliśmy dość szczęścia pisząc nasze własne malloc i darmowe funkcje. W produkcji po prostu nazywają standard malloc i free, ale w debugowaniu mogą robić, co chcesz. Mamy również prostą klasę bazową, która nie robi nic poza nadpisywaniem operatorów new i delete, aby używać tych funkcji, wtedy każda klasa, którą napiszesz, może po prostu dziedziczyć z tej klasy. Jeśli masz mnóstwo kodu, może to być duża praca, aby zastąpić połączenia do malloc i za darmo do nowego malloc i za darmo (nie zapomnij realloc!), ale na dłuższą metę jest to bardzo pomocne.

W książce Steve 'a Maguire' a pisanie solidnego kodu (wysoce zalecane), istnieją przykłady debugowania rzeczy, które można zrobić w tych procedurach, jak:

  • śledź alokacje, aby znaleźć przecieki
  • Przydziel więcej pamięci niż to konieczne i umieść znaczniki na początku i końcu pamięci - podczas wolnej procedury możesz upewnić się, że znaczniki te nadal tam są
  • memset pamięci ze znacznikiem przy alokacji (aby znaleźć użycie niezainicjalizowanej pamięci) i na free (aby znaleźć użycie wolnej pamięci)

Innym dobrym pomysłem jest nigdy nie używać takich rzeczy jak strcpy, strcat, or sprintf -- Zawsze używaj strncpy, strncat, i snprintf. Napisaliśmy również własne wersje tych plików, aby upewnić się, że nie odpisujemy końca bufora, a te również napotkały wiele problemów.

 7
Author: Graeme Perrow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 12:32:29

Powinieneś zaatakować ten problem zarówno podczas wykonywania, jak i analizy statycznej.

Do analizy statycznej rozważ kompilację za pomocą PREfast (cl.exe /analyze). Wykrywa niedopasowane delete i delete[], przekroczenia bufora i wiele innych problemów. Bądź jednak przygotowany, aby przebrnąć przez wiele kilobajtów Ostrzeżenia L6, zwłaszcza jeśli twój projekt nadal ma L4 nie naprawiony.

PREfast jest dostępny z systemem Visual Studio Team oraz, najwyraźniej , jako część Windows SDK.

 4
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 12:32:50

Pozorna przypadkowość uszkodzenia pamięci brzmi bardzo podobnie do problemu z synchronizacją wątków - błąd jest odtwarzany w zależności od prędkości maszyny. Jeśli obiekty (części pamięci) są współdzielone między wątkami i synchronizacja (sekcja krytyczna, mutex, SEMAFOR, inne) prymitywy nie są na zasadzie dla klas (na obiekt, na klasę), to możliwe jest, aby dojść do sytuacji, w której klasa (część pamięci) jest usuwana / zwalniana podczas użytkowania lub używana po usunięciu / zwolnieniu.

Jako test dla tego, można dodać synchronization primitives do każdej klasy i metody. Spowoduje to, że Twój kod będzie wolniejszy, ponieważ wiele obiektów będzie musiało na siebie czekać, ale jeśli wyeliminuje to korupcję sterty, problem z korupcją sterty stanie się optymalizacją kodu.

 3
Author: Ignas Limanauskas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 19:55:09

Czy to w warunkach niskiej pamięci? Jeśli tak, to może być tak, że new zwraca NULL zamiast rzucać STD:: bad_alloc. Starsze Kompilatory VC++ nie implementowały tego poprawnie. Jest artykuł o awariach alokacji pamięci upaść STL aplikacje zbudowane z VC6.

 3
Author: Steve Steiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 12:34:24

Próbowałeś starych kompilacji, ale czy jest jakiś powód, dla którego nie możesz cofnąć się dalej w historii repozytorium i zobaczyć dokładnie, kiedy błąd został wprowadzony?

W Przeciwnym Razie sugerowałbym dodanie jakiegoś prostego logowania, aby pomóc w wyśledzeniu problemu, chociaż jestem w błędzie, co konkretnie możesz chcieć zalogować.

Jeśli możesz dowiedzieć się, co dokładnie może spowodować ten problem, za pośrednictwem google i dokumentacji wyjątków, które dostajesz, może to da większy wgląd na co zwrócić uwagę w kodzie.

 1
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-04 07:48:51

Moja pierwsza akcja będzie następująca:

  1. Zbuduj binaria w wersji "Release", ale tworząc plik informacji o debugowaniu (taką możliwość znajdziesz w ustawieniach projektu).
  2. Użyj Dr Watson jako debugera defualt (DrWtsn32-I) na komputerze, na którym chcesz odtworzyć problem.
  3. Powtórz problem. Dr Watson stworzy wysypisko, które może być pomocne w dalszej analizie.

Kolejną próbą może być użycie WinDebug jako narzędzia do debugowania, które jest dość potężna, jednocześnie lekka.

Być może te narzędzia pozwolą ci przynajmniej zawęzić problem do określonego komponentu.

A czy jesteś pewien, że wszystkie komponenty projektu mają poprawne ustawienia biblioteki uruchomieniowej (zakładka C / C++, Kategoria Generowanie kodu w ustawieniach projektu VS 6.0)?

 1
Author: Piotr Tyburski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-04 08:26:55

Więc z ograniczonych informacji, które posiadasz, może to być kombinacja jednej lub więcej rzeczy:

  • złe użycie sterty, tzn. podwójne zwolnienie, odczyt po zwolnieniu, zapis po zwolnieniu, ustawienie znacznika HEAP_NO_SERIALIZE za pomocą allocs i zwolnienie z wielu wątków na tej samej stercie
  • Out of memory
  • zły kod (tj. przepełnienie bufora, niedopełnienie bufora itp.)
  • "Timing" issues

Jeśli to w ogóle dwa pierwsze, ale nie ostatni, powinieneś już go złapać z każdej strony.exe.

Co najprawdopodobniej oznacza, że jest to spowodowane tym, jak kod uzyskuje dostęp do pamięci współdzielonej. Niestety, namierzenie tego będzie dość bolesne. Niezsynchronizowany dostęp do pamięci współdzielonej często przejawia się jako dziwne "problemy z czasem". Rzeczy takie jak niestosowanie semantyki acquire/release do synchronizacji dostępu do pamięci współdzielonej za pomocą flagi, niestosowanie odpowiednich blokad itp.

Co najmniej, pomogłoby być w stanie śledzić alokacje w jakiś sposób, jak było sugerowane wcześniej. Przynajmniej wtedy można zobaczyć, co faktycznie stało się aż do korupcji sterty i spróbować zdiagnozować z tego.

Ponadto, jeśli możesz łatwo przekierować alokacje do wielu stosów, możesz spróbować tego, aby sprawdzić, czy to rozwiązuje problem lub skutkuje bardziej powtarzalnym zachowaniem błędów.

Podczas testowania z VS2008, czy uruchomiłeś HeapVerifier z Conserve Memory ustawionym na Yes? Może to zmniejszyć wpływ alokatora sterty na wydajność. (Plus, musisz uruchomić z nim Debug- > Start with Application Verifier, ale możesz już to wiedzieć.)

Możesz również spróbować debugowania za pomocą Windbg i różnych zastosowań !Komenda sterty.

MSN

 1
Author: Mat Noguchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-22 16:51:06

Jeśli zdecydujesz się przepisać new/delete, zrobiłem to i mam prosty kod źródłowy pod adresem:

http://gandolf.homelinux.org/ ~ smhanov / blog/? id=10

To wykrywa wycieki pamięci, a także wstawia dane ochrony przed i po bloku pamięci, aby wychwycić uszkodzenie sterty. Możesz po prostu zintegrować z nim, umieszczając #include "debug.h " na górze KAŻDEGO pliku CPP i definiowanie DEBUG i DEBUG_MEM.

 1
Author: Steve Hanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 13:40:35

Graeme ' s sugestia custom malloc / free to dobry pomysł. Sprawdź, czy potrafisz scharakteryzować jakiś wzór korupcji, by dać ci przewagę.

Na przykład, jeśli jest zawsze w bloku o tym samym rozmiarze (powiedzmy 64 bajty), Zmień swoją parę malloc / free, aby zawsze przydzielać 64 bajtowe kawałki na ich własnej stronie. Gdy uwolnisz 64 bajtowy fragment, Ustaw bity Ochrony Pamięci na tej stronie, aby zapobiec czytaniu i wites (używając VirtualQuery). Wtedy każdy, kto próbuje uzyskać dostęp do tego pamięć generuje wyjątek, a nie uszkadza stertę.

To zakłada, że liczba zaległych 64 bajtów jest tylko umiarkowana lub masz dużo pamięci do wypalenia w pudełku!

 0
Author: Rob Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-02 04:23:34

Trochę czasu musiałem rozwiązać podobny problem. Jeśli problem nadal istnieje, proponuję zrobić to : Monitoruj wszystkie połączenia do new / delete i malloc / calloc / realloc / free. Wykonuję pojedynczy DLL eksportujący funkcję do rejestrowania wszystkich wywołań. Ta funkcja odbiera parametr do identyfikacji źródła kodu, wskaźnik do przydzielonego obszaru i typ wywołania zapisując te informacje w tabeli. Wszystkie przydzielone/zwolnione pary są eliminowane. Na końcu lub po trzeba wykonać połączenie do innej funkcji do tworzenia / align = "left" / Dzięki temu możesz zidentyfikować błędne połączenia (Nowe / Darmowe lub malloc/delete) lub brakujące. W przypadku nadpisania bufora w kodzie zapisane informacje mogą być błędne, ale każdy test może wykryć / odkryć/załączyć rozwiązanie zidentyfikowanej awarii. Wiele biegów, aby pomóc zidentyfikować błędy. Powodzenia.

 0
Author: lsalamon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-19 11:52:50

Czy uważasz, że to stan rasy? Czy wiele wątków dzieli jedną stertę? Czy możesz dać każdemu wątkowi prywatną stertę za pomocą HeapCreate, a następnie mogą działać szybko za pomocą HEAP_NO_SERIALIZE. W przeciwnym razie sterta powinna być bezpieczna dla wątków, jeśli używasz wielowątkowej wersji bibliotek systemowych.

 0
Author: Don,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 13:48:40

Kilka propozycji. Wspominasz o obfitych ostrzeżeniach na W4 - sugerowałbym poświęcenie czasu na naprawienie kodu, aby skompilować go czysto na poziomie Ostrzeżenia 4 - to przejdzie długą drogę do zapobiegania subtelnym trudnym do znalezienia błędom.

Drugi-dla przełącznika / analizatora-rzeczywiście generuje obfite Ostrzeżenia. Aby użyć tego przełącznika w moim własnym projekcie, to co zrobiłem, to stworzenie nowego pliku nagłówkowego, który używał Ostrzeżenia #pragma, aby wyłączyć wszystkie dodatkowe Ostrzeżenia generowane przez / analyze. Potem dalej w pliku włączam tylko te ostrzeżenia, na których mi zależy. Następnie użyj przełącznika kompilatora /FI, aby wymusić dołączenie tego pliku nagłówka jako pierwszego do wszystkich jednostek kompilacji. Powinno to pozwolić na użycie przełącznika / analyze podczas kontrolowania wyjścia

 0
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-03 16:48:57