Testowanie wyścigów wielowątkowych

54

Czytanie komentarzy do tej odpowiedzi , w szczególności:

To, że nie możesz napisać testu, nie oznacza, że ​​nie jest zepsuty. Niezdefiniowane zachowanie, które zwykle działa zgodnie z oczekiwaniami (C i C ++ są tego pełne), warunki wyścigu, potencjalne zmiany kolejności z powodu słabego modelu pamięci ... - CodesInChaos 7 godzin temu

@CodesInChaos, jeśli nie można go odtworzyć, nie można przetestować kodu napisanego w „fix”. A moim zdaniem wprowadzenie nie przetestowanego kodu jest gorszym przestępstwem - RhysW 5 godzin temu

... zastanawia mnie, czy istnieją jakieś dobre ogólne sposoby konsekwentnego uruchamiania bardzo rzadko występujących problemów produkcyjnych spowodowanych warunkami wyścigowymi w przypadku testowym.

Dan Neely
źródło
1
krok po kroku (montaż) instrukcja po obu stronach
maniak zapadkowy
1
Analiza statyczna często pokazuje potencjalny UB, nie jest jasne, czy jest to liczone jako test
jk.
Przepraszam, że pytam, ale co znaczy „UB”?
Doug
2
Ładne pytanie, byłbym ciekawy, widząc potencjalne rozwiązania tego.
RhysW,
1
@Doug Undefined Behavior, które mogą obejmować między innymi warunki wyścigu
jk.

Odpowiedzi:

85

Po tym, jak działałem w tym szalonym biznesie od około 1978 r., Spędziłem prawie cały ten czas na wbudowanych komputerach w czasie rzeczywistym, pracując w wielozadaniowości, wielowątkowych, wielorakich systemach, czasem z wieloma procesorami fizycznymi, ścigając więcej niż moją uczciwą część rasy Warunki, uważam, że odpowiedź na twoje pytanie jest dość prosta.

Nie.

Nie ma dobrego ogólnego sposobu na wywołanie warunków wyścigu w testach.

Waszą jedyną nadzieją jest zaprojektowanie ich całkowicie poza systemem.

Kiedy i jeśli okaże się, że ktoś go włożył, powinieneś postawić mu mrowisko, a następnie przeprojektować, aby go wyeliminować. Po zaprojektowaniu jego faux pas (wymawiane f *** up) ze swojego systemu, możesz uwolnić go od mrówek. (Jeśli mrówki już go pochłonęły, pozostawiając tylko kości, postaw znak: „To dzieje się z ludźmi, którzy stawiają warunki rasowe w projekcie XYZ!” I ZOSTAWIĄ GO TAM).

John R. Strohm
źródło
22
Całkowicie się zgadzam. Innymi słowy, jest to bardzo podobne do żartu - Pacjent: „Doktorze, to boli, kiedy to robię ...” Doktor: „Więc przestań to robić!”
Mark Rushakoff,
Niezła odpowiedź. Jeśli coś powoduje problem, którego nie można przetestować, spróbuj go obejść na początek, całkowicie unikaj problemu!
RhysW,
Moje jedyne pytanie brzmi: jak dużego mrowiska powinienem użyć? (+1 BTW).
Peter K.,
15
+1 za poprawną wymowę faux pas . (I resztę odpowiedzi.)
Blrfl,
1
@PeterK., Jest to jeden z niewielu przypadków rozwoju oprogramowania, wraz z monitorami, pamięcią RAM i napędami dyskowymi, gdzie większy jest lepszy.
John R. Strohm,
16

Jeśli jesteś w łańcuchu narzędzi MS. Ms Research stworzyło narzędzie, które wymusi nowe przeszkody dla każdego przebiegu i może odtworzyć nieudane przebiegi, zwane szachami .

tutaj jest wideo pokazujące to w użyciu.

ponownie odtwarzać
źródło
5
To wygląda imponująco; W pewnym momencie będę musiał znaleźć czas, aby to wypróbować.
Dan Neely,
16

Najlepszym narzędziem znanym z tego rodzaju problemów jest rozszerzenie Valgrind o nazwie Helgrind .

Zasadniczo Valgrind symuluje procesor wirtualny i uruchamia na nim Twój plik binarny (niezmodyfikowany), dzięki czemu może sprawdzać każdy dostęp do pamięci. Korzystając z tej struktury, system monitorowania Helgrind wywołuje wnioski, gdy dostęp do zmiennej dzielonej nie jest odpowiednio chroniony przez mechanizm wzajemnego wykluczania. W ten sposób może wykryć teoretyczny warunek wyścigu, nawet jeśli tak się nie stało.

Intel sprzedaje bardzo podobne narzędzie o nazwie Intel Inspector .

Narzędzia te dają świetne wyniki, ale Twój program będzie znacznie wolniejszy podczas analizy.

Julien
źródło
1
czy Valgrind wciąż jest narzędziem tylko * nix?
Dan Neely,
1
Tak, Linux, MacOSX, Android i niektóre BSD: valgrind.org/info/platforms.html
Julien
1
ThreadSanitizer to podobne narzędzie. Działa inaczej niż Helgrind, co daje mu tę zaletę, że jest znacznie szybszy, ale wymaga integracji z łańcuchem narzędzi.
Sebastian Redl,
7

Ujawnienie błędu wielowątkowości wymaga zmuszenia różnych wątków wykonania do wykonania ich kroków w określonej kolejności z przeplotem. Zwykle jest to trudne bez ręcznego debugowania lub manipulowania kodem w celu uzyskania jakiegoś „uchwytu” kontrolującego to przeplatanie. Ale zmiana kodu, który zachowuje się nieprzewidywalnie, często wpłynie na tę nieprzewidywalność, więc trudno to zautomatyzować.

Niezłą sztuczkę opisuje Jaroslav Tulach w praktycznym projekcie interfejsu API : jeśli masz instrukcje rejestrowania w omawianym kodzie, manipuluj konsumentem tych instrukcji rejestrowania (np. Wstrzykiwany pseudo-terminal), aby akceptował poszczególne komunikaty dziennika w określonym kolejność na podstawie ich zawartości. Pozwala to kontrolować przeplatanie kroków w różnych wątkach bez konieczności dodawania czegokolwiek do kodu produkcyjnego, którego jeszcze nie ma.

Kilian Foth
źródło
2
Zrobiłem podobnie przed użyciem repozytorium wstrzykiwanego do uśpienia wątków, które wywołują go w określonych porządkach, aby wymusić przeplot, którego chcę. Po napisaniu kodu, który to robi, jestem skłonny do +1 powyżej odpowiedzi Johna. Poważnie, stosowanie tych rzeczy jest tak bolesne i wciąż daje tylko najlepsze domysły, ponieważ mogą istnieć nieco inne przeploty z różnymi wynikami; lepszym rozwiązaniem jest po prostu wyeliminować wszystkie możliwe warunki wyścig przez analizy statycznej i starannego czesania lub kodu za wszelkie wspólnego państwa
Jimmy Hoffa
6

Nie ma absolutnej pewności, że różne rodzaje nieokreślonych zachowań (w szczególności warunki wyścigowe) nie istnieją.

Istnieje jednak szereg narzędzi, które pokazują dużą liczbę takich sytuacji. Być może możesz udowodnić, że istnieje problem z takimi narzędziami, nawet jeśli nie możesz udowodnić, że poprawka jest poprawna.

Kilka interesujących narzędzi do tego celu:

Valgrind to narzędzie do sprawdzania pamięci. Znajduje wycieki pamięci, odczytuje niezainicjowaną pamięć, wykorzystuje zwisające wskaźniki i dostęp poza granicami.

Helgrind to narzędzie do sprawdzania bezpieczeństwa wątków. Znajduje warunki wyścigu.

Oba działają za pomocą dynamicznej instrumentacji, tzn. Biorą twój program w niezmienionej postaci i uruchamiają go w zwirtualizowanym środowisku. To sprawia, że ​​są nieinwazyjne, ale powolne.

UBSan to niezdefiniowany moduł sprawdzania zachowania. Znajduje różne przypadki nieokreślonego zachowania C i C ++, takie jak przepełnienie liczb całkowitych, przesunięcia poza zasięgiem i podobne rzeczy.

MSan to kontroler pamięci. Ma podobne cele jak Valgrind.

TSan to narzędzie do sprawdzania bezpieczeństwa wątków. Ma podobne cele jak Helgrind.

Te trzy są wbudowane w kompilator Clanga i generują kod w czasie kompilacji. Oznacza to, że musisz zintegrować je z procesem kompilacji (w szczególności musisz skompilować z Clangiem), co znacznie utrudnia ich początkową konfigurację niż * grind, ale z drugiej strony mają znacznie niższy czas działania.

Wszystkie wymienione przeze mnie narzędzia działają w systemie Linux, a niektóre w systemie MacOS. Nie sądzę, aby jakakolwiek praca w systemie Windows była jeszcze niezawodna.

Sebastian Redl
źródło
1

Wydaje się, że większość odpowiedzi tutaj myli to pytanie jako „jak automatycznie wykryć warunki wyścigu?” kiedy pytanie brzmi „jak odtworzyć warunki wyścigu podczas testów, gdy je znajdę?”

Można to zrobić, wprowadzając synchronizację w kodzie używanym wyłącznie do testowania. Na przykład, jeśli wyścig wystąpi, gdy zdarzenie X nastąpi między zdarzeniem A a zdarzeniem B, wówczas w celu przetestowania aplikacji napisz kod, który czeka na zdarzenie X po zdarzeniu A. Prawdopodobnie będziesz potrzebować sposobu, aby testy porozmawiały z Twoją aplikacją, aby to powiedzieć („hej, testuję to, więc poczekaj na to wydarzenie w tej lokalizacji”).

Używam node.js i mongo, gdzie niektóre działania wymagają tworzenia spójnych danych w wielu kolekcjach. W takich przypadkach moje testy jednostkowe wywołają aplikację, aby powiedzieć jej „skonfiguruj oczekiwanie na zdarzenie X”, a po skonfigurowaniu aplikacji zostanie uruchomiony test dla zdarzenia X, a następnie testy pokażą aplikacja („skończyłem z oczekiwaniem na zdarzenie X”), więc pozostałe testy będą działać normalnie.

Odpowiedź tutaj wyjaśnia szczegółowo tego rodzaju rzeczy w kontekście pythona: https://stackoverflow.com/questions/19602535/how-can-i-reprrive-the-race-conditions-in-this-python-code- niezawodnie

BT
źródło