Zepsuta sterta - znalezienie winowajcy mazania sterty.

0

Witam,
robię sobie mały projekcik - grę tetris.
W tej chwili jest to jeszcze bieda, aczkolwiek działa.
Problem:
Mam klasę CGame, zawiera ona główną karuzelę wychwytującą akcje użytkownika oraz uruchomiony na sekundę przed nią wątek tworzący klocki i ustawiający je, animujący etc.
Klasa ta (singleton) posiada również flagę (czy kontynuować przetwarzanie, jezeli zmieni się na true, wątek oraz karuzela dostaje info o zakończeniu pracy).
I tutaj jest cały problem, gdy naciskam 'q' lub zamykam okienko, co powoduje zamkniecie karuzel i wywołanie destruktorów dostaję błąd o nadpisanej stercie.
Niestety nie mogę się dokopać co dokładnie jest nie tak.

Zapis stosu:
ntdll.dll!_RtlReportCriticalFailure@8() Unknown
ntdll.dll!_RtlpHeapHandleError@4() Unknown
ntdll.dll!_RtlpLogHeapFailure@24() Unknown
ntdll.dll!RtlFreeHeap() Unknown

msvcr120.dll!free(void * pBlock) Line 51 C
Tetris.exe!std::_DebugHeapDeletestd::_Facet_base(std::_Facet_base * _Ptr) Line 62 C++
Tetris.exe!std::~locale() Line 440 C++
Tetris.exe!``anonymous namespace'::path_locale'::2'::dynamic atexit destructor for 'loc''() C++
msvcr120.dll!doexit(int code, int quick, int retcaller) Line 628 C
msvcr120.dll!exit(int code) Line 417 C
Tetris.exe!__tmainCRTStartup() Line 666 C
kernel32.dll!@BaseThreadInitThunk@12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart@8() Unknown
Błąd wyskakuje zaraz potym jak wyjdę z funkcji main.

Link do kodu:
https://github.com/bartekordek/Tetris

Jeżeli nikt nie będzie w stanie/chciało się pokazać mi co jest nie tak, to prosiłbym chociażby o podpowiedź od czego powinienem zacząć szukanie takiego chochlika.
Z góry przepraszam za kod, jest "smelly", nad czym walczę, wszelkie rady, obelgi i zarzuty przyjmę z radością.

0

Nie bardzo cię rozumimem. Zapnij się tam debugerem i zobacz co się dzieje.

0

Problem w tym, że w tym momencie debugger pokaże mi adresy.
Tylko nie wiem czego dotyczyły te adresy bo kontekst z aplikacji nie jest widoczny. Dostanie się opcją "Break" w okienku error wyrzuca mnie do pliku free.c, a to nie zbyt dużo mi mówi.

0

Ale jakie znów adresy? o_O Umiesz ty korzystać z debugera? Śledź program krok po kroku i zobaczysz knkretnie co powoduje problem.

0

Śledziłem. Rzecz w tym że wszystko się dzieje gdy zwalniana jest sterta. Destruktory moich obiektów zostały dawno odpalone. Patrzyłeś na stacka?

0

No to odpal ten program przez valgrinda i może powie ci gdzie coś źle zwalniasz.

0

Kurcze, no właśnie pod windą nie ma valgrinda.
Czyli mi pozostaje dopisanie mejkfejla i kompilacja under Linux?

0

Game.cpp

delete eventPoolThread;

Problem w tym, że eventPoolThread jest składową prywatną, dokładniej wskaźnikiem na std::thread a ty nigdzie nie tworzysz std::thread na stercie (przez operator new oczywiście) w metodach klasy CGame, a jeszcze gorzej, bo nie zerujesz nigdzie tego wskaźnika. Tworząc obiekt klasy CGame pod tym wskaźnikiem może być cokolwiek, a ty to jeszcze próbujesz zwolnić w destruktorze. Poza tym po wstępnym spojrzeniu na kod już wiem, że źle się do tego zabierasz i masz za mało wiedzy. Nie wiesz jak używać czystych pointerów to nie używaj i zacznij używać std::unique_ptr i std::shared_ptr, bo wątpię, że napiszesz własną implementację garbage collectora.

0

@mwl4
Oczywiscie, że destruktor prywatny może być. Jest to klasa napisana zgodnie ze wzorcem singleton (jedna z jego wersji).
Info odnośnie wzorca: http://www.yolinux.com/TUTORIALS/C++Singleton.html

Co do pointera, wstawiłem jego zerowanie w konstruktorze i to nie pomogło.

0

@pylaochos zerknąłem trochę na kod i @mwl4 ma racje z tym delete - zwalniasz to a nigdzie nie alokujesz. Poza tym ja bym zasugerowal małą modyfikacje do kodu:

  • niech plansza ma rozmiar NxM zadany przez użytkownika
  • niech klocki się skalują zgodnie parametrem podanym przez użytkownika (bo jak plansza większa to i klocki mogłyby być większe). W ogóle najlepiej jakby można było samemu zadać wygląd klocków z których chcemy układać.
    Czemu tak? Żebyś oduczył sie ręcznego pisania wszystkich możliwości w kodzie ;]
0

MainGrid.cpp

delete m_activeBrick; 

Znowu w konstruktorze nie zerujesz pointera i może być problem w destruktorze.

0

Witam ponownie,
znalazłem miejsce w kodzie które powoduje ekspeszyn.
Jest to linijka 167 i 168 w Game.cpp, zamieniłem to teraz w one-linera, żeby łatwiej można było ogarnąć:

 
	Path picDir = Fs::current_path().parent_path() / "pic" / "BackGroundBlock.bmp";

Usunięcie jej (zastąpienie wszystkiego hard codowanym stringiem naprawia sprawę).
Wiecie jak zjeść ciastko i mieć ciastko?
Szczerze wolę posiadać boost::path niż stringa (bardziej przenośne rozwiązanie i bardziej eleganckie).

Co śmieszne samo stworzenie path-a nie powoduje wyjątku, ale powoduje go usunięcie instancji klasy CGame, okazuje się, że pamięć została pomazana (wynika z tego, że przez ten obiekt).

Jakieś pomysły?

0

Zapominasz chyba że mówimy o C++. Błąd nie jest w pokazanej linijce. Pokazany kod powoduje uwidocznienie się błędu. Błąd zapewne polega na jakiejś błędnej alokacji pamięci i utworzenie tej twojej zmiennej Path powoduje zamazanie danych gdzieś na stosie, z których to danych nielegalnie ktoś inny korzysta. Ja bym popatrzył co jest deklarowane na stosie obok tej zmiennej na przykład...

0

Pod Windows jest Visual Leak Detector (dla Visual Studio): http://vld.codeplex.com/

0

Przy problemach z pamięcią na Windowsie warto przyjrzeć się programowi Application Verifier. Z testów najlepiej wybrać tylko Basic -> Heaps i odpalić pod debuggerem :)

0

Ok, skompilowałem pod linuhem. Odpaliłem. Wszystko śmiga. Wszystko gra...
Promienie kosmiczne tylko pod windowsem?

1

Pod GNU/Linuxem polecam kompilację z Address Sanitizerem (http://tsdgeos.blogspot.com/2014/03/asan-and-gcc-how-to-get-line-numbers-in.html - tu jest krótkie wytłumaczenie jak tego używać). Potrzebujesz mieć w miarę nowe gcc albo clang.

Ew. zamiast asan'a użyj msan (memory sanitizer), ubsan (undefined behavior sanitizer), etc (http://clang.llvm.org/docs/index.html). Trochę tego jest, i jest to niezastąpione do szukania za takimi błędami.

0

Próbowałem różne flagi valgrinda, śledzilem przydział pamięci.
Próbowałem różne flagi optymalizacyjne clang++.
Usunąłem pointery tam gdzie się dało.
W końcu doszedłem do <Platform toolset> i przestawiłem na:
[Visual Studio 2013 - Windows XP (v120_xp)]
z:
[Visual Studio 2013 (v120)]
teraz pozostaje mi tylko odnaleźć różnicę między tymi flagami.

1 użytkowników online, w tym zalogowanych: 0, gości: 1