Hilfeoption des Debuggers
In der Februar-Ausgabe wurde ĂŒber die neueste Version von Pro-spero-Fortran berichtet, die einerseits ĂŒber ihre Workbench besser in GEM eingebunden ist, andererseits aber auch durch eine umfangreiche Unterprogrammbibliothek dem Programmierer einen erweiterten Zugriff auf die leistungsfĂ€higen GEM-Routinen ermöglicht.
In diesem Artikel sollen einige Aspekte dieses Compilers im Detail betrachtet werden. Dabei haben wir vor allem diejenigen Programmierer vor Augen, die zwar schon Erfahrung mit Fortran haben, sich bisher aber noch nicht weiter mit GEM beschÀftigt haben und auch mit dem neuen Prospero-Fortran noch keinen intensiven Kontakt hatten.
ZunĂ€chst werden wir uns mit dem Debugger beschĂ€ftigen, anschlieĂend wird ein wenig gefensterlt, wobei uns einige GEM-Routinen assistieren werden, danach kommen einige Vergleichsmessungen mit der alten Fortran-Version und mit Fortran-Versionen auf anderen Rechnern. Zum SchluĂ geben wir noch Hinweise auf weitere, bisher gefundene Fehler und Unstimmigkeiten.
Fehlersuche mit PROBE
Ein Debugger ist ein Hilfsmittel, im allgemeinen ein Zusatzprogramm, welches bei der Fehlersuche eingesetzt wird. Normalerweise werden allenfalls Faufzeitfehler mit Fehlerart und Programmzeile gemeldet. Dies ist oft aber nur die Wirkung von Fehlem, die Ursache liegt dann meist im Dunkeln. Logische Fehler findet man auf diese Weise ohnehin nicht. Es kommt daher darauf an, den Ablauf eines Programms genau zu beobachten und an manchen Stellen den Wert signifikanter Variablen zu prĂŒfen. Hierzu quĂ€lt man sich oft mit zusĂ€tzlich ins Programm aufgenommenen Druck-anwei-sungen: âHier ist Unterprogramm ABCâ, âDie Variable XYZ hat den Wert... â und Ă€hnliches. Diese Methode ist zwar jedem gestandenen Fortran-Programmierer wohlvertraut, nichtsdestotrotz aber sehr umstĂ€ndlich: Die zunĂ€chst als fehlerhaft verdĂ€chtigten Stellen sind natĂŒrlich völlig in Ordnung, man muĂ ganz woanders suchen, also neue Druckbefehle ârein, nochmal ĂŒbersetzen, binden, wieder falsch, ... Wir kennen das!
Mit einem Debugger hingegen ist man viel besser dran: Das Originalprogramm wird unter der Kontrolle dieses Hilfsprogramms ausgefĂŒhrt, man kann sich zur Faufzeit ĂŒberlegen, was man ansehen möchte, wo man weitermachen will und was einem sonst noch so einfĂ€llt. Debugger sind natĂŒrlich nicht genormt, und so ist ihre Bedienung und Fei-stungsfĂ€higkeit recht unterschiedlich, wenn man ĂŒberhaupt einen hat. Selbst auf GroĂrechnern ist dieses wichtige Hilfsmittel nicht immer selbstverstĂ€ndlich.
Der Einstieg
Soll eine Programmeinheit mit PROBE untersucht werden, so ist sie mit der Option âNâ zu ĂŒbersetzen, alle anderen Programmteile und Programme sind fĂŒr den Debugger transparent. Beim Ăbersetzen mit âNâ werden die Hilfsdateien der Typen â.NAMâ und â.SYMâ erzeugt, die PROBE bei seiner Arbeit benötigt. Verwendet man zusĂ€tzlich noch die âFâ-Option, dann kann PROBE die dadurch erzeugte Auflistung des Quellprogramms in der '.PRNâ-Datei wĂ€hrend einer Sitzung ebenfalls verwenden. PROBE kann direkt vom Desktop gestartet oder aus der Workbench ĂŒber âDebug Programâ oder die Tastenkombination (Alternate+P) aufgerufen werden. Im folgenden werden wir den letzteren Fall betrachten.
Nach (Alternate+P) darf man zunĂ€chst anhand der ĂŒblichen Auswahlbox das gewĂŒnschte Programm aussuchen. Leider gibt es keine direkte Möglichkeit, das gerade entwickelte Programm einzustellen. Oder ist die nur nicht gut genug dokumentiert? Die beim Aufruf von PROBE aus der Workbench verwendeten Pfadnamen sind etwas verwirrend: PROBE.PRG wird im Pfad fĂŒr âCompiler overlaysâ gesucht; fĂŒr das zu testende Programm wird in der Auswahlbox der Pfad fĂŒr âuser filesâ voreingestellt; die Dateien vom Typ â.PRNâ und â.NAMâ werden im Pfad fĂŒr "librariesâ gesucht, wo auch die Protokolldatei PROBE.LOG abgelegt und die Datei PROBE.HLP mit den Hilfstexten vermutet wird. Wird in der laufenden Sitzung fĂŒr eine nicht gefundene Datei einmal ein anderer Pfadname eingegeben, so gilt der dann aber auch fĂŒr die nĂ€chste gesuchte Datei.
Ein Beispiel
NatĂŒrlich können, dĂŒrfen und wollen wir hier nicht die LektĂŒre des Handbuchs ersparen. Wir geben aber einige Tips und Hinweise, die den Einstieg erleichtern und einen kleinen Eindruck von der LeistungsfĂ€higkeit dieses Debuggers geben. Ganz instruktiv ist hierbei der Ausdruck in Bild 1. Dies ist das Protokoll einer Sitzung, die mit ECHO in die Datei PROBE.LOG protokolliert und dann mit einem Editor nachbearbeitet wurde: Alle Benutzereingaben sind unterstrichen. Das Protokoll enthĂ€lt stets nur die von PROBE erzeugte, nicht die vom getesteten Programm erzeugte Ausgabe und auch nicht die vom Benutzer eingegebenen Pfadnamen. Diese wurden in Bild 1 nachtrĂ€glich noch eingesetzt. Die vom Benutzer eingegebenen PROBE-Be-fehle stehen dagegen auch wieder im Protokoll.
Bild 1 vermittelt zunĂ€chst einen treffenden Eindruck von dem Chaos mit den Pfadnamen. Positiv fĂ€llt dagegen der Umfang der HELP-Funktion auf, die hier nur andeutungsweise demonstriert wurde. Zu jedem Punkt kann noch ein ausfĂŒhrlicher Hilfstext abgerufen werden. So etwas ist vorbildlich! Mit DISPLAY wird der Inhalt von Variablen und Feldern angezeigt. NatĂŒrlich kann man genau angeben, was man sehen möchte; ohne Parameter wirkt der Befehl auf alle GröĂen der aktuellen Programmeinheit.
Goodies
Nach Eingabe von PROFILE wird gezĂ€hlt, wie oft jede Programmzeile ausgefĂŒhrt wird. Gerade bei komplexen Programmen gewinnt man so wichtige Hinweise fĂŒr die Optimierung. Mit LIST wird diese Liste ausgegeben, zuvor sollte natĂŒrlich das Mitschreiben der Ausgabe durch ECHO in die Datei PROBE.LOG eingeschaltet werden. Fragen Sie jetzt aber bitte nicht, in welchem Ordner diese Datei anschlieĂend gesucht werden muĂ! Ein weiteres ECHO schaltet die Ausgabe ĂŒbrigens wieder ab. Die so erzeugte Liste fĂŒr das Beispiel aus Bild 1 finden Sie in Listing 1.
Ganz wichtig ist auch der Befehl STEP. Ohne Parameter wird genau eine Anweisung ausgefĂŒhrt und dann die nĂ€chste zur AusfĂŒhrung anstehende Zeile angezeigt. Fehlt die â.PRN'-Datei, dann wird nur die Zeilennummer gemeldet. Und nun raten Sie mal, was passiert, wenn man eine Zahl als Parameter angibt!
Mit âBREAK 8â wird das Programm vor AusfĂŒhrung der Anweisung in der Quellzeile Nummer 8 unterbrochen und PROBE meldet sich zurĂŒck. Warum hier gerade die Zeile Nummer 8 angegeben wird? Schauen Sie sich einmal die Auflistung des Quellprogramms in Listing 1 an! Irgendwann muĂ man ja, bevor sich alles in Wohlgefallen auflöst, die mit PROFILE erzeugte Statistik ausgeben. Der Befehl WATCH ist wesentlich leistungsfĂ€higer, als man zunĂ€chst glaubt. Man kann beispielsweise, wie hier geschehen, die VerĂ€nderung von Variablen oder Feldelementen ĂŒberwachen. Dies kann auch noch von Bedingungen abhĂ€ngen, etwa âWATCH C<=5.0\ Stets wird jedoch nicht einfach nur auf die Verwendung des angegebenen Namens in der aktuellen Programmeinheit geachtet; vielmehr wird jeder Zugriff auf den zugehörigen Speicherplatz auch aus aufgerufenen Unterprogrammen gemeldet, wo die untersuchte GröĂe natĂŒrlich ganz anders heiĂen kann.
Bild 1: Beispiel einer Sitzung mit PROBE. Alle Benutzereingaben sind unterstrichen. Deutlich zu sehen ist das Problem mit den Pfaden.
KĂŒr verpatzt
WĂ€hrend einer Sitzung mit PROBE kann durch den Befehl X ein beliebiges anderes Programm gestartet werden. LĂ€ngere PROBEn könnte man dadurch wesentlich erleichtern: Aus dem Desktop wird zunĂ€chst PROBE aufgerufen und dann erst die Workbench mittels des Kommandos âX F_BENCH\ WĂ€hrend der gesamten Sitzung lĂ€uft dann jedes mit der N-Option ĂŒbersetzte Programm automatisch unter PROBE, ohne daĂ der Debugger jedesmal erneut auf gerufen und geladen werden mĂŒĂte. Dies klingt nun zwar alles sehr schön, zuvor sollten die Programmentwickler allerdings ihren Debugger selbst einmal debuggen. Nach dem Aufruf von PROBE und Eingabe des Befehls âX F_BENCHâ - der Cursor ist selbstverstĂ€ndlich nicht zu sehen -wird zunĂ€chst einmal der Quellcode eines mysteriösen Programms namens P03MG angemahnt. Dieses Ansinnen kann man nun mittels Return-Taste zurĂŒckweisen und die Chose anschlieĂend endlich durch den Befehl GO zum Laufen bringen. Der daraufhin sichtbare Anfangsbildschirm der Workbench sieht allerdings schon recht merkwĂŒrdig aus; auch scheinen sich einige Zeiger zu verhaspeln, so daĂ bei mehrfachem Start eines Programms mit (Alternate+R) dieses an unterschiedlichen Stellen begonnen wird. Wenn aber in Zukunft einmal alles so lĂ€uft, wie im Handbuch beschrieben, dann wĂ€re dies gewiĂ ein enormer Komfort.
Die fĂŒr GEM charakteristischen Betriebssystem-Routinen gliedern sich in zwei Hauptgruppen: AES (Application Environment Services) und VDI (Virtual Device Interface). Die VDI-Routinen dienen hauptsĂ€chlich der graphischen Ausgabe, wĂ€hrend die AES-Routinen vor allem die WIMP-Benutzerschnittstelle verwalten. WIMP steht hier fĂŒr âWindows, icons, mice, pull-down menusâ. Am interessantesten ist sicher der Einstieg in AES, hier kann nun jeder Fortran-Programmierer seinen Programmen das GEM-typische Aussehen verleihen.
Ohne RSC-Datei
Bisher wurde hierzu immer eine besondere RSC-Datei benötigt, die alle Angaben zu Menues, deren Funktionen und sonstigen Ausgaben enthĂ€lt und mit einem speziellen Editor erstellt werden muĂ. Dies kann man zwar mit den Fortran-Program-men auch tun, jetzt gibt es jedoch noch zusĂ€tzlich die Möglichkeit, eine MenĂŒleiste durch die Funktion menu_create dynamisch innerhalb eines Programms selbst zu erstellen. Unser Beispielprogramm in Listing 2 macht davon Gebrauch.
Was sind Bindings?
Die AES-Routinen des Betriebssystems werden aus Maschinenprogrammen, die ĂŒblicherweise in Assembler geschrieben sind, ĂŒber einen Software-Interrupt aufgerufen.
Dies ist in Hochsprachen nicht ohne weiteres programmierbar, man benötigt hier einen Satz Unterprogramme, die die entsprechenden AES- (oder auch VDI-) Funktionen aufrufen und dabei auch, falls nötig, das Umsetzen von Parametern besorgen. Diese Unterprogramme sind die sogenannten âBindingsâ, die man etwa bei jedem C-Compiler fĂŒr den ATARI mitbekommt. In Prospero-Fortran sind, ab Version 2, solche Bindings nun ebenfalls, wohldokumentiert, enthalten.
Einarbeiten mit Handbuch
FĂŒr die AES- und VDI-Bindings von Prospero wird jeweils ein Handbuch mit etwa 250 Seiten Umfang mitgeliefert. Die QualitĂ€t dieser HandbĂŒcher ist so gut, daĂ man sich damit auch ohne zusĂ€tzliche Literatur in die GEM-Programmierung einarbeiten kann. Zumindest die ersten Gehversuche werden gut unterstĂŒtzt, wobei allerdings auch die mitgelieferten Beispielprogramme wertvolle Hilfestellung leisten.
Zu den Bindings
Die originalen AES-Routinen geben meist einen Parameter zurĂŒck, der oft aber nicht benötigt wird. Viele AES-Funktionen werden daher in Fortran in SUBROUTINE-Aufrufe umgesetzt; wird der Ergebnisparameter doch einmal benötigt, so kann man ihn nachtrĂ€glich noch mittels der Funktion AESret abholen. Datenaustausch mit den AES-Routinen des Betriebssystems geschieht ĂŒber Speicherbereiche, deren Startadressen der jeweiligen Funktion mitgeteilt werden. In den Fortran-Bindings wird dies ĂŒber benannte COMMON-Bereiche realisiert, deren Namen alle mit den vier Zeichen âAES_â beginnen. Ăhnlich ist es bei den VDI-Bindings. In den Bindings wird bei Bedarf noch eine Umwandlung von INTEGER4-Konstanten (aus dem rufenden Programm) in INTEGER2-Werte (zum Betriebssystem) vorgenommen. Einige fĂŒr den Aufruf der GEM-Bin-dings nĂŒtzliche Konstanten werden durch PARAMETER- und Typ-Anweisungen in der mitgelieferten Datei GEMCONST.FOR vereinbart, die der Programmierer mittels IN-CLUDE-An Weisung ins Quellprogramm integrieren kann. In unserem Beispielprogramm in Listing 3 wird dies zur ErlĂ€uterung auch getan, obwohl lediglich die Konstante MN_SELECTED benötigt wird.
Die Beispielprogramme
Zur Demonstration der Anwendung einiger AES-Funktionen dienen die Programme PROG1, GEM1 und TOS1 aus den Listings 3, 4 und 5. In PROG1 wird ein MenĂŒ aufgebaut, mit dem im wesentlichen die Programme GEM1 und TOS1 hinzugeladen und ausgefĂŒhrt werden können. Dies sind vollkommen selbstĂ€ndige Programme, keine Unterprogramme! In diesem Sinne ist PROG1 eine sogenannte âShell'â. Das Programm erklĂ€rt sich durch die im Quellcode eingebrachten Kommentare weitgehend selbst. GrundsĂ€tzlich wird zu Beginn jedes GEM-Programms mit appl_init Arbeitsspeicher reserviert, der am Ende mit appl_exit wieder freigegeben werden muĂ. GEM1 zeigt einen kleinen Text unter GEM an und arbeitet mit PROG1 gut zusammen. TOS1 dagegen ist ein reines TOS-Programm und zerstört den Bildschirmaufbau des rufenden GEM-Programms. Um den Effekt vorzufĂŒhren, wird dies in PROG1 nicht verhindert. Die Anzahl der Formalparameter der Routine menu_create weicht in den Beispielprogrammen von Prospero vom Handbuch ab; da es mit der Verdoppelung des ersten Parameters (bisher!) keine Fehlfunktionen gab, haben wir uns hier an die Beispielprogramme gehalten.
Bleibt also noch, unseren Lesern viel SpaĂ beim Fensterin in Fortran zu wĂŒnschen. In einer der nĂ€chsten Ausgaben werden wir ein gröĂeres Programm vorstellen, bei dem die Möglichkeiten des GEM ausgiebig verwendet und erlĂ€utert werden.
Dr. V. Kurz
>>LIST 1..200
1 * 1 A « 10.
2 * 1 B = 5.
3 * 1 CALL ADD (A, B, C)
4 * 1 PRINT *, C
5 * 1 B = 6.5
6 * 1 CALL ADD (A, B, C)
7 * 1 PRINT *, C
8 * STOP
9 * END
10 + *
11 SUBROUTINE ADD (XI, X2, X3)
12 * 2 X3 = XI + X2
13 * 2 RETURN
14 * 2 END
*Listing 1: Ausgabe der mit dem Beispiel aus Bild 1 erzeugten Statistik. Das Hauptprogramm wurde einmal, das Unterprogramm zweimal ausgefĂŒhrt. Alle ausfĂŒhrbaren Anweisungen werden mit ' ' gekennzeichnet.
* PROG1.FOR 02-02-88 VK
* Demonstration einiger GEM-Funktionen...
* - Menueleiste einrichten und anzeigen
* - Tochterprogramme starten
* - Textbox anzeigen
*
PROGRAM PROG1
*
* Verwendete Funktionen des Bindings...
INTEGER*4
- menu_create,
!erzeugt Menueleiste ohne RSC-Datei
- menu_title, !Menuepunkt anfĂŒgen
- menu_item, !Funktion an Menuepunkt anfĂŒgen
- AESret, !Parameter holen
- form_alert !zeichnet Alarm-(alert-)Box
*
* Eigene Variable und Felder
INTEGER*4
- MENUE_BAUM, !Adresse des Menue-Baums
- MENUE1, MENUE2, !Titel der Menues 1 und 2
- FKT1, (Funktion von Menue 1
- FKT2, FKT3, FKT4, (Funktionen von Menue 2
- GEW_MENUE, GEW_FKT, !gewa"hltes Menue und Funktion
- DUMMY !Hilfsvariable
INTEGER*2 PUFFER(0:7) !Ergebnisse der Menuewahl
CHARACTER*4 TEXT !Ausgabe im Alarmfenster
*
INCLUDE 'gemconst'
*
* Hier werden die Menues aufgebaut...
*
* {Immer zuerst GEM-Anwendung initialisieren}
CALL appl_init
IF (AESret() .lt. 0) GOTO 999 !Das ging schief!
*
* {Menuebaum mit erstem Menue erstellen}
MENUE_BAUM = menu_create ( 15, 15,
' Wie immer '//char(0) )
*
* {Weitere Menues anfu"gen)
MENUE1 = menu_title ( MENUE__BAUM,
' Ausgang '//char(0) )
MENUE2 = menu_title ( MENUE_BAUM,
' Programme '//char(0) )
*
* {Funktionen fu"r Menues 1 und 2}
FKT1 = menu_item ( MENUE_BAUM, MENUEI,
' Ende'//char(0) )
FKT2 = menu_item( MENUE_BAUM, MENUE2,
' GEM-Meldung '//char(0) )
FKT3 = menu_item ( MENUE_BAUM, MENUE2,
'-----' / /char (0))
FKT4 = menu_item( MENUE_BAUM, MENUE2,
' TOS-Meldung '//char(0) )
*
* {Striche schattieren, damit Auswahl verhindern}
CALL menu_ienable( MENUE_BAUM, FKT3, .FALSE. )
*
* {Menueleiste anzeigen}
CALL menu_bar( MENUE_BAUM, .TRUE. )
*
* Jetzt wird gearbeitet...
*
10 CALL evnt_mesag( PUFFER )
!Menue anbieten, auf Auswahl warten
GEW_MENUE = PUFFER (3) !Gewa"hltes Menue und...
GEW_FKT = PUFFER(4) !...daraus gewa"hlte Funktion
IF (PUFFER(0) .EQ. MN_SELECTED) THEN
IF (GEW_MENUE .EQ. 3) THEN
!Menue 3 ist immer das Desk-Menue
DUMMY = form_alert ( 1, ' [1] [ '//
- (C) Copyright: |'//
- ' Gibt es fu"r ein so \'//
- 'einfaches Programm nicht!]'//
- '[Na klar!]'//char (0) )
ELSEIF (GEW_FKT .EQ. FKT1) THEN GOTO 999
ELSEIF (GEW_FKT .EQ. FKT2) THEN
DUMMY = form_alert ( 1, '[1] ['//
- 'Gleich wird sich ein|'//
- 'GEM-Programm|'//
- 'bei Ihnen melden.]'//
- '[Na los!)'//char(0) )
CALL EXECPG( '\GEM1.PRG', DUMMY )
!Tochterprogramm nachladen
IF (DUMMY .NE. 0) THEN
WRITE (TEXT,' (I4) ' ) DUMMY
DUMMY = form_alert ( 1, '[3]['//
- 'Das GEM-Programm konnte\'//
- 'nicht gestartet werden.|'//
- 'Fehlercode: '//TEXT//']'//
- '[Ich habe gepfuscht]'//char(0) )
ENDIF
ELSEIF (GEW_FKT .EQ. FKT4) THEN
DUMMY = form_alert ( 1, '(1) t'//
- 'Gleich wird ein|'//
- 'TOS-Programm|' / /
- 'eine Ausgabe abliefern]'//
- '[Her damit!]'//char(0) )
CALL EXECPG( '\TOS1.PRG', DUMMY )
!Tochterprogramm nachladen
IF (DUMMY .NE. 0) THEN
WRITE (TEXT,'(I4)') DUMMY
DUMMY = form_alert ( 1, '[3]['//
- 'Das TOS-Programm konnte\'//
- 'nicht gestartet werden.\'//
- 'Fehlercode: '//TEXT//']'//
- '[Ich habe gepfuscht]'//char(0) )
ELSE
CALL menu_bar( MENUE_BAUM, .TRUE. )
!Bildschirm aufra"umen
ENDIF
ENDIF
*
* {'Reverse Video' fu"r 'gewa"hltes Menue aufheben }
CALL menu_tnormal ( MENUE_BAUM, GEW_MENUE,
.TRUE. )
ENDIF
GOTO 10
999 CALL appl_exit
END
Listing 2: Das Oberprogramm PROG1. Die GEM-Funktionen sind in den Kommentaren erlĂ€utert, tu" = U, "ĂŒ" ist unter dem von Prospero mitgelieferten Editor nicht verfĂŒgbar.)
PROGRAM GEM1
CALL appl_init
IF (AESret() .lt. 0) GOTO 999
!Das ging schief!
DUMMY = form_alert ( 1, ' [1] ['//
- 'Hier ist das|'// 'GEM-Programm|â//
- '[Weiss ich!)'//char(0) )
999 CALL appl_exit
END
Listing 3: GEM1, ein ordentlicher GEM-Benutzer
PROGRAM TOS1
CHARACTER DUMMY
PRINT*,
'Hallo, hier ist TOS1 !
PRINT*,
'Leider ist jetzt das Desktop nicht mehr scho"n.'
PRINT*,
'Weiter geht es mit <RETURN> '
READ (*, ' (A) ') DUMMY
STOP
END
Listing 4: TOS1, ohne alle GEM-Zutaten