Witam, poniżej omawiam używanie debuggera oraz kilku narzędzi, który ułatwiają wyszukiwanie błędów w programach.

Opis debuggera.

Debugger jest narzędziem Delphi przeznaczonym do łatwego i efektywnego wyszukiwania błędów w kodzie. Pozwala on na zatrzymywanie błędów w określonych przez programistę punktach programu, szpiegować wartości zmiennych oraz ich zmianę oraz wpływać na wartości zmiennych w czasie działania programu, co pozwala sprawdzić, bez rekompilacji projektu, jak zachowa się kod programu, gdy dana zmienna będzie miała taką a taką wartość.

Punkty przerwań; praca krokowa.

Często pisząc program i testując go, chcielibyśmy wiedzieć jak działa napisany przez nas kod, dlaczego podany algorytm nie działa poprawnie, jakim cudem dana zmienna przybiera dziwne, nieoczekiwane wartości. W takim przypadku, możemy ustawić odpowiednie punkty przerwań w edytorze kodu. Po uruchomieniu programu będzie on działał z pełną szybkością aż do momentu, gdy napotka linię oznaczoną jako punkt przerwania. Działanie programu zostanie wstrzymane, sterowanie zostanie zwrócone do Delphi. Mamy teraz możliwość podglądania zmiennych, pracy krokowej (to znaczy, że wykonujemy program linijka po linijce) oraz możliwość zmiany wartości pewnych zmiennych. Możemy w każdej chwili powrócić do programu działającego z pełną szybkością – tak jak byśmy uruchamiali projekt od nowa, czyli np. skrótem F9 lub poprzez kliknięcie zielonej strzałki Run.

Utwórz nowy projekt w Delphi, dodaje na formę przycisk. OnClick przycisku powinna wyglądać następująco:

Kod nie wykonuje nic ciekawego, użyjemy go tylko do przykładów.

Jeżeli chcesz dodać nowy punkt przerwania, możesz kliknać z lewej strony edytora kodu na tak zwanym gutterze (jest to ten szary pasek leżący z lewej strony edytora kodu) w odpowiedniej linijce. Możesz też kliknąć prawym przyciskiem myszki na odpowiedniej linijce kodu i z menu kontekstowego Debug wybrać Toggle Breakpoint. Jeżeli chcesz usunąć dany punkt, kliknij ponownie na gutter w danej linii lub ponownie wybierz pozycję z menu kontekstowego. Jeżeli nie chcesz usuwać danego punktu, ale chcesz go na chwilę dezaktywować, kliknij prawym przyciskiem myszki na czerwona kropkę i wybierz Enabled (aktywujesz go w ten sam sposób). Możesz też używać skrótu – F5 (dodanie/usunięcie punktu przerwania). Dodaj teraz nowy punkt przerwania w linijce a := 5 ; Linijka zostanie podświetlona na czerwono, co oznacza, że jest punktem przerwania, a obok niej, na gutterze, pojawi się czerwona kropka: 

Uruchom program i naciśnij przycisk. Sterowanie zostanie zwrócone do Delphi, pokaże się kod programu z punktem przerwania. Obok czerwonej kropki na gutterze, zobaczysz małą zieloną strzałkę. Jest to tak zwany punkt wykonania, oznaczający linię, która zostanie wykonana w następnej kolejności. Linia ta zaznaczona jest na niebiesko (jeżeli jednak w tej samej linijce znajduje się punkt przerwania, to linia będzie czerwona). Naciśnij teraz F8, co spowoduje wykonanie jednej linijki programu (dokładniej o tym za chwilę), czyli do ‘a’ przypisana zostanie wartość 5. Teraz dobrze widać, że następna linia, która zostanie wykonana, zostaje podświetlona na niebiesko, a na gutterze widnieje znajoma zielona strzałka:

Na gutterze widać także małe, niebieski kropki. Na razie się nimi nie przejmuj, omówię je dalej.

Mamy teraz możliwość sprawdzać, jakie wartości mają zmienne w programie. Służą do tego lista wyrażeń testowych, inspektor śledzenia oraz okno Evaluate/Modify, którymi jednak zajmiemy się później. Jest jeszcze jeden, wygodny sposób ma sprawdzanie wartości zmiennej – dymek z podpowiedzią, który zawiera wartość zmiennej wskazanej przez kursor myszy. Może to być pojedyncza zmienna np. Integer, a może to być rekord czy tablica – wtedy zostaną wyświetlone wszystko pola rekordu/elementu tablicy. Najedź myszką na zmienną a, w dymku podpowiedzi zobaczysz 5. Zauważ, że nad b nie pojawi się w ogóle podpowiedź, jeżeli najedziesz na nią myszką – to też omówimy dalej. Przejdź do końca metody za linijka po linijce za pomocą F8 a następnie naciśnij F9, dzięki czemu z powrotem wrócisz do programu, kliknij na pasku zadań na swój program, co spowoduje przywrócenie go na pierwszy plan. Jeżeli nie chcesz wracać do programu, możesz nacisnąć Ctrl + F2 – program zostanie natychmiast zamknięty, powrócisz do Delphi.

To tyle wstępnych informacji na temat punktów przerwań. Zajmijmy się teraz pracą krokową.

Już w poprzednim przykładzie pracowaliśmy krokowo – program wykonywał się linijka po linijce, za pomocą F8. Jest to skrót do Step Over (tłumaczone jako przekroczenie), które znajduje się menu Run->Step over. Przekroczenie powoduje wykonanie linijki kodu zaznaczonej przez punkt wykonania (ta zielona strzałka na gutterze) oraz zatrzymanie się w następnej linijce, co już spraktykowaliśmy powyżej. Oprócz przekroczenia, możemy posłużyć się wkroczeniem (Trace Into). Czym różni się ono od przekroczenia? Otóż, jeżeli aktualną linijką jest wywołanie procedury/funkcji, to użycie przekroczenia spowoduje wykonanie się tej proc/fun oraz przejście do następnej linii. Natomiast wkraczanie przenosi nas do danej funkcji i możemy poruszać się linia po linii jej ciała. Skrótem dla Trace Into jest F7.

Otwórz teraz jakiś swój projekt i poeksperymentuj z pracą krokową. Na pewno zauważyłeś, że czasami, gdy ustawisz punkt przerwania i uruchomisz program, na kropce punktu przerwania na gutterze jest ‘x’ zamiast ‘ptaszka’. Takie punkt przerwania traktowany jest jako niepoprawny i nie spowoduje zatrzymania programu. Dlaczego tak się dzieje? Otóż, punkty przerwań mogą ustawiane jedynie w miejscach programu, które są liniami wykonania. Czyli np. nie możesz ustawiać punktów przerwań w liniach komentarzy, deklaracjach, pustych liniach. Czas na rozwianie tajemnicy małych, niebieskich kropek na gutterze 😉 Oznaczają on linie, w których możesz ustawić punkty przerwań.

Poza tym, napisałem wcześniej, że nad zmienna ‘b’ nie zostanie pokazany dymek w drugiej linijce (b := a * 10). Dzieje się tak z powodu optymalizacji – kompilator rozpoznaje, które zmienne nie są wykorzystywane w programie i je omija – na razie b do niczego nie służy, przechowuje tylko wartość. Aby wyeliminować ten program, zmienna b powinna stać po prawej stronie operatora przypisania. Możesz też wyłączyć optymalizację poprzez Menu->Project->Options->Compiler i odznaczyć Optimization (na czas pracy z debuggerem) lub oznaczyć fragment kodu dyrektywą wyłączające ją z optymalizacji:

Dymki z wartością nie są również wyświetlane w bloku with – i nic na to nie poradzimy. W tym przypadku, nad zmiennymi X oraz Y nie pojawią się podpowiedzi (ale nad JakisRekord – tak):

Co do punktów przerwań – istnieje też możliwość, aby ustalić, po którym przejściu przez dany punkt ma się zatrzymać program, np. chcemy, aby dopiero za piątym razem wykonania jakiejś procedury program zatrzymał się. Możemy też podać wyrażenie, którego wartość obliczana jest podczas natrafienia na dany punkt – jeżeli jest ono prawdziwe, to program zatrzymuje swoje działanie w tym punkcie. Kliknij prawym przyciskiem myszki na gutterze na czerwonej kropce oznaczającej punkt przerwania i wybierz z menu kontekstowego Breakpoint properties. Pojawi się następujące okno:

W polu Pass count wpisujemy, za którym przejściem przez punkt przerwania ma nastąpić zatrzymanie programu. Pozostaw 0, jeżeli program ma się zatrzymać za pierwszym razem. W pole Condition możesz wpisać warunek. Ustaw punkt przerwania w linijce b := a * 10 (poprzedni punkt usuń). Otwórz okno Breakpoint properties i w Condition wpisz a > 5. Uruchom program, naciśnij przycisk – program się nie zatrzyma. Możesz też wpisywać inne wyrażenia, np. Edit1.Text = ‘jakis tekst’ ;

Do zarządzania punktami przerwań służy Okno listy przerwań. Pozwala ono w łatwy i prosty sposób zarządzać punktami przerwań zgromadzonymi w całym projekcie – aktywować/dezaktywować je, dodawać/usuwać, modyfikować ich opcje. Aby wyświetlić to okna, wybierz z Menu->View->Debug Windows->Breakpoints.

Dodam na koniec, że punkty przerwania możesz dodawać w każdym momencie – czy to przed uruchomieniem projektu, czy w trakcie wykonywania czy też podczas pracy krokowej.

Polecenie Run To Cursor, dostępne z menu Run lub menu kontekstowego edytora kodu (w Debug) powoduje wykonanie programu aż do osiągnięcia kursora – wtedy następuje zatrzymanie programu. Jest to tak jakby jednorazowy punkt przerwania, ponieważ po przerwaniu wykonania programu, punkt ten już nie obowiązuje. Uwaga: jeżeli program napotka na drodze jakiś punkt przerwania, po wznowieniu działania programu polecenie to nie będzie już aktualne. Skrót do Run To Cursor to F4.

Lista wyrażeń testowych – podgląd zmiennych.

Gdy działanie programu zostanie wstrzymane przez punkt przerwania, możesz sprawdzać wartości zmiennych, kontrolować ich zmiany itp. Służy do tego lista wyrażeń testowych – Watch List. Dzięki niej możemy monitorować wartości raczej pojedynczych zmiennych (do bardziej złożonych struktur jak klasy czy rekordy użyj inspektora śledzenia). Okno to wywołujesz poprzez Menu->Debug Windows->Watches. Jego menu kontekstowe zawiera pozycje odpowiedzialne za dodawanie/usuwanie/edytowanie/dezaktywowanie/aktywowanie itd. poszczególnych wyrażeń testowych, oraz takie, które wykonują działania na wszystkich dodanych wpisach.

Możesz dodać nową pozycję na kilka sposobów:
1. Wybierz zmienną w kodzie, a następnie z menu kontekstowego edytora kodu wybierz Debug->Add Watch at Cursor
2. Wybierz zmienną i wciśnij Ctrl+F5.
3. Wybierz z Menu->Run->Add Watch i wpisz nazwę zmiennej.

Nie ma ograniczenia co do ilości wyrażeń testowych, jednakże zbyt duża ich ilość może spowodować spowolnienie pracy w czasie zatrzymania programu, gdyż wartości zmiennych są obliczane po każdej przebytej linijce kodu, a przy większej ilości wyrażeń testowych – może to zająć trochę czasu.

Gdy będziesz już w trakcie zatrzymania programu i program będzie wykonywany linia po linii, wartości na liście będą uaktualniane. Jak jednak widzisz na górze, zamiast wartości widnieje wpis [process not accessible], co oznacza, że program nie jest aktualnie w fazie zatrzymania.

Przetestujmy nowo poznane narzędzie. Ustaw punkt przerwania w a := 5 ; Następnie, uruchom program i naciśnij przycisk – program zatrzyma się na odpowiedniej linii. Wywołaj okno wyrażeń testowych z Menu->Debug Windows->Watches. Możesz zadokować je gdzieś w edytorze, aby było zawsze w tym samym miejscu lub wybrać z jego menu kontekstowego StayOnTop, dzięki czemu będzie ono zawsze widoczne, na wierzchu. Kliknij prawym przyciskiem na zmienną a, wybierz z Debug->Add Watch at Cursor – zmienna zostanie dodana, w oknie wyrażeń testowych pojawi się nowy wpis. To samo zrób ze zmienną b. Teraz, linia po linii wykonuj program i obserwuj zmiany.

Jest to bardzo przydatna rzecz, o czym przekonasz się nie podczas szukania błędów w swoich aplikacjach. Możemy też ustalać pewne właściwości dla dodanych wyrażeń. Kliknij prawym przyciskiem na pierwszy wpis i wybierz z menu Edit Watch. Pojawi się okno edycji wyrażenia:

Expression: tu podajemy nazwę zmiennej, którą chcemy ‘monitorować’.
Group name: tu możemy podać nazwę grupy (już istniejącej, lub nowej – zostanie ona utworzona). Dzięki temu możemy pogrupować różne wyrażenia w odpowiadający dla nas sposób.
Repeat count: przydatne przy podglądaniu tablic. Jeżeli chcesz monitorować np. elementy tablic tab : array [1..20] z zakresu 10-15, to w pole Expression wpisz tab[10] a w Repeat count 5. Jeśli w Expression podasz samą nazwę tablicy, to monitorowane będą wszystkie jej elementy. Możesz ograniczyć zakres właśnie poprzez Repeat count – wtedy wzięte zostaną pod uwagę elementy od tab[0] do Repeat count – 1.
Digits: używane, gdy podglądasz liczby zmiennoprzecinkowe. Digits oznacza liczbę cyfr znaczących obserwowanej wartości. Uwaga: działa tylko wtedy, gdy jako formatowanie wybierzesz Floating point.
Enabled: oznacza, czy wyrażenie ma być aktywne czy nie (w tym przypadku w oknie wyrażeń testowych pokaże się, zamiast wartości, .
Allow Function Calls: przy zaznaczeniu tej opcji sprawdzane będą też wartości tych wyrażeń, które wymagają wywołanie funkcji. Przy wyłączonej opcji debugger będzie traktował wartość jako niedostępna (inaccessible).
Znajdujące się niżej przycisku radio służą do wybory formatowania wyświetlanych wartości. Po dokładniejszy opis odsyłam do Help’a Delphi.

To tyle, jeśli chodzi o listę wyrażeń testowych.

Inspektor śledzenia.

Służy on do podglądania bardziej złożonych struktur jak obiekty klas czy rekordy. Używać go można jedynie podczas zatrzymania programu. Aby przyjrzeć się bliżej danemu obiektowi, kliknij na niego prawym przyciskiem myszki i wybierz z menu Dubug->Inspect lub z menu głównego->Run->Inspect (skrót: Alt + F5).

Dla przykładu, uruchom program i naciśnij przycisk (zakładam, że w jego OnClick znajduje się punkt przerwania). Gdy program zostanie zatrzymany, z menu Run wybierz Inspect i wpisz Form1. Ukaże się okno zawierające trzy zakładki:

Masz teraz możliwość podglądać i zmieniać ustawienia formy. Na pasku statusu wyświetla się typ wybranego elementu. Pierwsza zakładka zawiera elementy należące do bezpośredniego przodka obiektu klasy (o ile taki występuje) oraz obiekty innych klas, jakie zawiera w sobie podglądana klasa. Jeżeli nie badamy obiektu klasy, to zostanę tu wyświetlona odpowiednie inne wartości, np. pola rekordu wraz z wartościami. Następne dwie zakładki pojawiają się tylko, gdy monitorujesz obiekt klasy – nie są one dostępne w przypadku prostszych typów danych. Na drugiej zakładce znajdują się metody klasy badanego obiektu, a na Properties właściwości klasy wraz z aktualnymi wartościami.
Wartości są uaktualniane wraz z postępem wykonania programu.

Możesz zmieniać wartość tych elementów, przy których znajduje się przycisk z trzema kropkami – taki, jak na rysunku powyżej. Po kliknięciu na niego, pokaże się okno z prośbą o podanie nowej wartości.

Poprzez dwukrotne kliknięcie na daną pozycję zaczynasz inspekcję nowego elementu – kliknij dwa razy na Button1 – pojawi się nowe okno. Naraz może być uruchomionych wiele okien inspekcji.

Jeżeli chcesz, aby okno inspekcji było zawsze na wierzchu, to wejdź do Menu->Tools->Debugger Options i zaznacz Inspectors stay on top.

Do omówienia pozostało menu kontekstowe inspektora, zawiera ono kilka przydatnych funkcji:

Change: pozwala zmienić wartość wskazanej właściwości.
Show Inherited: jeżeli ta opcja jest zaznaczona, to wyświetlane są elementy, które posiada bezpośredni przodek klasy badanego obiektu.
Show Fully Qualified Names: pokazuje pełne nazwy metod/właściwości, to znaczy dodaje na początku pochodzenia danej metody/właściwości, np: Forms.TCustomForm.Hide.
Inspect: powoduje otwarcie nowego okno inspekcji z danymi o wybranym na liście obiekcie.
Descend: przydatna rzecz: po wybraniu danego elementu, np. Button1, wyświetlane są o nim informacje w tym samym oknie (tracimy widok o Form1). Jednakże, poprzednio obserwowane obiekty są zapamiętywane – wystarczy, że z górnego ComboBoxa wybierzesz interesujący Cię, poprzednio podglądany obiekt.
New Expression: pozwala śledzić nową wartość.
Type Cast: pozwala zmienić typ wyświetlania wartości.

Okno Evaluate/Modify.

Ostanim narzędziem, które omowię, jest okno Evaluate/Modify. Służy ono, podobnie jak lista wyrażeń testowych oraz inspektor, do śledzenia wartości zmiennych. Możliwe jest także zmienianie wartości – dzięki temu możemy sprawdzić, bez rekompilacji, jak program zachowa się dla takich a takich danych. Jeżeli chcesz dodać jakąś zmienną do listy, kliknij na niej prawym przyciskiem i z menu Debug wybierz Evaluate/Modify. To samo możesz osiągnąć poprzez menu główne Run->Evaluate/Modify (skrót: Ctrl + F7). Po wybraniu dodaniu zmiennej ujrzysz następujące okno:

Expression podajesz, wartość jakiego wyrażenia chcesz sprawdzić. Wyrażenia – to znaczy, zmiennej lub np. 100 = 10. Po naciśnięciu przycisku Evaluate w polu Result podana zostanie wartość obliczonego wyrażenia, czyli false, bo 100 <> 10. Jeżeli chcesz zmienić wartość zmiennej, wpisz w pole New value nową wartość i kliknij Modify.

Możesz dodawać zmienne zarówno przed uruchomieniem programu, jak i w czasie jego zatrzymania, jednak w tym pierwszym przypadku w polu Result pojawi się wpis: Undeclared identifier: 'a’

Narzędzie to różni się od poprzednich tym, że wartość wyrażenia nie jest obliczana wraz z postępem wykonania programu – to znaczy, że jeżeli chcesz poznać wartość zmiennej, musisz kliknąć na przycisk Evaluate. Dzięki temu, działa ono szybciej od poprzedników. Jeżeli przeglądałeś już kilka zmiennych i chcesz szybko wrócić z powrotem do którejś z nich – możesz wybrać ja z pola Expression (które to jest ComboBox’em). Jeżeli chcesz, możesz wpisać nazwę ręcznie.

Jeżeli chcesz szybko dodać aktualnie sprawdzane wyrażenie do listy wyrażeń testowych bądź do inspektora – użyj przycisków Watch oraz Inspect.

Na tym kończę artykuł o używaniu debuggera w Delphi oraz narzędzi z nim współpracujących. Nie są to jednak pełne możliwości 😉 istnieje jeszcze wiele innych ciekawych narzędzi do wykrywania błędów itp., jednakże są one przeznaczone dla zaawansowanych programistów. W razie czego, odsyłam do Helpa Delphi.

Autor: Iskar