Co to jest debugger i jak może mi pomóc zdiagnozować problemy?

Aug 19 2014

Ma to być pytanie ogólnego przeznaczenia, aby pomóc nowym programistom, którzy mają problem z programem, ale nie wiedzą, jak użyć debugera do zdiagnozowania przyczyny problemu.

To pytanie obejmuje trzy klasy bardziej szczegółowych pytań:

  • Kiedy uruchamiam program, nie generuje on wyników, których oczekuję od danych wejściowych, które mu podałem.
  • Gdy uruchamiam program, ulega awarii i wyświetla mi ślad stosu. Mam zbadać ślad stosu , ale nadal nie wiem przyczynę problemu, ponieważ ślad stosu nie daje mi wystarczająco dużo informacji.
  • Kiedy uruchamiam program, ulega awarii z powodu błędu segmentacji (SEGV).

Odpowiedzi

68 Raedwald Aug 19 2014 at 20:49

Debugger to program, który może zbadać stan programu podczas jego działania. Środki techniczne, których używa do tego, nie są ważne dla zrozumienia podstaw korzystania z debugera. Możesz użyć debugera, aby zatrzymać wykonywanie programu, gdy osiągnie on określone miejsce w kodzie, a następnie zbadać wartości zmiennych w programie. Możesz użyć debugera do bardzo powolnego uruchamiania programu, po jednym wierszu kodu na raz (tzw. Krok po kroku ), podczas gdy badasz wartości jego zmiennych.

Korzystanie z debugera to oczekiwana podstawowa umiejętność

Debugger to bardzo potężne narzędzie pomagające w diagnozowaniu problemów z programami. Debuggery są dostępne dla wszystkich praktycznych języków programowania. Dlatego umiejętność korzystania z debuggera jest uważana za podstawową umiejętność każdego programisty profesjonalnego lub entuzjastów. I za pomocą debuggera samemu uważa się za podstawowe prace należy zrobić sobie przed pytając innych o pomoc. Ponieważ ta witryna jest przeznaczona dla profesjonalnych i entuzjastów programistów, a nie jest stroną pomocy technicznej lub mentoringu, jeśli masz pytanie dotyczące problemu z określonym programem, ale nie korzystałeś z debuggera, jest bardzo prawdopodobne, że Twoje pytanie zostanie zamknięte i odrzucone. Jeśli będziesz upierał się przy takich pytaniach, w końcu nie będziesz mógł publikować kolejnych.

Jak debugger może ci pomóc

Używając debuggera, możesz odkryć, czy zmienna ma złą wartość i gdzie w programie została zmieniona na niewłaściwą wartość.

Korzystając z pojedynczych kroków, możesz również sprawdzić, czy przepływ sterowania jest zgodny z oczekiwaniami. Na przykład, czy ifgałąź została wykonana, kiedy się tego spodziewasz.

Ogólne uwagi dotyczące korzystania z debuggera

Specyfika korzystania z debugera zależy od debugera i, w mniejszym stopniu, od używanego języka programowania.

  • Możesz dołączyć debugger do procesu, który już działa w twoim programie. Możesz to zrobić, jeśli program utknął.

  • W praktyce często łatwiej jest uruchomić program od samego początku pod kontrolą debuggera.

  • Wskazujesz miejsce, w którym twój program powinien przestać działać, wskazując plik z kodem źródłowym i numer wiersza, w którym ma się zatrzymać wykonywanie, lub wskazując nazwę metody / funkcji, na której program ma się zatrzymać (jeśli chcesz zatrzymać jako jak tylko wykonanie wejdzie do metody). Środek techniczny używany przez debuger do zatrzymania programu nazywany jest punktem przerwania, a proces ten nazywa się ustawieniem punktu przerwania .

  • Większość nowoczesnych debugerów jest częścią IDE i zapewnia wygodny interfejs graficzny do badania kodu źródłowego i zmiennych programu, z interfejsem typu „wskaż i kliknij” do ustawiania punktów przerwania, uruchamiania programu i wykonywania jednego kroku.

  • Korzystanie z debugera może być bardzo trudne, chyba że pliki wykonywalne programu lub pliki kodu bajtowego zawierają informacje o symbolach debugowania i odniesienia do kodu źródłowego. Być może będziesz musiał skompilować (lub ponownie skompilować) swój program w nieco inny sposób, aby upewnić się, że informacje są obecne. Jeśli kompilator przeprowadza rozległe optymalizacje, te odniesienia mogą stać się mylące. Dlatego może być konieczne ponowne skompilowanie programu z wyłączonymi optymalizacjami .

39 SlugFiller Apr 05 2015 at 19:15

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ś dokładnie 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 nie pojawiłby się.
  • 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ę, w momencie 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 rejestrowanie 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 obszarze z błędami.

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.