• EDBG - debugger dla AmigaE

26.04.2020 18:08, autor artykułu: Krzysztof Donat
odsłon: 1534, powiększ obrazki, wersja do wydruku,

EDBG logo

W pracy każdego programisty zdarzają się sytuacje, że jego mozolnie tworzony program nie działa tak jak to sobie zaprojektował. To mogą być różne przypadki - od błędnych wyników wyliczeń, poprzez przypadkowe i nieprzewidywalne działanie programu, aż po totalne zawieszenia się programu. W przypadku Amigi takie problemy mogą być szczególnie uciążliwe, bo błąd w programie często kończy się zawieszeniem całego komputera, a jedynym ratunkiem jest reset poprzez Ctrl+A+A.

Jak sobie radzić w takich sytuacjach? Rozwiązań jest jak zwykle kilka. Najprostsze to rozszerzone logowanie, czyli wstawienie w kod programu kontrolnych wydruków na konsolę, które będą wyświetlały wartości zmiennych na poszczególnych krokach programu, i w ten sposób próbować zorientować się gdzie jest problem. Innym sposobem jest komentowanie (wyłączanie) fragmentów programu, od największych, stopniowo włączając kolejne bloki programu i sprawdzanie kiedy pojawi się błąd. Najpopularniejszą metodą na analizowanie działania programów są jednak debuggery, czyli specjalistyczne programy do krokowego wykonywania i śledzenia programu. Kryterium obecności debuggera przy wyborze środowiska programistycznego i języka do nauki jest często niedoceniane i nie brane pod uwagę, a istnieją szacunki mówiące że średni czas programisty poświęcany na debugowanie programu to nawet 50% czasu pracy nad projektem. Warto więc przed wyborem języka, któremu poświęcimy dużo czasu na naukę, zweryfikować czy jest dla niego dostępny i jak działa debugger. W wydaniu amigowym debuggery są dostępne dla różnych języków programowania, lecz z reguły są to programy dedykowane i dostarczane razem z konkretną wersją kompilatora. Tak też jest w przypadku pakietu AmigaE, gdzie autor Wouter van Oortmerssen oprócz kompilatora dostarczył również narzędzia pomocnicze, w tym program EDBG, który jest debuggerem dla programów tworzonych w języku AmigaE. Cały pakiet można pobrać z Aminetu, jest dostępny pod adresem http://aminet.net/package/dev/e/amigae33a. Rozwój EDBG niestety zatrzymał się w roku 1997 na wersji 3.3 razem z całym AmigaE - a szkoda, bo jak sam autor przyznaje nie zostały zaimplementowane w niej wszystkie zaplanowane funkcjonalności. Co więc oferuje EDBG w swojej ostatniej wersji? Całkiem sporo, w tym najważniejsze - krokowe wykonywanie programu, podgląd zmiennych, system punktów kontrolnych (breakpointy) i monitor pamięci i rejestrów. Zanim jednak będzie możliwość debugowania jakiegoś programu napisanego w E, trzeba go odpowiednio do tego przygotować. Jest to banalnie prosta czynność, bo podczas kompilacji wystarczy ustawić flagę DEBUG. Doda ona do kodu wynikowego niezbędne dla debuggera informacje. Trzeba jednak pamietać, żeby wersja finalna programu którą opublikujemy dla świata była skompilowana bez tej flagi. Spójrzmy na przykładowy program 'prog1':

/*  prog1.e   */
PROC main() 
    DEF i, str:PTR TO CHAR

    str:='Iteracja nr'
    
    FOR i:=0 TO 9
        WriteF('\s \d' , str, i )
        MOVE.L i, D0
        WriteF('\n')
    ENDFOR
    
    CleanUp(0)
ENDPROC

Program 'prog1' jest bardzo prosty - krótka pętla FOR dziesięciokrotnie inkrementuje zmienną 'i' od 0 do 9 oraz wypisuje nr iteracji na konsolę. Dodatkowo w każdej iteracji do rejestru D0 kopiowana jest wartość zmiennej 'i'.

Po kompilacji programu (z flagą DEBUG), można już uruchomić go w środowisku EDGB. Przedtem należy upewnić się, że w systemie uruchomiony jest interpreter skryptów ARexx. EDBG ma wbudowany port ARexxa, z którego korzysta m. in. do zapisywania i ładowania swoich ustawień. Z pomocą ARexxa zapamiętuje też listę zmiennych monitorowanych w programie czy pozycje poszczególnych okien, dzięki czemu nie trzeba tego za każdym razem ustawiać. Najprostsza sekwencja uruchomienia wygląda jak na poniższym ekranie:

EDBG - komp1

Jeśli wszystko przebiegło dobrze, EDBG otworzy się na własnym ekranie (można wskazać też dowolny publiczny ekran) z wczytanym programem do wykonania i otwartm kodem źródłowym. Powinno to wyglądać mniej więcej jak na poniższym ekranie.

EDBG - v1

Na ekranie widać kilka otwartych okien oraz toolbar z kilkoma najczęściej wykorzystywanymi funkcjami. Funkcje toolbara to kolejno:

EDBG - toolbar

Okno z kodem źródłowym to proste okno tekstowe, gdzie podświetlana jest kolejna linia programu do wykonania w następnym kroku. Okno jest bardzo minimalistyczne, niestety brakuje w nim choćby informacji o pozycjach wstawionych breakpointów, przydatne też byłoby wyświetlanie numerów linii. Ma za to kilka interaktywnych funkcji - dwuklik kursorem myszy na nazwie zmiennej dodaje tą zmienną do monitora zmiennych, kliknięcie w innym miejscu pozwala ustawic breakpoint na żądanej linii i zatrzymać na niej wykonywanie programu. Monitor pamięci to okno w którym możemy podejrzeć wybrany fragment pamięci. Nie ma żadnych interaktywnych funkcji, ale wygodnie można wskazać jaki obszar pamięci nas interesuje. To może byc adres bezwzględny (np $B33000), ale też względny (np przesunięcie względem adresu dowolnej zmiennej). Możemy też skorzystać z wyrażenia AmigaE - np. jeśli jako adres podamy {i}, to zgodnie ze składnią E zostanie wyświetlony adres pod jakim znajduje się zmienna 'i'. Kolejnym bardzo przydatnym oknem jest monitor zmiennych. W zasadzie jest to chyba drugie najczęściej wykorzystywane okno debuggera, zaraz po oknie z kodem programu. Pozwala śledzić zawartość zmiennych oraz to jak się zmieniają podczas wykonywania kolejnych kroków programu. Monitor pokazuje aktualną wartość zmiennej oraz jej adres. W przypadku typów wskaźnkikowych (PTR TO ... ) widzimy również pierwsze 32 bajty pamieci na którą wskazuje zmienna, w postaci hexadecymalnej oraz ASCII. Największym brakiem monitora zmiennych jest brak wbudowanego podglądu pól w obiektach (strukturach AmigaE). Monitor pokazuje tylko adres takiego obiektu, a najczęściej interesuje nas jego zawartość. Jest na to rada, ale o tym napisze w dalszej części artykułu. Przydatnymi monitorami mogą się okazać również monitor rejestrów oraz monitor stosu programu. Pozwalają one zmonitorować m. in. wszystkie rejestry adresowe oraz danych naszego procesora (Ax i Dx). Jest to opcja przydatna zwłaszcza w sytuacji, kiedy wykorzystujemy w kodzie programu możliwość języka E polegającą na bezpośrednim używaniu instrukcji asemblerowych - program 'prog1' prezentuje tą możliwość. W przykładzie widać jak się zmienia rejestr D0 po każdym wykonaniu instrukcji MOVE.L i, D0. Ostatnim ważnym oknem jest okno konsoli, czyli standardowe wejście / wyjście naszego programu, na które będą przekierowane wszystkie wydruki programu.

Sam proces debuggowania jest prosty i intuicyjny. Po uruchomieniu automatycznie ładuje się plik źródłowy debugowanego programu, a kursor wykonywania programu ustawia się na pierwszej linii funkcji main(). Teraz możemy prześledzić działanie programu na kilka sposóbów:
a) metoda 'krok po kroku' - za pomocą toolbara (lub bardziej intuicyjnie z pomocą klawiszy kursorów 'Down' i 'Right') przechodzimy przez program
b) metoda brekpointów - ustawiamy punkt kontrolny w dowolnym miejscu kodu lub breakpoint reagujący na zmianę wartości dowolnej komórki w pamięci Amigi, i puszczamy program, który się normalnie wykonuje aż napotka ustawiony punkt kontrolny.
c) metoda automatycznego wykonywania krokowego - jest to zautomatyzowana metoda 'krok po kroku' - EDBG będzie w ustalonych odstępach wykonywał kolejne linie programu (domyślnie co 0.6 sekundy), pokazując co wykonuje i odświeżając widok monitorów po każdym kroku.

Oczywiście powyższe metody można ze sobą łączyć, optymalizując sobie pracę i przechodząc do wybranego miejsca w programie.
Ciekawą funkcją jest możliwość wpływu na działanie uruchomionego w debuggerze programu. Można to osiagnąć poprzez edycję zmiennych (ich wartości), dzięki czemu można w locie wytestować zachowanie się programu w różnych sytuacjach. W każdej chwili możemy tez uruchomić kalkulator wyrażeń AmigaE, w którym można używać zmiennych z wykonywanego programu oraz wartości z pamięci komputera. Zmienne w kalkulatorze można wykorzystać zgodnie ze składnią E, czyli np. bez problemu wyliczymy wyrażenie (zmiennaA+{zmiennaB}*3)-$bfe000. Bardzo to ułatwia tworzenie i testowanie algorytmów, gdy na bieżąco możemy zobaczyć efekt naszych zamierzeń.

Jedną z większych wad EDBG jest brak możliwości łatwego podglądu zawartości obiektów E. Z pomocą przychodzi nam jednak nieoceniony ARexx i program explorer, którego autorem jest Jason R. Hulance. Program ten jest również dostępny standardowo w pakiecie AmigaE v3.3, a jego nominalnym zadaniem jest załadowanie wybranego modułu (lub listy modułów) i możliwość ich przeglądania pod kątem zawartych w nich obiektów. Po załadowaniu mudułu intuition/intuition.m będziemy mogli zobaczyć budowę obiektów window, menu, gadget i innych z intutition. Uruchamiając explorera poleceniem 'explorer all' załadujemy obiekty ze wszystkim modułów z lokalizacji AmigaE (niepolecane przy wolniejszych amigach).

EDBG - explorer

Jeśli w systemie jest uruchomiony port Arrexa, a katalog bin z pakietu AmigaE znajduje się w ścieżce wyszukiwania uruchamianych programów (assign c: path_to_E/bin), to w zasadzie nie musimy nic robić, wystarczy że uruchomimy EDBG, a podczas dodawania zmiennej obiektu do monitora zmiennych przytrzymamy klawisz Shift. Ten Shift bedzie dla EDBG sygnałem, że chcemy wysłać typ i adres zmiennej do explorera. EDBG ma wbudowany skrypt ARexxa który to robi, przy okazji przekazując kilka innych informacji (np. nazwę ekranu EDBG na którym ma się uruchomić explorer). To rozwiązanie działa i to całkiem sprawnie. Możemy podglądać zawartość dowolnej systemowej struktury, których obiekty utworzyliśmy w naszeym programie, lub bardziej ogólnie te których znamy adres w pamięci. A co z naszymi prywatnymi obiektami? Tu niestety pojawia się mały problem, bo żeby załadować definicję obiektu do explorera, musi ona być w skompilowanym module, explorer niestety nie odczyta jej z naszego kodu źródłowego. Nie jest to jednak duży problem, bo i tak warto mieć swoje obiekty w modułach - jest to rozwiązanie zalecane przez autora AmigaE, i po okresie nauki bardzo ułatwiające i porzadkujące pracę. Spójrzmy na drugi przykład, w którym zdefinujemy własny obiekt, i spróbujemy go zdebuggować. Program składa się z dwóch części - modułu 'osoba', który zawiera definicje obiektu 'osoba' wraz z trzema funkcjami na nim działającymi, oraz programu 'prog2', kóry korzysta z tego modułu. Obiekt 'osoba' ma trzy pola: imie, nazwisko i wiek. Imię i nazwisko to wskaźniki na ciagi znaków, a wiek to zwykła 32-bitowa zmienna typu całkowitego. Do tego funkcje initOsoba() zwracająca zainicjowany obiekt, destroyOsoba() zwalniająca zaalokowane zasoby obiektu oraz printOsoba(), drukująca instancje obiektu na konsolę. Metoda initOsoba() w przypadku braku pamięci może zwrócić wyjątek ERR_OSOBA_MEM.

/*    osoba.e    */
OPT MODULE

EXPORT OBJECT osoba
    imie:PTR TO CHAR
    nazwisko:PTR TO CHAR
    wiek
ENDOBJECT

EXPORT ENUM ERR_OSOBA_MEM=1
RAISE ERR_OSOBA_MEM IF String()=NIL
RAISE ERR_OSOBA_MEM IF New()=NIL 

EXPORT PROC initOsoba() HANDLE

    DEF ptrOsoba:PTR TO osoba
    
    ptrOsoba:= New( SIZEOF osoba)
        ptrOsoba.imie:=String(16)
        ptrOsoba.nazwisko:=String(16)
        ptrOsoba.wiek:=0
    
    EXCEPT DO
    
    ReThrow()
    
ENDPROC ptrOsoba

EXPORT PROC destroyOsoba(ptrOsoba:PTR TO osoba)
    
    IF ptrOsoba 
        IF ptrOsoba.imie         THEN DisposeLink(ptrOsoba.imie)
        IF ptrOsoba.nazwisko     THEN DisposeLink(ptrOsoba.nazwisko)
        Dispose(ptrOsoba)
    ENDIF    

ENDPROC

EXPORT PROC printOsoba(ptrOsoba:PTR TO osoba)

    IF ptrOsoba
        WriteF('Imie: \s\n',         ptrOsoba.imie)
        WriteF('Nazwisko: \s\n',     ptrOsoba.nazwisko)
        WriteF('Wiek: \d\n',         ptrOsoba.wiek)
    ENDIF

ENDPROC

Program wykorzystujący moduł 'osoba' jest jeszcze mniej skomplikowany. Definuje zmienną będąca wskaźnikiem na obiekt typu 'osoba', a następnie inicjuje go danymi. Ponieważ metoda initOsoba() może zwrócić wyjątek braku pamięci, program oczywiście go obsłuży.

/*   prog2.e      */

MODULE '*osoba'

PROC main() HANDLE 

    DEF ptrOsoba:PTR TO osoba
    DEF iWiekOsoby=0

    ptrOsoba := initOsoba()
    StrCopy( ptrOsoba.imie, 'Funky')
    StrCopy( ptrOsoba.nazwisko, 'Koval')
    ptrOsoba.wiek:=34
    
    iWiekOsoby:= ptrOsoba.wiek
 
    printOsoba(ptrOsoba)
    
    EXCEPT DO
        destroyOsoba(ptrOsoba)
        IF exception = ERR_OSOBA_MEM THEN WriteF('Brak wolnej pamieci\n')
        CleanUp(0)

ENDPROC

Po kompilacji modułu i programu, można uruchomić EDBG. Po dodaniu zmiennych do monitora, te dodane z klawiszem Shift zostana automatcznie wysłane do explorera, który wyświetlił strukturę obiektu 'osoba' i umożliwił podgląd wszystkich jego pól.

EDBG - v2

To już prawie wszystko, co oferuje EDBG. Prawie - bo przecież EDBG ma port ARexxa, standardowo nazwany 'EDBG', z całkiem bogatą listą rozkazów. A to oznacza, że można jego pracę dodatkowo zautomatyzować wedle własnych potrzeb lub sterować debuggerem z zewnątrz (np. z poziomu GoldEDa). Rozkazy dzielą się na dwie grupy - sterujące procesem debugowania i konfiguracyjne. Dodatkowo EDBG ma 3 zdefiniowane sloty na skrypty ARexx, które są dostepne z menu lub za pomocą skrótów klawiszowych.

Pełna lista rozkazów ARExxa przedtawia się nastepująco:
Rozkazy konfiguracyjne:
MEMORY exp - otwiera monitor pamięci pod wskazanym w argumencie 'exp' adresie.
VARIABLES left top width height - otwiera monitor zmiennych w określonym miejscu i rozmiarze
SRCWINDOW srcname left top width height - otwiera okno w określonym miejscu i rozmiarze ze wskazanym kodem źródłowym
NOABOUT - blokuje wyświetlanie okna 'About' przy uruchamianiu programu
REXX n script - przypisuje wskazany skrypt ARexxa do slotu n

EDBG przy starcie wczycztue plik .edbg-startup.rexx z katalogu bieżącego, dlatego jest to dobre miejsce, żeby ustawić najpotrzebniejsze ustawienia EDBG.

Rozkazy sterujące wykonywaniem programu:
STEPIN - wykonuje aktualną linię programu z wejściem do podprocedury STEPOVER - wykonujw aktualną linię programu RUN - wykonujw program do najbliższego punktu kontrolnego QUIT - wychodzi z debuggera EVAL exp - kalkulator wyrażeń AmigaE, wylicza wyrażenie exp BREAKPOINT linenum - ustawia punkt kontrolny na wskazanej linii programu MEMORYBREAKPOINT addr - ustawia punkt kontrolny na wskazanej komórce pamięci RAISE exception exceptioninfo - wywołuje okreslony wyjątek AmigaE WATCH varname varname ... - dodaje zmienne do monitora zmiennych NOREFRESH - blokuje odświeżanie monitorów po kolejnych krokach programu

Lista rozkazów ARexx niestety nie obejmuje wszystkich funkcjonalności jakie oferuje EDBG w swoim normalnym interaktywnym trybie użytkownika. Z dokumentacji programu wynika, że to był element na którym autor chciał się skupić w kolejnych wersjach, niestety nigdy do tego nie doszło. Jednak kto wie, źródła programu (oczywiście w E) są dostępne, i może ktoś kiedyś odświeży ten świetny program. Program już jest bardzo funkcjonalny i stabilny (ale uwaga - nie lubi jak się zmienia plik źródłowy podczas debuggowania, co przy amigowym multitaskingu jest banalnie proste i kończy się zamrożeniem systemu. Przydałby się jakis debug ;) ). Obecnie nie wyobrażam już sobie programowania w AmigaE bez jego pomocy. A wszystkim chętnym spróbowania swoich sił w tworzeniu amigowego oprogramowania polecam wypróbowanie środowiska jakie oferuje pakiet AmigaE. Tym bardziej że EDBG to nie nie ostatnie mało znane narzędzie z pakietu E, ale o tym może już innym razem.

 głosów: 7   tagi: Amiga E, programowanie, debugger
dodaj komentarz
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem