SEP 2006 Debugger für Verteilte Komponentensysteme auf Basis

Werbung
SEP 2006
Thema:
Debugger für Verteilte Komponentensysteme
auf Basis der CTL
Martin Krosche
Institut für Wissenschaftliches Rechnen
Technische Universität Braunschweig
Hans-Sommer-Straße 65
D-38106 Braunschweig
Version 1.1 (2006-04-11)
Copyright © by Institut für Wissenschaftliches Rechnen, Technische Universität Braunschweig
This work is subject to copyright. All rights are reserved, whether the whole or part of the material is concerned,
specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on
microfilm or in any other way, and storage in data banks. Duplication of this publication or parts thereof is permitted in
connection with reviews or scholarly analysis. Permission for use must always be obtained from the copyright holder.
Alle Rechte vorbehalten, auch das des auszugsweisen Nachdrucks, der auszugsweisen oder vollständigen Wiedergabe
(Photographie, Mikroskopie), der Speicherung in Datenverarbeitungsanlagen und das der Übersetzung.
Im Folgenden werden die Anforderungen für das SEP (Softwareentwicklungspraktikum) 2006 am
Institut für Wissenschaftliches Rechnen der Technischen Universität Braunschweig bekannt
gegeben.
Einleitung
Die CTL (Component Template Library) wurde am Institut für Wissenschaftliches Rechnen der
Technischen Universität Braunschweig entwickelt und ist eine C++ Template Bibliothek zur
Realisierung verteilter komponentenbasierter Software Systeme. In diesem Kontext ist eine
Komponente ein Stück Software, bestehend aus einer Schnittstelle (CI = Component Interface) und
einer Implementierung. CI und Implementierung sind über einen Kommunikationskanal
miteinander verbunden. Die Kommunikation erfolgt mittels TCP/IP, der
Kommunikationsbibliothek MPI, PVM, via Pipe, Thread oder Shared Library.
Vergleichbare Produkte sind CORBA (Common Object Request Broker Architecture)
Implementierungen, Microsofts .Net oder CCA (Common Component Architecture).
In einem Verteilten System kommunizieren Prozesse miteinander über Prozess- bzw.
Rechnergrenzen. Genauer betrachtet, kommunizieren die Objekte miteinander, die innerhalb der
Prozesse „leben“. Befinden sich die Prozesse auf verschiedenen Rechnern, erfolgt die
Kommunikation über das Netzwerk, indem die Rechner eingebettet sind. Ein Objekt, das von außen
(außerhalb des Prozesses) angesprochen werden kann, wird von der aufrufenden Instanz eines
anderen Prozesses als sogenanntes entferntes Objekt (remote object) wahrgenommen. Die damit
verbundenen Aufrufe werden als entfernte Aufrufe (RMI = remote method invocation) bezeichnet.
Ein Prozess, der ein entferntes Objekt aufruft, nimmt die Rolle des sogenannten Clients (Kunden)
an. Der entfernte Prozess entspricht damit dem Server bzw. Service.
Wird ein entferntes Objekt innerhalb eines CTL Systems aufgerufen, so ist dieser Aufruf technisch
für den Programmierer völlig identisch zu einem lokalen Aufruf. Daher wird auch von OrtsTransparenz gesprochen. Tatsächlich resultiert ein Methodenaufruf typischerweise in zwei
Nachrichten. Die erste Nachricht wird vom Client zum Server gesendet, der als Konsequenz die
gewünschte Methode lokal aufruft. Die zweite Nachricht wird vom Server zum Client gesendet, um
das Resultat des Methodenaufrufs zu liefern, siehe folgende Abbildung. Das Versenden einer
Nachricht entspricht dem Schreiben in einen Kommunikationskanal, das Empfangen einer
Nachricht dem Lesen von dem Kanal. Diese Begriffe werden im Folgenden als Synonym für das
Versenden und Empfangen von Nachrichten verwendet.
RMI call
Client
Server
RMI answer
Ziel
Durch die Kommunikation über Prozess- bzw. Rechnergrenzen treten weitere Fehlerquellen bei der
Implementierung von Verteilten Anwendungen auf, wie beispielsweise verlorene Verbindungen
oder das Rebooten eines entfernten Rechners.
Das Ziel ist daher die Realisierung eines Debuggers für Verteilte Komponentensysteme auf Basis
der CTL. Der Debugger muss über einen Prompt zur Eingabe von Debuggerkommandos, einem
interaktiven Visualisierer von Kommunikationsaktivitäten sowie einer Anbindung zu einem
existierenden Java bzw. C++ Debugger für monolithische Programme verfügen, um auch eine
Komponente selbst debuggen zu können.
Zur Fehlersuche in einem Komponentensystem müssen alle zugehörigen Komponenten im DebugModus angestartet werden. Der Debugger übernimmt als außenstehende Instanz die Rolle des
Kontrollorgans. Hierzu steuert er den Ablauf der Komponenten, indem er sie anhält bzw.
weiterlaufen lässt (vergleichbar mit einer Verkehrsampel). Schaut man genauer hin, so führt der
Debug-Modus dazu, dass die Komponenten sich selber anhalten. Der Debugger kontrolliert somit
lediglich das Weiterlaufen der angehaltenen Komponenten.
Die Kommunikation zwischen Debugger und Komponenten erfolgt ausschließlich über
Nachrichten. Die Nachrichten des Debuggers werden als Control-Nachrichten bezeichnet, da sie
den Ablauf der Komponenten steuern. Sämtliche Nachrichten, die eine Komponente verschickt,
werden als Kopie zuerst an den Debugger gesendet.
Im Detail führt eine Komponente, die im Debug-Modus angestartet wurde, Anweisungen aus bis sie
einen Schreibbefehl auszuführen, also eine Nachricht zu schreiben hat. Sie schickt die Kopie der
Nachricht an den Debugger und wartet auf eine Continuous-Nachricht (spezielle ControlNachricht) des Debuggers. Hat sie diese erhalten, schickt sie die Nachricht an den eigentlichen
Empfänger und führt alle Anweisungen bis zum nächsten Schreibbefehl aus. Da der Debugger den
Aufruf vor dem tatsächlichen Aufruf im Visualisierer anzeigt, wird das Identifizieren von
Fehlerquellen erleichtert. Dies entspricht der Vorgehensweise monolithischer Debugger.
Der Debugger erhält zur Laufzeit alle Nachrichten, die Komponenten nach der nächsten
Continuous-Nachricht schreiben werden (siehe oben). Er speichert alle IP-Adressen und PortNummern der Komponenten, die nach der letzten Continuous-Nachricht eine Kopie der als nächstes
zu verschickenden Nachricht versendet haben, in einer Liste. Folglich warten alle Komponenten aus
dieser Liste auf eine Continuous-Nachricht (Reply des Debuggers). Es müssen nicht alle
Komponenten des Systems in dieser Liste zu finden sein. Zum Einen taucht eine Komponente, die
beschäftgt ist, aber bislang keine Nachricht zu verschicken hat, in der Liste nicht auf. Zum Anderen
sind auch Komponenten, die nicht beschäftigt sind, also auf Aufträge warten, nicht in der Liste
gespeichert. Das Versenden von Continuous-Nachrichten wird dem Debugger durch den Benutzer
über Debuggerkommandos in einem Prompt mitgeteilt. Generell wird eine Control-Nachricht an
alle Komponenten der Liste gleichzeitig verschickt (Broadcasting).
Der momentane Zustand des Komponentensystems wird von Komponenten, den entfernten
Objekten und den letzten Kommunikationen, also der nach dem nächsten Continuous-Lauf zu
verschickenden Nachrichten, beschrieben. Möchte der Benutzer den nächsten Zustand des Systems
einsehen, beauftragt er den Debugger mit der Versendung der nächsten Continuous-Nachricht.
Allerdings kann das Warten des Benutzers auch die Folge haben, das eine weitere Komponente den
Punkt erreicht hat, an dem sie einen Schreibbefehl auszuführen hat und demnach eine
Nachrichtenkopie verschickt. Der Zustand ist daher dynamisch und soll durch den Visualisierer als
solcher auch angezeigt werden. Diese Vorgehensweise erhält die Dynamik des Systems und zwängt
dieses nicht in ein vollständig getaktetes System mit festem Taktschritt.
Es sei zudem angemerkt, dass Komponenten durch andere Komponenten angestartet oder auch
kontrolliert zerstört werden können, also das Komponentensystem dynamisch anwachsen oder auch
schrumpfen kann. Allerdings müssen dem Debugger zu Beginn sämtliche zur Laufzeit angestarteten
Komponenten bekannt sein. Hierzu liest der Debugger in einer initialen Phase die
Komponentenschnittstellen (CIs) aller Komponenten ein.
Der Debugger kann in zwei Softwareteile zerlegt werden, einer Hauptroutine und den Mirror. Die
Hauptroutine wird durch den Benutzer zum Debuggen des Komponentensystems aufgerufen. Der
Mirror stellt einen Spiegel zum Komponentensystem dar. Er wird als CTL Komponente
implementiert, der aber im Vergleich zu den Komponenten des Systems nicht im Debug-Modus
angestartet wird. Im Mirror werden die in der Initialisierungsphase eingelesenen CIs implementiert.
Demnach bildet dieses implementierte Komponentensystem auf der Seite des Debuggers
hinsichtlich der Schnittstellen einen Spiegel zum eigentlichen Komponentensystem. Die
spiegelnden Komponentenimplementierungen sollen dann den Visualisierer direkt beeinflussen, das
heißt die Kommandos zum Zeichnen der einzelnen Grafikbausteine selber aufrufen, siehe die
folgende Abbildung (zum Visualisierer gleich mehr). Zur Generierung der spiegelnden
Implementierungen ist ein Code-Generator erforderlich. Die Realisierung des Mirrors als
Komponente hat einen entscheidenen Vorteil. Da die Nachrichten zwischen Debugger und
Komponenten des Komponentensystems binär codiert sind, gilt es diese zu verstehen. Demnach
muss der Debugger in der Lage sein, selber Nachrichten schreiben, aber auch eintreffende
Nachrichten lesen zu können. Als CTL-Komponente übernimmt der Mirror automatisch die
Konvertierung der binären Daten. Eine Re-Implementierung des CTL Protokolls ist daher nicht
nötig. Als Konsequenz müssen die Kopien der Nachrichten durch die Komponenten des Systems
direkt an den Mirror geschickt werden. Wird eine Komponente während des Debug-Modus
angestartet, erhält sie daher die IP-Adresse und Port-Nummer des Mirrors.
Debugger
Port
Komponentensystem
Mirror
Comp2::
Comp2::
Obj2
Obj2
Obj3
Obj1
Obj4
Comp1::
Comp3::
Obj3
Obj1
Obj4
Comp1::
Comp3::
Der Visualisierer soll den derzeitigen Zustand des Komponentensystems in Form eines Graphen
erfassen. Der Zustand wird durch das vom Benutzer eingegebene Debuggerkommando initiiert. Der
Graph umfasst die Komponenten und entfernten Objekte (zum Beispiel durch Kreise dargestellt),
als auch die zuletzt stattgefundenen Kommunikationen (zum Beispiel durch Pfeile dargestellt). Des
Weiteren soll jede Kommunikation interaktiv anwählbar sein, um so nähere Informationen über die
Kommunikation zu erhalten. Diese Informationen beinhalten die Argumente bzw. Ergebnisse des
mit der Nachricht verknüpften Methodenaufrufs, sofern sinnvoll. Zum Beispiel sollen Vektoren mit
relativ vielen Einträgen nicht angezeigt werden. Zudem soll graphisch ersichtlich sein, zu welcher
Komponente ein Objekt gehört, siehe folgende Abbildung. Es bietet sich an, die Objekte einer
Komponente als Verbund graphisch kenntlich zu machen. Eintreffende Nachrichten sollen durch
den Debugger im Graph des Visualisierers umgehend aktualisiert werden. Somit wird die Dynamik
des Systems durch den Visualisierer erfasst. Komponenten, die seit dem letzten Continuous-Lauf
keine Nachricht verschickt haben, sollen im Graphen kenntlich gemacht werden (zum Beispiel
durch blasse Farben). Natürlich soll auch das dynamische Anwachsen bzw. Schrumpfen des
Systems in der Visualiserung abgebildet werden. Dies ergibt sich bei korrekter Implementierung
allerdings automatisch. Die Komponenten, Objekte und Kommunikationen sollen im Graphen
automatisch und sinnvoll angeordnet werden. Der Benutzer soll des Weiteren auch in der Lage sein,
interaktiv die Lage der Komponenten und Objekte zu bestimmen.
Möchte der Benutzer ein n-fachen Continuous-Lauf (n Continuous-Nachrichten hintereinander)
durchführen, so wäre eine Möglichkeit, die Liste der Komponenten (siehe oben) so oft zu
durchlaufen bis jede zu Beginn des Durchlaufs vermerkte Komponente n Nachrichtenkopien
verschickt hätte. Es ist möglich, dass dieser Vorgang nicht terminiert oder zumindest für den
Benutzer zu lange dauert. Daher soll der Benutzer die Möglichkeit haben die Ausführung eines
solchen Befehls zu stoppen.
RMI call
double 2.00
double 0.5
Comp1::
Obj1
Comp2::
RMI call
method2
Obj2
Ebenso muss ein existierender Java bzw. C++ Debugger eingebunden werden, mit dem eine
Komponente selbst geöffnet werden kann. Dies soll über einen Systemcall geschehen.
Zum Abschluss soll nun die Arbeitsweise des Debuggers zusammengefasst werden. Der Debugger
liest zu Beginn alle CIs der Komponenten des Komponentensystems ein. Aus diesen erzeugt er
mittels Code-Generator die spiegelnden Komponenten auf der Seite des Debuggers. Die
Implementierungen der spiegelnden Komponenten beeinflussen direkt den Visualisierer, das heißt,
eine eintreffende Nachrichtenkopie einer Komponente resultiert umgehend in der Aktualisierung
des Zustandsgraphen des Komponentensystems. Komponenten, die noch keine Nachricht innerhalb
des momentanen Zustands verschickt haben, sollen als solche kenntlich gemacht werden. Der
Benutzer soll die Möglichkeit haben, interaktiv Kommunikationsinformationen zu erhalten und den
Graphen selber anzuordnen. Des Weiteren soll auch die Möglichkeit des direkten Debuggens durch
einen Debugger für monolithische Programme bereitgestellt werden.
Zur Entwicklung des Debuggers müssen eine Reihe bestehender Tools bzw. Bibliotheken zum
Einsatz kommen. Insbesondere für die Graphendarstellung, das Parsen der CIs mit anschließender
Code-Generierung sowie den Java bzw. C++ Debugger für monolithische Programme müssen
entsprechende Tools bzw. Bibliotheken gefunden und auf Funktionalität und Zuverlässigkeit
geprüft und dementsprechend ausgewählt werden.
Wünschenswert wäre noch, sich auch vergangene Zustände (Zustandshistorie) im Debugger
ansehen zu können. Demnach müssten die einzelnen Zustände chronologisch richtig abgespeichert
werden.
Einsatz
Der zu entwickelnde Debugger muss in vollem Umfang unter Linux lauffähig sein. Als
Programmiersprache soll Java verwendet werden. Daher muss auf die Java CTL Realisierung
CTL4j zurückgegriffen werden.
Übersicht
In der folgenden Zusammenfassung wird der zu entwickelnde Debugger noch einmal aufgezeigt.
•
•
•
•
•
•
Debugger als CTL Komponente, Code-Generator und Spiegel des Komponentensystems
Parser zur Konvertierung der Komponentenschnittstellen (CIs)
Prompt zur Eingabe von Debuggerkommandos durch den Benutzer
Interaktiver Visualisierer von Kommunikationsaktivitäten, Graphendarstellung des
Komponentensystemzustands, interaktives Anwählen von Kommunikationen im Graphen
Schnittstelle zu existierendem Java bzw. C++ Debugger für monolithische Programme
Einsicht in Zustandshistorie wünschenswert
Die technische Produktumgebung ist wie folgt.
•
Betriebssystem:
Linux
•
Programmiersprache:
Java
•
Entwicklungsumgebung:
eclipse
•
Testwerkzeuge:
JUnit
•
Tools/Bibliotheken:
CTL4j und weitere (Analyse notwendig)
Funktionen
Die im Folgenden angegebenen Funktionalitäten des zu entwickelnden Debuggers sind lediglich
aus Sicht des Benutzers.
Der Prompt zur Eingabe von Debuggerkommandos muss mitunter die folgenden Kommandos
ausführen können.
•
run prog:
Starten des zu prüfenden Programms
•
next arg:
Zeige den arg-ten Zustand des Komponentensytems, der dem
derzeitigen Zustand folgt (für den Entwickler: schicke arg
Continuous-Nachrichten an alle Komponenten)
•
break on arg: Zeige den nächsten Zustand, in dem Komponente,
Objekt, Funktion oder Methode arg aufgerufen wird und halte dort an
•
list:
Auflisten aller Komponenten im Prompt
•
debug arg:
Öffne Komponente arg im Java bzw. C++ Debugger für
monolithische Programme
•
more arg:
Spiegeln der Kommunikationsinformation zur Funktion/Methode arg
im Prompt
Der Visualisierer muss, wie oben beschrieben, den derzeitigen Zustand des Komponentensystems
anzeigen. Über das interaktive Anwählen einer Kommunikation innerhalb des Graphen müssen
nähere Informationen (Argumente bzw. Rückgabewerte) über die Kommunikation geliefert werden.
Graphenbausteine sollen automatisch und sinnvoll angeordnet werden, allerdings auch durch den
Benutzer verschoben werden können.
Mittels existierendem Java bzw. C++ Debugger für monolithische Programme muss eine
gewünschte Komponente geöffnet werden können.
Zu guter letzt wäre noch eine Einsicht in die Zustandshistorie des Komponentensystems
wünschenswert. Dem Benutzer wäre so möglich den Weg zum derzeitigen Zustand historisch
zurückzuverfolgen.
Qualitätsanforderungen
Die Qualitätsanforderungen an das zu entwickelnde Produkt sind im Folgenden angegeben.
Benutzbarkeit
Eine benutzerfreundliche Bedienung ist Voraussetzung. So sollen die Graphenbausteine des
Visualisierers automatisch gesetzt aber auch auf Wunsch des Benutzers durch diesen manuell
angeordnet werden können.
Zuverlässigkeit
Es wird auf ausgiebige Tests zum Erhalten eines stabiles Produktes Wert gelegt.
Effizienz
Optimierungen bezüglich der Effizienz sind nicht vorgesehen.
Änderbarkeit
Das Programmdesign muss für eine spätere Erweiterung ausgelegt sein. Neben einer sauberen
Programmstruktur sollen die Kommentare nach Javadoc Vorgaben erfolgen. Programmcode und
Kommentare sollen in Englisch verfasst werden. Klassen, Methoden, Variablen etc. sollen sinnvoll
benannt werden. Zum Testen der Klassen soll unter anderem JUnit zum Einsatz kommen.
Zugehörige schriftliche Ausarbeitungen sind wünschenswerterweise ebenso in Englisch
auszuführen. Hierzu soll Latex verwendet werden.
Bei der Wahl der zu verwendenen Tools/Bibliotheken muss darauf geachtet werden, dass diese mit
hoher Wahrscheinlichkeit auch in Zukunft von den Herstellern gepflegt werden. Des Weiteren
müssen alle Quellen in das institutsinterne CVS Repository eingespeist und in diesem während des
SEPs gepflegt werden.
Ergänzungen
Die in diesem SEP entstandene Software muss unter LGPL gestellt werden.
Auf der Webseite dieses Themas wird eine Studienarbeit zur Verfügung gestellt, in der genauer auf
das CTL Protokoll sowie CTL4j eingegangen wird.
Ansprechpartner für das SEP 2006 am Institut für Wissenschaftliches Rechnen ist
Martin Krosche ([email protected]).
Herunterladen