Session D-DBUG

Der neue Debugger von VFP 5.0

Norbert Abb
Wizards & Builders GmbH


Einleitung

Der Debugger ist wohl eine der auffälligsten Änderungen in der neuen Version. Er ist von der Arbeitsweise an den Debugger von Visual C++ angelehnt. Der Debugger hat zahlreiche Konfigurationsmöglichkeiten, einige neue Möglichkeiten in den Programmablauf einzugreifen und zahlreiche neue Funktionen zu bieten. Es gibt neue Fenster, die innerhalb des Debuggers zusammengefaßt sind. Der Debugger muß nicht mehr innerhalb von VFP laufen, er kann als separater Task ausgeführt werden.

Einige neue Kommandos im VFP Sprachumfang erleichtern und verbessern das Debuggen und Tracen.

Überblick

Neues Aussehen

Der Debugger besteht nicht mehr aus zwei Einträgen im Menü für das ‘Debug’ Fenster und das ‘Trace’ Fenster. Er ist jetzt ein Modul, das die Funktionen der beiden ‘alten’ Fenster enthält und noch viel mehr dazu.

Wenn der Debugger aus dem Menü gestartet wird, so präsentiert er sich in einem Hauptfenster in dem sich bis zu fünf weitere Fenster befinden können.

Es bestehen zahlreiche Möglichkeiten festzulegen wie der Debugger arbeiten soll, dazu gibt es eine eigene Seite in den ‘Optionen’ Einstellungen.

Mehr Möglichkeiten

Mit dem neuen Debugger sind Eingriffe in Variableninhalte und den Programmablauf wesentlich einfacher als bisher möglich.

Die Darstellung von Arrayinhalten und Objektstrukturen wurde verbessert. Einige Dinge für die bisher externe Tools benötigt wurden, sind in den Debugger integriert (z.B. gibt es eine Möglichkeit Events zu verfolgen).

Die Möglichkeiten Drag & Drop Funktionalität anzuwenden machen den Umgang mit dem Tool sehr effektiv.

Mehr Kontrolle

Die Kontrolle über den Programmablauf ist wurde erweitert. Es gibt erweiterte Möglichkeiten Haltepunkte (Breakpoints) zu setzen. Es ist sogar möglich den Programmablauf direkt zu beeinflussen und den Programmablauf an einem selbst gewählten Punkt fortzusetzen.

Neue Kommandos

Die neuen Funktionen können auch von Programmen aus angesteuert werden. Dafür wurden einige neue Kommandos in die Sprache aufgenommen. Mit diesen ist es möglich den Programmablauf zu verfolgen und zu protokollieren oder Meldungen in ein bestimmtes Fenster (bzw. eine bestimmte Datei) zu lenken.

Die Fenster

Die auffälligste Änderung beim ersten Aufruf des neuen Debuggers ist, daß fünf Fenster geöffnet werden: Trace, Watch, Call Stack, Debug Output und Locals.

Trace

Das Trace Fenster enthält wie auch der Editor und das Kommando Fenster, ‘Code Coloring’. Das bedeutet die verschiedenen Sprachelemente (Kommandos, Variablen, Kommentare, usw.) werden in unterschiedlichen Farben dargestellt. Welche Farben verwendet werden hängt von den Einstellungen in den Optionen ab.

Man kann im Trace Fenster sehr einfach und schnell feststellen welchen Wert eine Variable hat: Einfaches ‘Überfahren’ mit der Maus zeigt den Wert der Variable im ‘Tool Tip’ an.

Die Möglichkeiten den nächsten Befehl auszuführen einen Befehl zu ‘überspringen’ (Step over) bzw. aus der aktuellen Prozedur herauszuspringen sind erhalten geblieben. Neu hinzugekommen ist die Möglichkeit das Programm bis zur Zeile, in der der Cursor steht auszuführen. Dies kann das Setzen eines extra Haltepunktes ersparen.

Völlig neu ist die Möglichkeit den Programmablauf an einer selbst gewählten Stelle fortzusetzen. Damit sind Korrekturen im Programm noch zur Laufzeit möglich. Eine fehlerhafte Programmzeile kann ausgelassen (und im Kommandofenster durch eine ‘verbesserte Version’ ersetzt werden) und das Programm in der nächsten Zeile fortgesetzt werden.

Ein neues Feature, das zur Produktivität des Entwicklungsprozesses beiträgt ist die Möglichkeit, sobald ein Fehler festgestellt wurde in den entsprechenden Sourcecode zu springen. Im ‘Debug’ Menü des Debuggers gibt es dafür den Eintrag ‘Fix’. Durch Anwahl dieses Menüpunktes wird das laufende Programm gestoppt und in VFP die Entwicklungsumgebung gestartet. Es wird die Prozedur im Editor gezeigt, die gerade in Ausführung war. Dies gilt auch für Code in Methoden oder sogar in Klassen: Die Maske oder die Klasse wird im Designer geöffnet und der jeweilige Code Abschnitt zum Editieren geöffnet. Dies funktioniert auch bei Klassen, die in der Hierarchie weiter oben liegen und den Sourcecode an die Objekte vererbt haben.

Haltepunkte lassen sich durch einen Doppelklick in den Randbereich des Trace Fensters setzen. In einem erweiterten Breakpoint Dialog können Haltepunkte neu gesetzt, gelöscht oder deaktiviert werden. Vorsicht: Veränderungen an Haltepunkten können nicht vorgenommen werden der veränderte Haltepunkt muß mit ‘Add’ aufgenommen werden. Der original Haltepunkt kann dann gelöscht werden.

Haltepunkte können an Zusatzbedingungen gebunden werden. Das bedeutet, das Programm wird an dieser Stelle nur gestoppt, wenn die Zusatzbedingung erfüllt ist. Außerdem ist es möglich einen Haltepunkt ab einer bestimmten Anzahl von Durchläufen einer Programmzeile zu aktivieren.

Im Breakpoint Dialog erscheinen auch Haltepunkte, die im Watch Fenster gesetzt wurden. Haltepunkte können deaktiviert (‘disabled’) werden, so daß sie den Programmablauf nicht beeinflussen. Ihre Definition bleibt erhalten. Sie können damit jederzeit wieder ‘eingeschaltet’ werden.

Die Haltepunkte können mittels ‘File’; ‘Save Configuration’ als Debugger Einstellungen permanent in einer Datei abgespeichert und später aus der Datei wieder geladen werden. Die Haltepunkte (und beobachteten Variablen) müssen also bei einem Programmabbruch nicht verloren gehen.

Watch Fenster

Das Watch Fenster war bereits in ‘alten’ Versionen des Debugers enthalten. Auch bei diesem Fenster wurden einige Überarbeitungen vorgenommen. Das Zufügen von weiteren Ausdrücken ist nur noch eine Eingabezeile oder per Drag & Drop möglich. Es können wesentlich mehr Ausdrücke als in den bisherigen Versionen des Debuggers beobachtet werden.

Arrays und Objekte können in das Watch Fenster aufgenommen werden und in einer hierarchischen Darstellung beobachtet werden. Es muß nicht jedes zu beobachtende Arrayelement separat aufgenommen werden. Besonders wichtig ist diese Möglichkeit jedoch um die Eigenschaften von Objekten auszuwerten. Die gesamte Hierarchie eines Objektes kann angezeigt werden. Einzelne Eigenschaften können separat beobachtet werden (Drag & Drop).

Der Datentyp wird zusätzlich zu den Werten angezeigt.

Verändert sich der Wert einer Zeile im Watch Fenster, so wird dieser Wert rot dargestellt. Damit sind Veränderungen schnell zu erkennen, auch wenn das Fenster viele Zeilen enthält.

Im Watch Fenster ist es nun direkt möglich den Wert einer Variablen zu verändern (aber nicht den Datentyp).

Durch einen Doppelklick in den Randbereich können Haltepunkte gesetzt werden, die bei der Änderung des Wertes aktiviert werden.

Ein weiteres Feature, das sich in diesem Fenster als nützlich erweist, ist die Drag und Drop Funktionalität, die in den Debugger integriert wurde: Variablen oder Ausdrücke können aus einem anderen Fenster in das Watch Fenster ‘gezogen’ werden.

Locals Fenster

Das Locals Fenster ist ein neues Fenster. Es zeigt in einer ähnlichen Weise wie das Watch Fenster Werte von Variablen an. Für Arrays und Objektvariablen gibt es auch hier die Möglichkeit der hierarchischen Darstellung. Es gibt allerdings keine Möglichkeit den Inhalt des Fensters festzulegen oder Werte von Ausdrücken anzuzeigen. Es werden die jeweils für den aktuellen Programmabschnitt gültigen Variablen mit ihren Werten angezeigt.

Für Programmabschnitte (Prozeduren, Methoden), die im Programmstack enthalten sind können die jeweils gültigen Variablen angezeigt werden. Damit besteht die Möglichkeit Probleme, die durch den Gültigkeitsbereich von Variablen entstehen, zu erkennen.

Call Stack Fenster

Das Call Stack Fenster ist ebenfalls ein neues Fenster. Ein Teil der Funktionalität des Call Stack Fensters war in den früheren Versionen des Debuggers im Menü des Trace Fensters enthalten.

Das Call Stack Fenster zeigt den Aufrufabfolge bis zum aktuellen aktuellen Programmabschnitt an. Bei der Anwahl von Prozeduren in der Hierarchie wird jeweils im Trace Fenster angezeigt wo der Programmzeiger des dortigen Programmabschnittes steht.

Debug Output Fenster

Das Debug Output Fenster ist das dritte neue Fenster. Es zeigt Ausgaben die mit Debug Befehlen im Programm erzeugt wurden (DebugOut) und, wenn diese Option aktiviert wurde, die Ereignisse an.

Das Anzeigen von Ereignissen kann per Dialog eingestellt werden. Es kann ausgewählt werden, welche Ereignisse dargestellt werden sollen. Die ‘Mouse Move’ Events sollten normalerweise ausgeschaltet werden.

Die Ausgaben des Output Fensters können zusätzlich in eine Datei mitprotokolliert werden.

Optionen

Es gibt zahlreiche Möglichkeiten die Arbeitsweise des Debuggers zu beeinflussen. Insbesondere gibt es im Optionen Dialog eine eigene Seite, auf der Optionen zum Debugger bzw. den Debugger Fenstern eingestellt werden können.

Allgemeine Optionen

Als wichtigste globale Option kann bestimmt werden wie der Debugger arbeiten soll: Als eigener Task, oder innerhalb des FoxPro Fensters. Die Standard Einstellung ist, daß der Debugger als eigener Task läuft. Dies hat den Vorteil, daß alle Debug Fenster mit einem Task Switch erreicht werden können, oder daß der Debugger neben der eigentlichen Applikation angezeigt werden kann (bei ausreichend großem Monitor). Wenn der Debugger im FoxPro Task mitläuft, können die verschiedenen Fenster einzeln aktiviert werden, wie vom alten Debugger bekannt. Die Fenster des Debuggers können als Toolbars ‘gedockt’ werden. Damit ist es möglich Fenster ‘aus dem Weg zu räumen’.

Eine weitere allgemein einzustellende Option ist es, ob Timer Ereignisse angezeigt bzw. beim Tracen berücksichtigt werden. Dies kann ein sinnvolles Debuggen von Programmen, die Timer Ereignisse enthalten, erst ermöglichen (Superclass Utility!!).

Optionen für Fenster

Für die einzelnen Fenster des Debuggers können weitere Optionen eingestellt werden. Für jedes Fenster kann die Schriftart (und Größe) festgelegt werden.

Für das Call Stack Fenster können weitere Anzeigeoptionen gewählt werden.

Für das Debug Output Fenster kann festgelegt werden, daß und in welche Datei die Ausgaben zusätzlich erfolgen sollen.

Für das Trace Fenster können hier die Einstellungen vorgenommen werden, die in früheren Versionen des Trace Fensters per Menü zu wählen waren, wie die Einstellung ob Zeilennummern angezeigt werden sollen und wie groß die Verzögerung beim Ablauf im Trace Fenster zwischen zwei Zeilen sein soll (Throttle).

Event Tracking

Was bei VFP 3.0 sehr mühsam per Hand bzw. mit einem Tool erledigt werden mußte, ist nun im Debugger integriert: Die Möglichkeit die Ereignisse in einem Programm zu verfolgen.

Diese Option kann mit ‘Set Eventtrack On’ bzw. wie bereits beschrieben im Debug Output Fenster aktiviert werden. Es kann zusätzlich eingestellt werden, welche Ereignisse im der Ausgabe erscheinen sollen. ‘Mouse Move’ Events sollten, bis auf Ausnahmefälle, abgestellt werden.

Die Ausgabe kann zusätzlich als Protokoll in eine Datei geschrieben werden.

Das Eventtracking kann auch im Debugger über einen Dialog (aus dem Tools Menü) eingestellt werden

Dort können auch am einfachsten die Ereignisse eingestellt werden, die tatsächlich protokolliert werden sollen (kein MouseMove z. B.). Dies kann auch programmgesteuert per ‘Set Eventlist’ geschehen.

Neue Kommandos

Im Zusammenhang mit den neuen Möglichkeiten für den Debugger wurde auch der Befehlsumfang der Sprache erweitert:

Debug

Der Befehl ‘Debug’ startet den Debugger, aber er aktiviert ihn nicht. Um den Debugger direkt zu aktivieren ist es besser wie bisher den Befehl ‘Set Step on’ zu verwenden. Wenn der Debugger vor dem Start des Programms schon aktiv war, dann aktiviert ‘Debug’ den Debugger.

DebugOut

Mit dem DebugOut Kommando ist es möglich beliebige Messages im Debug Output Fenster (bzw. in der Protokoll Datei) zu erzeugen. Hinter dem Befehl DebugOut kann dann ein String stehen oder eine Funktion, die einen String zurückliefert (z.B. Program()).

Die Ausgaben erscheinen nur im Debug Output und nirgendwo sonst. Das bedeutet ‘vergessene’ Debug Meldungen erscheinen nicht in der ausgelieferten Version des Programms und (ver)stören Kunden.

Im Kommando DebugOut können beliebige Ausdrücke verwendet werden. Dadurch ist es möglich auch eigene Routinen einzubinden. Diese Funktionen werden auch abgearbeitet wenn das Programm ohne Debugger läuft.

Mit ‘Set DebugOut to <xxx>’ können die Ausgaben zusätzlich in eine Protokolldatei gelenkt werden. Damit besteht die Möglichkeit auch bei Kunden vor Ort Protokolle von kritischen Programmteilen zu erstellen.

Set DebugOut beeinflußt auch die Ausgaben des Event Trackings (Ereignis Protokollierung) und die Meldungen des Assert Kommandos (siehe unten).

Coverage

Mit dem ‘Coverage’ Befehl können besonders kritische Programmteile exakt unter die Lupe genommen werden. Dies kann sowohl den Programmablauf, als auch die Performance betreffen.

Mit ‘Set Coverage to <xxx>’ werden alle ausgeführten Programmzeilen in die Datei <xxx> geschrieben (mit ‘additive’ kann eine Datei erweitert werden statt sie zu überschreiben).

In der Protokoll Datei werden die Zeit die für die Programmzeile benötigt wurde, die Klasse aus der die Programmzeile stammt, die Prozedur, die Zeilennummer, sowie der Dateiname aus dem der Befehl stammt, protokolliert. Das Protokoll kann auch über das Menü (oder per Toolbar) im Debugger aktiviert werden.

Diese Datei wird, wenn zu große Programmstücke damit protokolliert werden, recht lang. Um die Daten genauer untersuchen zu können kann diese Datei jedoch einfach in eine Tabelle eingelesen werden. Sie wird in durch Kommas getrenntem Format geschrieben. Nach dem Laden der Daten in eine FoxPro Tabelle können Auswertungen gemacht werden.

Für die Auswertungsapplikation gibt es sogar eine eigene Systemvariable (ähnlich wie ‘_browser’): ‘_coverage’. Der Inhalt dieser Variable ist der Name des Programms, das ausgeführt wird, wenn der ‘Generate’ Button im Dialog für das ‘Coverage Logging’ angeklickt wird. Hier gibt es noch einige Möglichkeiten für Erweiterungen. Die in der Hilfe versprochene Applikation ‘cover.app’ ist jedoch nicht auf der VFP CD enthalten (sorry). Auf der Begleitdiskette zu dieser Session gibt es eine Routine (Coverage.PRG), die die Daten aus einer ‘Coverage Datei’ in eine Tabelle schreibt.

Die Werte, die mit diesem Tool erzeugt werden, sind was die Ausführungszeiten betrifft nicht vollständig korrekt und wiederholbar, da eventuelle Systemereignisse in Windows mit in die Ausführungszeiten eingehen. Dies kann zu Verfälschungen führen. Messungen sollten daher mehrmals durchgeführt werden um repräsentative Ergebnisse zu erhalten.

Assert

Mit dem ‘Assert’ Befehl kann die Einhaltung von bestimmten Bedingungen an bestimmten Programmpunkten gewährleistet werden (to assert: zusichern).

Der Befehl ‘Assert’ hat folgende Syntax: ‘Assert <lExpression> [Message cMessageText]’. Der logische Ausdruck wird ausgewertet und wenn er als falsch (.f.) ausgewertet wird, wird eine Messagebox angezeigt mit dem Text, der in cMessageText steht. In der Box können dann die Optionen Debug, Cancel, Ignore und Ignore all. Man kann also entweder in den Debugger springen um nachzusehen was genau passiert ist, oder das Programm abbrechen (weil man schon weiß was los ist), oder aber die Nachricht ignorieren. Der Punkt ‘Ignore all’ entspricht dem Kommando ‘Set Assert off’.

Der Set Befehl ist die kleine Gemeinheit beim ersten Test dieses Befehls: Die Standardeinstellung für Assert ist nämlich ‘Set Assert Off’ was bedeutet: die Assert Befehle sollen ignoriert werden. In der ausgelieferten Version eines Programms sollte diese Einstellung so stehen, da dort kein Debugger vorhanden ist (normalerweise). Während der Entwicklung von Programmen kann dieser Befehl aber sehr nützlich sein, insbesondere wenn mehrere Leute zusammen arbeiten oder ein Programmteil oder eine Klasse eines anderen Programmierers eingesetzt werden soll. Wenn dort die entsprechenden Assert Befehle verwendet werden, können Fehler, die eventuell erst in späteren Teilen der Routine auftauchen, oder zu unerklärlichen Fehlern führen früh abgefangen werden (sogar mit passender Meldung).

Die Meldungen des Assert Befehls (cMessageText) erscheinen auch im Debug Output Fenster.