Feb. 2006 Fehlerbehandlung in Programmiersystemen Christoph Kessler Universität Linköping, Schweden Fehler-Klassifikation Behandlung statischer Fehler Behandlung von Laufzeitfehlern Exception-Konzept Debugging Christoph Kessler, IDA, Linköpings universitet, 2006. Programmierfehler… Ein erheblicher Teil der Gesamtkosten eines Softwareprojektes entfällt auf Testen, Fehlersuche und -behebung. Welche Fehlertypen können auftreten? Klassifikation Prävention, Diagnose, Behandlung Programmiersprachliche Konzepte Compiler, IDE Sonstige Werkzeuge: Debugger, Verifizierer, ... C. Kessler, IDA, Linköpings universitet. 2 Feb. 2006 Programmierfehler – Klassifikation (1) Syntaktische Fehler Syntaxfehler z.B. vergessenes Semikolon Semantische Fehler Statische semantische Fehler Statische Typfehler Falscher Parametertyp; Downcast ohne Laufzeit-Überprüfung Nicht deklarierte Variable Laufzeitfehler Logische Fehler Algorithmische Fehler vergessener Spezialfall, Nichtterminierung Akkumulation von Rundungsfehlern Verletzung geforderter Invarianten Numerische Fehler Kontraktverletzung C. Kessler, IDA, Linköpings universitet. 3 Feb. 2006 Programmierfehler – Klassifikation (2) Laufzeitfehler – in der Regel nicht statisch prüfbar Zugriffsfehler z.B.: Arrayindex-Fehler Index out of bounds Pointerfehler Dereferenziere NULL-Pointer Arithmetische Fehler Division durch 0, Überlauf I/O – Fehler unerwartetes Dateiende Kommunikationsfehler Falscher Empfänger, falscher Typ Synchronisationsfehler Daten-”race”, deadlock Ressourcen-Erschöpfung Speicher, Zeitkonto ... Bemerkung: Es gibt weitere Fehlertypen, und Kombinationen. C. Kessler, IDA, Linköpings universitet. 4 Feb. 2006 Gegenmittel: Prävention, Diagnose, Behandlung Programmiersprache / Laufzeitsystem Typsicherheit statische Typfehler Exception-Konzept Laufzeitfehler Automatische Speicherverwaltung Speicherlecks, Pointerfehler Compiler-Frontend, IDE Syntaxfehler, statische semant. Fehler Programmverifizierer Kontraktverletzung Code-Inspektion [Fagan’76] Alle Fehlertypen Testen und Debuggen Laufzeitfehler Laufzeit-Schutzmonitor Zugriffsfehler Visualisierer Kommunikationsfehler, Synchronisationsfehler C. Kessler, IDA, Linköpings universitet. 5 Feb. 2006 Exception-Konzept PL/I (IBM) ca. 1965: ON condition … J. B. Goodenough, POPL’1975 und Comm. ACM Dez. 1975 In vielen modernen Programmiersprachen unterstützt CLU, Ada, Modula-3, ML, C++, Java, C# Überblick: Fehler vs. Exception Exception-Propagation Geprüfte vs. ungeprüfte Exceptions Implementierung Exceptions in CORBA Exceptions und Aspekt-orientierte Programmierung Zusammenfassung und Literatur C. Kessler, IDA, Linköpings universitet. 6 Feb. 2006 Exception-Konzept 2 Arten von Laufzeitfehlern: Fehler (error): im Programm nicht behandelbar, Programmabbruch Ausnahme (exception): im Programm (teilweise) behandelbar Ausgelöst (thrown) durch Laufzeitsystem bei erkanntem Laufzeitfehler oder durch das Programm selbst Nachricht Laufzeitobjekt, das eine ungewöhnliche oder Fehlersituation definiert hat an das Programm einen Typ (Exception-Klasse) kann Parameter haben, z.B. String mit Klartextmeldung Auch benutzerdefinierte Exceptions z.B. für Randfälle Exception-Handler: enthält Code-Block zur Behandlung ist statisch assoziiert mit geschütztem Code-Block, den er im Ausnahmefall ersetzt C. Kessler, IDA, Linköpings universitet. 7 Feb. 2006 Exception – Beispiel (in Java) public class class1 { public static void main ( String[] args ) { try { System.out.println("Hallo, " + args[0] ); } catch (ArrayIndexOutOfBoundsException e ) { System.out.println("Bitte ein Argument angeben! " + e); } System.out.println("Tschuess"); } } % % java java class1 class1 Christoph % javaChristoph class1 Hallo, Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0 Bitte ein Argument angeben! java.lang.ArrayIndexOutOfBoundsException: 0 Tschuess at class1.main(class1.java:4) Tschuess C. Kessler, IDA, Linköpings universitet. 8 Feb. 2006 Propagation von Exceptions Falls eine Exception nicht in der betroffenen Methode behandelt wird, wird die Methode verlassen und dieselbe Exception beim Aufrufer ausgelöst, bis entweder ein passender Handler gefunden wird, oder main() verlassen wird (dann Fehlermeldung und Abbruch). Optionaler finally-Block wird jedoch immer ausgeführt z.B. zur Rückgabe von Ressourcen Zu klären: Wann passt ein Handler? Wie kann man statisch sicherstellen, dass eine bestimmte Exception irgendwann behandelt wird? Implementierung? C. Kessler, IDA, Linköpings universitet. 9 Feb. 2006 Wann ”passt” ein Handler? Object Exception-Klassenhierarchie Benutzerdefinierte Exceptions durch Ableiten Throwable Error Exception RunTimeException ThreadDeath VirtualMachineError ArithmeticException … ArrayIndexOutOfBoundsE NullPointerException Handler catch( XYException e ) {…} passt, falls XYException vom gleichen Typ oder Supertyp der ausgelösten Exception ist. C. Kessler, IDA, Linköpings universitet. IllegalAccessException NoSuchMethodException 10 … Feb. 2006 Geprüfte und ungeprüfte Exceptions Geprüfte (checked) Exception: muss in einer Methode behandelt, oder in Methodendeklaration explizit als propagiert gekennzeichnet werden: void writeEntry( … ) throws IOException { … } Ungeprüfte (unchecked) Exception: wird implizit propagiert In Java: Alle Exceptions sind geprüft, ausser RunTimeException und deren Subtypen. Geprüfte Exceptions: + Kapselung + statische Prüfbarkeit + wird Teil des Kontrakts einer Methode + geeignet für Komponentensysteme, z.B. CORBA – Erweiterbarkeit C. Kessler, IDA, Linköpings universitet. 11 Feb. 2006 void bar(…) { try { … } catch(E1 e) {…} catch(E2 e) {…} Einfache Lösung: -> catch(E1) … Stack von Handlern bar: -> catch(E2) } Bei Eintritt in geschützten Block (try {…}): -> catch(…) Pushe alle seine Handler (catch(…) {…}) foo: -> catch(…) Bei Auftreten einer Exception: main: -> catch(…) Poppe obersten Handler und beginne (Test auf Exceptiontyp). Falls der nicht passt, löse wieder aus und iteriere. (Falls letzter Handler in aktueller Methode auch nicht passte, poppe auch deren Activation record => verlasse Methode.) Bei normalem Verlassen des try-Blocks: poppe seine Handler + einfach – Overhead (push/pop) auch bei Nichtauftreten einer Exception Implementierung Effizientere Lösung: Compiler erzeugt Tabelle aus Paaren (try-Block, passende Handler) Auftreten finde try-Block durch Binärsuche (PC) Feb. 2006 C. Kessler, IDA, Bei Linköpings universitet. einer Exception: 12 Exceptions in CORBA CORBA IDL (Interface Definition Language) erlaubt benutzerdefinierte Exceptions Sprachunabhängig Propagation über Fernaufrufe hinweg // IDL module BookRepository { … interface BorrowableCollection : Collection { exception Unavailable { Date when_available; } void borrow_book ( in ISBN book_id, in PersonName borrower, out Date return_date ) raises ( Unavailable ); }; }; C. Kessler, IDA, Linköpings universitet. 13 Feb. 2006 Exceptions und AOP Nachteil von Exception-Behandlung: catch()-Blöcke stören Programmübersicht Code-Länge Idee für Java: Exception-Behandlung als Aspekt ausfaktorisieren, mit Aspect-J einweben Systematischer durch generische Exceptionbehandlung Kompression des Exception-Behandlungscode um ca. 75% M. Lippert, C. Lopes: A Study on Exception Detection and Handling using Aspect-Oriented Programming. Proc. ICSE-2000, ACM. C. Kessler, IDA, Linköpings universitet. 14 Feb. 2006 Zusammenfassung, Literatur Exceptions Bewährtes Konzept zur Behandlung von Laufzeitfehlern Effizient implementierbar Geeignet für komponentenbasierte Softwareentwicklung M. Scott: Programming Language Pragmatics. Morgan Kaufmann, 2000. Abschnitt 8.5 über Exception Handling. J. Goodenough: Structured Exception Handling. ACM POPL, Jan. 1975 J. Goodenough: Exception Handling: Issues and a proposed notation. Communications of the ACM, Dec. 1975 B. Ryder, M. Soffa: Influences on the Design of Exception Handling, 2003 Konferenzen ACM POPL und OOPSLA C. Kessler, IDA, Linköpings universitet. 15 Feb. 2006 Feb. 2006 Debugging Debuggen vs. Testen Debugging-Methoden und Werkzeuge Debugger-Technologie Debuggen nebenläufiger Programme Zusammenfassung, Literatur Christoph Kessler, IDA, Linköpings universitet, 2006. Debugging Testen: kann Existenz eines Fehlers feststellen (ohne Garantie auf Vollständigkeit!) Vergleiche Ausgabe des Testkandidaten mit Referenzausgabe (z.B. älterer, korrekter Version – Regressionstesten, z.B. DEJAGNU) Debuggen: lokalisiere Fehler: Iterativer Prozess Systematisches Eingrenzen Fehler entdeckt Initiale Hypothesenmenge Ursache Modifiziere Hypothesenmenge Wähle Hypothese Effekt Verifiziere Hypothese nein Fehler beseitigt? ja C. Kessler, IDA, Linköpings universitet. 17 Feb. 2006 Debugging-Techniken und Werkzeuge (1) Manuelle Methoden Statisch: Code-Inspektion Dynamisch: print-Anweisungen, Validierung von Zusicherungen (assert() ) Werkzeuge für die manuelle Fehlersuche: z.B. dbx, gdb, jdb, ddd Debug-Problem-Dokumentation z.B. Symbolischer Debugger BUGZILLA (Fehler-Datenbank + Web-Interface) Laufzeit-Schutz-Monitorsystem ibs. für Zugriffsfehler ElectricFence, C. Kessler, IDA, Linköpings universitet. VALGRIND, Java VM, INSURE++, PURIFY, … 18 Feb. 2006 Debugging-Techniken und Werkzeuge (2) Automatisches Debugging: Formale Verifikation gegen formale Spezifikation des Programms Oft keine oder unvollständige formale Spezifikation verfügbar Ggf. Spezifikation herleitbar, aber dann selbst fehleranfällig Durchsuche Quellprogramm nach sprachspezifischen Fehler-Idiomen z.B. lint, splint, jlint unvollständig Fehlersuchbereich eingrenzen durch statische Analyse: Program Slicing [Weiser’82] [Lyle, Weiser’87] Program Dicing (Differenz zweier Slices) [Lyle, Weiser’87] z.B. UNRAVEL slicer [Lyle’95] Braucht gute statische Analyse (DFA, points-to-Analyse) Konservative statische Approximation – Slices werden schnell gross Delta-Debugging Automatisches Eingrenzen durch Binärpartitionierung (Eingabedaten, Code) C. Kessler, IDA, Linköpings universitet. 19 Feb. 2006 Symbolischer Debugger (1) Braucht Information über Namen und Typ von Speicherstellen auf Quellcode-Niveau d.h., die Symboltabelle und Typtabelle des Compilers Wird bei Bedarf eingefügt (cc –g … ) Braucht Koordinaten der Programmpunkte im Quellcode (z.B. Zeilennr.) Braucht enge Kontrollfluss-Übereinstimmung zwischen Quellcode und Maschinencode Unverträglich mit aggressiven Programmoptimierungen z.B. Prefetching, Loop-invariant code hoisting, Schleifentransformationen, Scheduling Trade-Off Code-Effizienz Debugger-Transparenz Kann unter gewissen Umständen dazu führen, dass der Fehler mit Debugger nicht auftritt (gilt auch für print-debugging) Graphische Oberfläche (z.B. ddd, Eclipse Debug-View) über Kommandozeilen-Debugger (z.B. dbx, gdb, jdb) C. Kessler, IDA, Linköpings universitet. 20 Feb. 2006 Symbolischer Debugger (2) Post-Mortem-Debugging Nach Absturz: Lies core-file; inspiziere Speicherinhalt, Variablenwerte Interaktives Debuggen Berechnung anhalten Breakpoints (Haltepunkte) setzen, löschen Schritt-für-Schritt-Ausführung Ausgabe von Werten, Ausdrucksauswertung (Interpreter) Variablenwerte ändern Aufrufkeller inspizieren Aufrufkette entlangwandern C. Kessler, IDA, Linköpings universitet. 21 Feb. 2006 Debugger-Technik mit OS/HW-Support Debugger-Prozess Zu debuggender Prozess OS OS-IRC fork() (via OS) ptrace() (via OS): ”trace me” signal() (via OS): ”stop” wait() (via OS) ptrace() (via OS) Lese, schreibe Werte im Adressraum; füge breakpoints (Spezialinstruktionen) in Code ein … signal() (via OS): ”resume” signal(): ”Breakpoint” … Finde Breakpunkt: trap signal(): ”continue” C. Kessler, IDA, Linköpings universitet. 22 Feb. 2006 Debuggen nebenläufiger Programme Problem: Auftreten des Fehlers kann vom Schedule abhängen Lauf 1: CPU Thread 1 Lauf 2: CPU Thread 2 Thread 2 Thread 1 Thread 1 Thread 2 t Thread 2 Thread 1 Nichtdeterminismus schwer, Fehler zu reproduzieren Lösung 1: Deterministic replay Eingaben und Schedule aufzeichnen, z.B. DEJAVU für Java Lösung 2: Statische Analyse (möglicher Parallelismus, ”Data-races”) Lösung 3: Dynamische Analyse identifiziert shared-memory-Zugriffe zur Laufzeit Lösung 4: Test-basierter Ansatz mit Delta-Debugging [Choi, Zeller ’02] In Kombination mit DEJAVU C. Kessler, IDA, Linköpings universitet. 23 Feb. 2006 Zusammenfassung und Literatur Testen vs. Debuggen Debugging-Methoden Debugger-Technologie Debuggen nebenläufiger Programme M. Scott: Programming Language Pragmatics, Morgan Kaufmann 2000. Abschnitt über Debugging Srikant, Shankar: Compiler Design Handbook, CRC press 2003, Kap. 9 über Debugger-Technologie (von Aggarwal und Kumar) J. Rosenberg: How Debuggers Work. Wiley, 1996. A. Zeller: Why Programs Fail. A Guide to Systematic Debugging. Morgan Kaufmann, 2005. A. Zeller, J. Krinke: Open-Source Programmierwerkzeuge. Dpunkt, 2003. C. Kessler, IDA, Linköpings universitet. 24 Feb. 2006