Wikipedia · einfach zusammengefasst · Stand
Debuggen
Als Debuggen (dt. Entwanzen) oder Fehlerbehebung bezeichnet man in der Informatik den Vorgang, in einem Computerprogramm Fehler oder unerwartetes Verhalten …
Inhalt4 Abschnitte
Bedeutung und Ablauf
Debuggen, auf Deutsch auch Entwanzen oder Fehlerbehebung, ist in der Informatik das Diagnostizieren und Beheben von Fehlern oder unerwartetem Verhalten in einem Computerprogramm. Die Suche nach Programmfehlern, sogenannten Bugs, gehört zu den wichtigsten und anspruchsvollsten Aufgaben der Softwareentwicklung und beansprucht einen großen Teil der Entwicklungszeit. Die möglichen Vorgehensweisen hängen vom jeweiligen Problem ab. Dazu gehören interaktives Debuggen mit Debuggern, die Analyse von Datenflussdiagrammen, Unittests, die Analyse von Speicherdumps sowie Profiler zur Optimierung der Laufzeit.
Zunächst muss bei einer Fehlermeldung oder Beobachtung geklärt werden, ob das Verhalten tatsächlich ein Fehler ist und welche Analysemethode geeignet ist. Deshalb wird oft der neutralere Begriff „Issue“ verwendet; er bedeutet Aufgabe oder Sachverhalt. Der erste praktische Schritt ist meist, das unerwünschte Verhalten zu reproduzieren, also es unter gleichen Bedingungen erneut hervorzurufen. Das kann schwierig sein, wenn ein Fehlerbericht ungenau oder sehr allgemein ist, wenn das Verhalten als zu langsam empfunden wird oder wenn unbekannte Hardwarefaktoren die Laufzeit beeinflussen.
Schwer erkennbare Fehler und Behebung
Besonders schwer reproduzierbar sind Fehler in Multithreading-Umgebungen. Dort kann ein Fehler nur zufällig auftreten, weil sein Auftreten von der zeitlichen Abhängigkeit zwischen zwei Threads abhängt. Manche solche Fehler verschwinden gerade dann, wenn man sie einzugrenzen versucht. Sie heißen Heisenbugs, benannt nach Werner Heisenberg.
Nachdem ein Fehler gefunden wurde, muss entschieden werden, wie er sicher zu beheben ist. Das kann kompliziert sein: Programmteile können möglicherweise nur deshalb korrekt funktionieren, weil der Fehler vorhanden ist. Seine Korrektur kann dann andere Funktionen beeinträchtigen. Außerdem kann ein Fehler aus einem Designfehler entstehen, wenn bei der Entwicklung eine bestimmte Situation gar nicht berücksichtigt wurde. Die Behebung kann viel Zeit erfordern; manchmal wird auf sie verzichtet, wenn das Risiko eines größeren Schadens als der erwartete Nutzen ist. Debuggen gilt als komplizierter als Programmieren und kann daher wesentlich mehr Zeit beanspruchen. Als hilfreiche Grundlage wird leicht lesbarer Code empfohlen.
Werkzeuge zur Fehlersuche
Bei klassischen Bugs ist ein Ergebnis offensichtlich falsch oder ein Programm stürzt bei einer bestimmten Eingabe reproduzierbar ab. Eingaben und Ausgaben sind dabei nicht nur Tastatur und Bildschirm, sondern alle Datenein- und -ausgänge, etwa Dateien, Netzwerkverbindungen, Drucker und Scanner.
Eine einfache Methode ist die Instrumentierung: Zusätzliche Programmzeilen geben Zwischenergebnisse auf dem Bildschirm oder in einer Logdatei aus. Damit lässt sich feststellen, in welchem Berechnungsschritt ein Problem entsteht oder wo ein Programm unerwartet abbricht. Zur Eingrenzung können Divide and Conquer oder binäre Suche verwendet werden. Diese Methode heißt auch printf-Debugging, nach der dafür häufig verwendeten C-Funktion printf.
Bei komplexeren Programmen wird meist ein Debugger eingesetzt, häufig als Teil einer IDE. Im Quelltext können Breakpoints gesetzt werden, an denen das Programm anhält. Im angehaltenen Zustand lassen sich der Aufrufstapel und Variablen untersuchen. Außerdem kann das Programm Zeile für Zeile ausgeführt werden, um unerwartete Sprünge zu entdecken. Der Debugger gilt neben dem Compiler als wichtigstes Werkzeug eines Programmierers.
Ein Tracer verfolgt den Ablauf anders: Er unterbricht die Programmausführung nicht, sondern zeichnet Informationen über Ablauf und Variablenwerte auf, die anschließend ausgewertet werden. Tracepoints können im Quelltext gesetzt oder allgemein festgelegt sein, beispielsweise für jeden Funktionsaufruf oder jede Zeile. Auch Logdateien und Crash Dumps können nach einem Absturz Hinweise liefern, selbst wenn sich der Fehler nicht leicht reproduzieren lässt oder der vorherige Programmpfad unbekannt ist. Ein gespeicherter Aufrufstapel zusammen mit einer Exception kann zeigen, welche Invariante nicht erfüllt war oder welche Operation unerwartete Ergebnisse lieferte.
Ein Profiler analysiert ein laufendes Programm und zeigt, welche Funktionen wie viel Zeit benötigen, meist relativ zur insgesamt verbrauchten Zeit. Dadurch werden unnötig lange Operationen und sinnvolle Ansatzpunkte für Optimierungen sichtbar. Nicht alle möglichen Optimierungen sollten schon während der Entwicklung eingebaut werden, weil erst Tests zeigen, welche Funktionen häufig ausgeführt werden und wo Verzögerungen stören. Mit denselben Werkzeugen lässt sich oft auch der Speicherverbrauch untersuchen.
Debugging fremder Programme und Schutzmaßnahmen
Ein Debugger kann grundsätzlich auch Programme untersuchen, die nicht selbst geschrieben wurden oder deren Quelltext nicht vorliegt. Ohne Quelltext ist dies wesentlich schwieriger, aber nicht unmöglich. Solche Untersuchungen können unstatthaft oder illegal sein, etwa wenn Algorithmen analysiert werden, um sie in eigenen Programmen zu verwenden, Lizenzabfragen oder Kopierschutz umgangen oder ausgebaut werden oder Mehrspieler-Computerspiele manipuliert werden. Beispiele sind das Sichtbarmachen von Gegnern durch Wände oder das Verhindern von Schaden für die eigene Spielfigur.
Schutzmaßnahmen dagegen heißen Anti-Debugging-Methoden. Ein Programm kann versuchen festzustellen, ob ein Debugger vorhanden ist. Ein guter Debugger soll für das Programm jedoch möglichst transparent sein, weil das Debuggen die Programmausführung normalerweise nicht verändern soll. Hackerangriffe lassen sich dadurch nicht vollständig verhindern. Es bleibt eine Abwägung zwischen dem Sicherungsaufwand, dem vermuteten Aufwand zum Umgehen der Maßnahmen und möglichen wirtschaftlichen Nachteilen. Die Maßnahme muss außerdem geeignet sein, das illegitime Verfahren zu vermeiden oder zu verhindern. Das Geheimhalten von Funktionsweise und Quelltext, beispielsweise zur Erschwerung des Ausnutzens von Sicherheitslücken, heißt Security through obscurity und ist in der Fachwelt umstritten.