Jak debuggować data race w visual studio?

0

Mam kod, który dzieli pewną klasę między 2 wątki i na 100 uruchomień działa dobrze, a już np. 101 się wywala. Wydaje mi się, że zabezpieczyłem wszystko dobrze mutexem. A nadal, bardzo rzadko, ale występuje ten błąd i chciałbym wykryć dlaczego, co, gdzie i jak. Czy jest jakieś narzędzie, które pomogłoby mi w samodzielnym wykryciu tego błędu? Zwykłe przejrzenie callstacku, jak zazwyczaj pomagało, to teraz tak średnio bym powiedział.

0

Przecież sam napisałeś przyczynę: - "Wydaje mi się, że zabezpieczyłem wszystko dobrze mutexem."
No i masz rację, wydaje ci się.

4

Generalnie to wujek Google coś tam wypluwa na temat debugowania aplikacji wielowątkowych, również w kontekście Visual Studio:

https://undo.io/resources/debugging-race-conditions-cpp/
https://docs.microsoft.com/en-us/visualstudio/debugger/debug-multithreaded-applications-in-visual-studio?view=vs-2019

Obawiam się, że szukanie wyścigu na piechotę z wykorzystaniem zwykłego debuggera może być uciążliwe.

Zauważ, że wyścig ogólnie wynika z dwóch rzeczy:
a) występowania zależności (dwa wątki modyfikują i/lub odczytują wspólny zasób)
b) niewłaściwego zabezpieczenia wspólnego zasobu przed jednoczesnym dostępem (brakuje sekcji krytycznej, lub zaczyna się za późno, lub kończy za wcześnie, lub wątki mają przestarzałe kopie i na nich pracują po wyjściu z sekcji krytycznej itp itd)

W efekcie masz sytuację, że jeśli instrukcje w poszczególnych wątkach wykonają się w specyficznej kolejności, to wyścig wystąpi i skutki będą widoczne lub też nie.

Problem jest taki, że w trybie debug:

  • pracujesz na niezoptymalizowanym kodzie, zatem może się wykonywać zupełnie inaczej niż finalny kod, optymalizowany w trakcie kompilacji (jak będziesz mieć pecha, wyścig objawi się np. raz na milion wykonań, zamiast raz na 100) - szczególnie, że niezoptymalizowany kod dużo częściej przepisuje z rejestrów do pamięci i z powrotem (zasadniczo dzięki temu możesz np. podglądać wartości zmiennych, rejestrów itd. w trakcie debugowania).
  • dodając sobie np. breakpointy i przechodząc po wykonaniu krok po kroku modyfikujesz to wykonanie i znowu, jak będziesz mieć pecha to będzie problem ze zreprodukowaniem wyścigu (patrz punkt wyżej). Zasadniczo może się okazać, że dopiero pauzując wykonanie w specyficznym momencie (żeby wymusić konkretną kolejność wykonania instrukcji z obu wątków) możesz zreprodukować błąd i znaleźć przyczynę. Zauważ, że wątki co do zasady mogą wykonywać się równolegle lub w przeplocie, w przypadku przeplotu OS może wybierać kiedy przełączyć kontekst, dochodzą jeszcze czynniki zewnętrzne (np. jeden z wątków wykonywanych równolegle na innym rdzeniu straci przydział CPU na rzecz zupełnie niepowiązanego procesu).

Ogólnie mając powyższe na uwadze, dwie najbardziej obiecujące / sensowne opcje to:

  • raz jeszcze, skrupulatnie przeanalizować kod programu i szczególnie przyjrzeć się wszystkim fragmentom, w których występuje użycie współdzielonego zasobu (instancji danej klasy, jak rozumiem)
  • dość obiecująco brzmi (nie korzystałem, nie ocenię) reverse debugging, tj. pozwalasz programowi wykonać się normalnie i w momencie, gdy wystąpi błąd zaczynasz analizować historię wykonania programu, przeglądasz co siedzi w zmiennych, pamięci etc. w poszczególnym momentach ;)
4

Jak możesz skompilować soft z clangiem/gcc - użyj thread sanitizera

4
JanuszProgramowania123 napisał(a):

Czy jest jakieś narzędzie, które pomogłoby mi w samodzielnym wykryciu tego błędu?

Clang (na MacOS) ma thread sanitizer, używałem działa super.
Nie wiem czy to narzędzie jest dostępne na Linux (gcc i clang) lub Windows.

Na Windows jest Application Verifier (nie używałem).

0
MarekR22 napisał(a):

Clang (na MacOS) ma thread sanitizer, używałem działa super.
Nie wiem czy to narzędzie jest dostępne na Linux (gcc i clang) lub Windows.

Może przypadkiem działa lub da się zmusić do działania na Windows, choć w przypadku clang docsy mówią, że nie: https://clang.llvm.org/docs/ThreadSanitizer.html

1

Polecam prześledzić co się działo w momencie crasha na pozostałych wątkach. Jeśli używasz VS to rzuć okiem na Debug > Windows > Parallel Stacks, ewentualnie Threads albo Parallel Watch. W ten sposób możesz (przy odrobinie szczęścia) zawęzić problem do danego kawałka pamięci, o który jest wyścig.

Dorzucę jeszcze kilka narzędzi (poza tymi, o których wspomnieli przedmówcy), przydatnych do analizy data race:

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