Chcę dodać, że debugger nie zawsze jest idealnym rozwiązaniem i nie zawsze powinien być rozwiązaniem do debugowania. Oto kilka przypadków, w których debugger może nie działać dla Ciebie:
- Część twojego programu, która zawodzi, jest naprawdę duża (być może słaba modularyzacja?) I nie jesteś do końca pewien, od czego zacząć przechodzenie przez kod. Przejście przez to wszystko może być zbyt czasochłonne.
- Twój program używa wielu wywołań zwrotnych i innych nieliniowych metod kontroli przepływu, co sprawia, że debugger jest zdezorientowany, gdy przez niego przechodzisz.
- Twój program jest wielowątkowy. Albo co gorsza, twój problem jest spowodowany stanem wyścigu.
- Kod, w którym jest błąd, jest uruchamiany wiele razy, zanim zostanie usunięty. Może to być szczególnie problematyczne w głównych pętlach lub, co gorsza, w silnikach fizycznych, gdzie problem może być numeryczny. Nawet ustawienie punktu przerwania w tym przypadku po prostu spowodowałoby, że uderzyłbyś go wiele razy, a błąd się nie pojawił.
- Twój program musi działać w czasie rzeczywistym. Jest to duży problem dla programów łączących się z siecią. Jeśli skonfigurujesz punkt przerwania w kodzie sieci, drugi koniec nie będzie czekał, aż przejdziesz, po prostu wygaśnie. Programy, które opierają się na zegarze systemowym, np. Gry z pomijaniem klatek, również nie są dużo lepsze.
- Twój program wykonuje jakąś formę destrukcyjnych działań, takich jak zapisywanie w plikach lub wysyłanie e-maili, i chciałbyś ograniczyć liczbę razy, kiedy musisz przez niego przejść.
- Możesz powiedzieć, że twój błąd jest spowodowany przez nieprawidłowe wartości docierające do funkcji X, ale nie wiesz, skąd te wartości pochodzą. Konieczność ciągłego przeglądania programu i ustawiania punktów przerwania coraz dalej i dalej wstecz może być ogromnym kłopotem. Zwłaszcza jeśli funkcja X jest wywoływana z wielu miejsc w programie.
We wszystkich tych przypadkach albo nagłe zatrzymanie programu może spowodować różnice w wynikach końcowych, albo ręczne przejście w poszukiwaniu jednej linii, w której spowodowany jest błąd, jest zbyt kłopotliwe. Może się to również zdarzyć niezależnie od tego, czy błąd zachowuje się nieprawidłowo, czy też się zawiesił. Na przykład, jeśli uszkodzenie pamięci powoduje awarię, do momentu wystąpienia awarii jest zbyt daleko od miejsca, w którym nastąpiło pierwsze uszkodzenie pamięci i nie pozostają żadne przydatne informacje.
Więc jakie są alternatywy?
Najprostsze jest po prostu logowanie i asercje. Dodawaj dzienniki do programu w różnych miejscach i porównuj otrzymane wyniki z oczekiwaniami. Na przykład sprawdź, czy funkcja, w której myślisz, że jest błąd, jest w ogóle wywoływana w pierwszej kolejności. Sprawdź, czy zmienne na początku metody są tym, czym myślisz, że są. W przeciwieństwie do punktów przerwania, istnieje wiele wierszy dziennika, w których nie dzieje się nic specjalnego. Możesz po prostu przeszukać dziennik później. Gdy trafisz w wiersz dziennika, który różni się od tego, czego się spodziewasz, dodaj więcej w tym samym obszarze. Zawężaj go coraz dalej, aż stanie się wystarczająco mały, aby móc zarejestrować każdą linię w zanieczyszczonym obszarze.
Asercji można używać do wychwytywania nieprawidłowych wartości w momencie ich wystąpienia, a nie wtedy, gdy mają one wpływ widoczny dla użytkownika końcowego. Im szybciej złapiesz nieprawidłową wartość, tym bliżej jesteś linii, która ją utworzyła.
Refaktoryzacja i test jednostkowy. Jeśli Twój program jest zbyt duży, warto przetestować go pojedynczo po jednej klasie lub funkcji. Podaj dane wejściowe, spójrz na wyniki i zobacz, które nie są zgodne z oczekiwaniami. Możliwość zawężenia błędu z całego programu do pojedynczej funkcji może mieć ogromny wpływ na czas debugowania.
W przypadku wycieków pamięci lub tępienia pamięci użyj odpowiednich narzędzi, które są w stanie analizować i wykrywać je w czasie wykonywania. Umiejętność wykrycia, gdzie faktycznie występuje korupcja, jest pierwszym krokiem. Następnie możesz użyć dzienników, aby wrócić do miejsca, w którym wprowadzono nieprawidłowe wartości.
Pamiętaj, że debugowanie to proces cofający się. Masz wynik końcowy - błąd - i znajdź przyczynę, która go poprzedzała. Chodzi o cofanie się i, niestety, debuggery robią tylko krok naprzód. W tym miejscu dobre logowanie i analiza pośmiertna mogą dać znacznie lepsze wyniki.