Autor Wątek: Access Violation z pustym Callstackiem pod MingW32 - jak zdebugować?  (Przeczytany 4773 razy)

Offline Shelim

  • Użytkownik
    • Homepage

# Luty 09, 2015, 11:56:16
Mam dość nietypowy problem: mianowicie podczas normalnego grania w moją grę wyskakuje od czasu do czasu Access Violation. Czasem tuż po wczytaniu poziomu, czasem 20 minut później. Nie udało mi się dotąd odkryć żadnej reguły. Oczywiście próbowałem go kilkukrotnie złapać debuggerem (GDB), ale callstack który dostawałem zawierał kilka losowych wpisów z NTDLL (albo coś równie konstruktywnego z kodem assembly) i nic z gry. Przejechałem kod CppCheck'iem, wszystko było ok. Potem spróbowałem odpalić dr Memory. Na standardowych ustawieniach gra chodziła w 0,05 FPS, natomiast w opcji -light dotarła do zwisu i dr Memory nie znalazł po drodze nic nietypowego.

Projekt jest OpenGlowy w SDLu i korzysta z LibVLC do odtwarzania cut-scenek. Kompilowany MinGW TDM 4.7.1

Rzecz nietypowa - założyłem własny handler na exceptiony Windowsowe wyświetlający modalnego message boxa i dwukrotnie podczas zwisu dostałem DWA message boxy jednocześnie, co świadczyłoby że crash nastąpił w dwóch wątkach na raz. Przy czym sam nie używam wielowątkowości, nie licząc tego co robią "za plecami" biblioteki z których korzystam.

Pytanie: jak do licha to zdebuggować? Co jeszcze mogę zrobić żeby namierzyć przyczynę?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline lethern

  • Użytkownik

  • +1
# Luty 09, 2015, 13:18:17
Nie mam doświadczenia w tego typu sytuacji, ale kilka luźnych pomysłów które możesz sprawdzić:
-dodać 'start' i 'end' logi do paru ważniejszych funkcji i liczyć na to, że okroisz kod do momentu, gdzie będzie błąd
-spróbować wywalić część funkcjonalności i zastąpić ją mock'ami i znowu liczyć na to, że okroisz miejsce poszukiwań
-może te biblioteki mają wersję skompilowaną do debug / sam możesz taką skompilować i wtedy spróbować debuggera?

Offline laggyluk

  • Użytkownik
    • twitter

# Luty 09, 2015, 13:24:05
uprzedzam że nie robiłem czegoś takiego ale może tak: jakiś globalny stosik na który robisz push i pop nazwy podejzanej funkcji na jej poczatku i koncu, kiedy sie wywali to wyswietlasz/dumpujesz stosik

Offline Xirdus

  • Redaktor

# Luty 09, 2015, 13:39:03
Rzecz nietypowa - założyłem własny handler na exceptiony Windowsowe wyświetlający modalnego message boxa i dwukrotnie podczas zwisu dostałem DWA message boxy jednocześnie, co świadczyłoby że crash nastąpił w dwóch wątkach na raz.
Postaw może breakpointa w tym handlerze.

Offline skmskm

  • Użytkownik

# Luty 09, 2015, 13:44:53
Jak dla mnie to wygląda jak jakiś wyciek pamięci/błąd alokacji zwykle wtedy są takie dziwne losowe błędy.
Musisz przejrzeć cały kod bardzo dokładnie i debuger łapie losowe momenty bo taki access violation nie daje się złapać zwykłym try...catch.

Offline CheshireCat

  • Użytkownik

# Luty 09, 2015, 13:46:23
Pozornie losowe błędy mogą się pojawić w wyniku zrównoleglenia również.
Sam piszesz, że błąd mógł się pojawić w dwóch wątkach.

Zrób single-threaded na moment (na pewno się jakoś da) i zobacz czy błąd się pojawia.

Offline Xirdus

  • Redaktor

# Luty 09, 2015, 13:53:29
Zobacz też jaki adres pamięci wyskakuje przy tym access violation.

Offline Liosan

  • Redaktor

# Luty 09, 2015, 20:39:06
Build debug czy release? 32 czy 64 bit? Czy oprócz exceptionów windowsowych lecą zwykłe c++owe?

Rzecz nietypowa - założyłem własny handler na exceptiony Windowsowe wyświetlający modalnego message boxa i dwukrotnie podczas zwisu dostałem DWA message boxy jednocześnie, co świadczyłoby że crash nastąpił w dwóch wątkach na raz. Przy czym sam nie używam wielowątkowości, nie licząc tego co robią "za plecami" biblioteki z których korzystam.
Jeśli podejrzewasz cudze wątki, to proponuję w message boksie dodać thread id, adres (tak jak mówi Xirdus), thread name jeśli posiadasz. Ale ja myślę, że to może być coś innego, bo dwa message boksy jednocześnie z dwóch wątków brzmią na zbyt duży zbieg okoliczności.

Oprócz tego - próbowałeś uruchamiać w gdb od początku? Załadować obraz gry w gdb i wystartować. Wtedy przed uruchomieniem możesz ustawić np. catchpointy - na rzucanie lub łapanie wyjątków C++owych.

Liosan

Offline kubera

  • Użytkownik
    • Prywatna strona

# Luty 09, 2015, 21:51:42
Ogólnie rady są dobre wg mnie.
Ja proponuję faktycznie oflagować we/wy każdej większej funkcji.
Potem, jak już jest rozpoznany ogólny podprogram, można logować wywoływane przez niego funkcje.
W ostatniej fazie można logować zmienne i stałe.

P. S.
Czasem podobne błędy mogą się pojawić, gdy się użyje przez nieuwagę std::unique_lock zamiast std::lock_guard itp.
Ja nad takim błędem siedziałem parę dni w VC :)

uprzedzam że nie robiłem czegoś takiego ale może tak: jakiś globalny stosik na który robisz push i pop nazwy podejzanej funkcji na jej poczatku i koncu, kiedy sie wywali to wyswietlasz/dumpujesz stosik
Można także wykonać małą klasę konstruowaną na wejściu, jako zmienną na stosie podprogramu.
Zwolnienie stosu zawoła destruktor, który zaloguje podobną informacje, co ctor.
« Ostatnia zmiana: Luty 09, 2015, 21:54:41 wysłana przez kubera »

Offline Shelim

  • Użytkownik
    • Homepage

# Luty 09, 2015, 22:49:33
Dzięki za uwagi, odpowiadając po kolei:

Problem leży w tym że tego zwisa ciężko zreprodukować - potrafię grać nawet 30 minut zanim się pojawi, więc wszystkie testy wymagają czasu. Z drugiej strony nie może to być po prostu memory leak sumujący się z czasem, poziom zużywanego RAMu jest mniej więcej na stałym poziomie, a parę razy zdarzył mi się zwis w pierwszej minucie po odpaleniu.

@lethern
Nie mam najmniejszego tropu w którym module może występować zwis (tak jak mówiłem, w callstacku nie ma ani jednej klatki z mojego kodu który wskazywałby w ogóle sekcję do poszukiwań), musiałbym oflagować wszystkie moduły i większe funkcje. A jeżeli jednak jest to problem wielowątkowości to będzie to bezużyteczne...

@Xirdus
GDB nie pozwala kontynuować programu po błędzie, kiedy debugger jest podczepiony, gra nie dociera do handlera... Ale w sumie dałeś mi pomysł, spróbuję odpalić bez GDB i podczepić debugger dopiero po wyskoczeniu message boxa. Ale to i tak kolejne 30 minut zabawy w złap mnie...

@skmskm
Też podejrzewam że pamięć jest gdzieś zajechana. Tylko kurka, nie bardzo wiem jak tego szukać...

@CheshireCat
W moim kodzie wszystkie rzeczy są single threaded, to zależności korzystają z MT, i obawiam się że nie da się ich zmusić by tego nie robiły

@Xirdus
Adres pamięci - 0x6C7B53BE - nie jest to niestety żadne 0xfefefefe które dałoby trop...

@Liosan
Zarówno w debugu jak i w releasie, kompilowane w x86. Spróbuję odpalić z włączonym łapaniem wyjątków C++

@kubera
W sumie ta rada się powtarza, jak nie złapię nic poprzednimi metodami to dorzucę stos printfów gdzie się da

Offline Xirdus

  • Redaktor

# Luty 09, 2015, 22:59:46
Adres pamięci - 0x6C7B53BE - nie jest to niestety żadne 0xfefefefe które dałoby trop...
Możesz spróbować przeciążyć globalne operatory new i delete, i printować każdy zaalokowany adres + rozmiar. Jeżeli jest to problem z wiszącymi wskaźnikami, to dałoby to być może pojęcie, jakiego typu obiekt jest niepoprawnie czyszczony.

Inny pomysł - skompiluj pod MSVC :)

Offline albireo

  • Użytkownik

# Luty 09, 2015, 23:06:19
Podejrzewałbym jeszcze nadpisanie jakiegoś obszaru pamięci (np wyjeżdżając gdzieś poza zakres tablicy).

Offline laggyluk

  • Użytkownik
    • twitter

# Luty 10, 2015, 00:26:02
a sprawdzałeś czy nie ma memory leaks? nawet jeżeli się przez to nie crashuje to i tak nie powinno ich być a jeżeli wywala się po pół godzinie to też leak nie dział by się co klatkę tylko okazjnie.
i przez to nie był by w oczywisty sposób widoczny w zużyciu pamięci

Offline Interceptor

  • Użytkownik

# Luty 10, 2015, 00:32:24
Przeciążenie operatorów new\delete i sprawdzenie memory leaków oraz wskazanie dokładnych linijek, to najlepszy pomysł.

Skoro naraz pojawiają się dwa messageboxy, to jednak któraś z bibliotek korzysta z osobnych wątków i wtedy przy jednoczesnym dostępie do zmiennych współdzielonych pomiędzy wątkami musi wystąpić błąd, chyba że używane są mutexy.

Może VLC ma w ustawieniach jakieś flagi do wielowątkowości ?

Offline albireo

  • Użytkownik

# Luty 10, 2015, 08:12:52
Skoro naraz pojawiają się dwa messageboxy, to jednak któraś z bibliotek korzysta z osobnych wątków i wtedy przy jednoczesnym dostępie do zmiennych współdzielonych pomiędzy wątkami musi wystąpić błąd, chyba że używane są mutexy.
Jeśli błąd jest w czymś, co jest wywoływane z WinodwProc, to dwa (lub więcej) messageboxów może się pokazać nawet na jednym wątku.