Ein hartnäckiger Bug verbirgt sich in einem Programm und behindert die Arbeit oder macht sie gar unmöglich: ein Fall für einen Debugger. War die Nutzung des Debuggers in den ersten Java-Versionen noch ein mühsames Geschäft, so gibt es jetzt gute und vor allem frei verfügbare Lösungen.
Der Nutzen von Debuggern ist jedoch umstritten. Linus Torvalds beispielsweise weigert sich beharrlich, einen Kernel-Debugger in den Mainline-Kernel-Tree aufzunehmen. Sein Argument, dass man dem Code ansehen muss, ob er richtig ist, und daher ein Debugger nur zu schlechtem Programmieren verleitet, hat durchaus etwas für sich. Allerdings lässt sich die Welt nur selten in Schwarzweiß malen. Manchmal hilft ein Debugger schon dabei, das berühmte Brett vor dem Kopf zu erkennen.
Speziell unter Java spricht jedoch auf den ersten Blick noch ein anderer Effekt gegen den Einsatz eines Debuggers: Java-Methoden sollten grundsätzlich kurz und übersichtlich sein. Das Eigentliche, was dann im Programm passiert, ist oftmals verteilt auf unterschiedliche Klassen, die über Mechanismen wie beispielsweise Vererbung oder Aggregation miteinander verknüpft sind. Selbst wenn jede Methode für sich Bug-frei ist, kann die Logik des Zusammenspiels irgendwo trotzdem einen Haken haben. In solchen Fällen ist ein Debugger nur von beschränktem Nutzen.
Weitere Argumente gegen den Einsatz eines Debuggers liefern die schönen Stack-Traces, die von nicht abgefangenen Exceptions (sehr oft »NullPointer«-Exceptions) ausgelöst werden. Solch ein Trace führt meist direkt zur fehlerhaften Programmzeile. Und als letztes Argument sei die Sprache selbst angeführt: Java ist nicht so mächtig wie C++, dafür aber auch weniger fehleranfällig - ohne Pointer-Arithmetik keine Fehler mit den Pointern.
Bereits als Fazit gleich am Anfang dieses Artikels steht also die - sicherlich persönlich gefärbte - Meinung, dass ein Debugger zwar in die Software-Kategorie "Nice to have" fällt, aber erfahrungsgemäß für einen erfolgreichen Software-Entwicklungsprozess unter Java keineswegs zwingend erforderlich ist.
Heimspiele
Wer mit einer Java-IDE entwickelt, für den ist das Debugger-Problem sowieso schon gelöst. Alle verbreiteten IDEs wie Borlands JBuilder, IBMs Visual Age oder SUNs Forte bringen einen Debugger mit. Der Einsatz dieser Werkzeuge soll hier kein Thema sein, obwohl es Entwickler gibt, die nur zum Zweck des Debuggens den Code in solche IDEs laden. Dieser Artikel beschäftigt sich mit Stand-alone-Debuggern, die zusammen mit dem eigenen Lieblingseditor verwendet werden können. Dazu wird zuerst die Architektur erläutert. Anschließend werfen wir einen Blick auf die Referenzimplementation, auf JSWAT und auf JDE.
Debugging unter Java
Java wäre nicht Java, wenn es nicht einheitliche Interfaces für alles, also auch fürs Debuggen gäbe. Es ist eine der großen Stärken der Sprache, durch Vorgabe von Schnittstellen es einerseits den Herstellern zu überlassen, wie sie die Funktionalitäten implementieren, andererseits aber die Interoperabilität zu sichern. Die Java Plattform Debugging Architecture, kurz JPDA genannt (siehe[1]), besteht aus drei Komponenten:
- dem Java Virtual Machine Debug Interface (JVMDI),
- dem Java Debug Wire Protocol (JDWP)
- und dem Java Debug Interface (JDI).
Das JVMDI beschreibt die Services, die eine virtuelle Maschine (VM) bieten muss. Dazu zählen neben der Abfrage des Stack-Frames Aktionen wie das Setzen von Breakpoints oder die Notifikation über Events, etwa wenn ein Breakpoint erreicht wird. Das JVMDI ist durch ein so genanntes Back-End implementiert. Das ist nativer Code, da hier Java das zu debuggende Programm beeinflussen könnte.
Das Back-End kommuniziert mit dem User-Interface, auch Front-End genannt, über das JDWP. Dabei ist nicht der Transportmechanismus selbst festgelegt, sondern nur die Bedeutung der ausgetauschten Informationen. Typischerweise werden aber Sockets verwendet, was auch das Debuggen einer VM auf einer Server-Maschine erlaubt. Ein VM-Hersteller muss also nicht zwingend das JVMDI implementieren, sondern sollte nur sicherstellen, dass das Back-End das JDWP beherrscht. Das JDWP ist paketorientiert und verbindungslos. Analog zum Dialog zwischen Browser und Webserver werden also Kommando- und Antwort-Pakete ausgetauscht.
Auf der Seite des Front-Ends standardisiert das letzte der drei Interfaces, das JDI, den Zugriff auf die Informationen des Transportprotokolls. Dieses Interface ist streng genommen auch nicht notwendig, da ein direkter Zugriff auf das Transportprotokoll möglich wäre, selbst aus einer anderen Programmiersprache heraus. Dieser Fall ist in Abbildung 1 auf der rechten Seite dargestellt.
Abbildung 1: Die Architektur des Java Plattform Debugging.