Ich möchte hinzufügen, dass ein Debugger nicht immer die perfekte Lösung ist und nicht immer die Lösung für das Debuggen sein sollte. In den folgenden Fällen funktioniert ein Debugger möglicherweise nicht für Sie:
- Der Teil Ihres Programms, der fehlschlägt, ist wirklich groß (schlechte Modularisierung vielleicht?) Und Sie sind sich nicht sicher, wo Sie mit dem Durchlaufen des Codes beginnen sollen. Das Durchlaufen all dessen kann zu zeitaufwändig sein.
- Ihr Programm verwendet viele Rückrufe und andere nichtlineare Flusssteuerungsmethoden, was den Debugger verwirrt, wenn Sie ihn durchlaufen.
- Ihr Programm ist multithreaded. Oder noch schlimmer, Ihr Problem wird durch eine Rennbedingung verursacht.
- Der Code, der den Fehler enthält, wird viele Male ausgeführt, bevor er ausfällt. Dies kann besonders problematisch in Hauptschleifen oder noch schlimmer in Physik-Engines sein, bei denen das Problem numerisch sein könnte. Selbst wenn Sie in diesem Fall einen Haltepunkt setzen, müssen Sie ihn einfach viele Male treffen, ohne dass der Fehler auftritt.
- Ihr Programm muss in Echtzeit ausgeführt werden. Dies ist ein großes Problem für Programme, die eine Verbindung zum Netzwerk herstellen. Wenn Sie einen Haltepunkt in Ihrem Netzwerkcode eingerichtet haben, wartet das andere Ende nicht darauf, dass Sie durchgehen, sondern es tritt lediglich eine Zeitüberschreitung auf. Programme, die auf der Systemuhr basieren, z. B. Spiele mit Frameskip, sind ebenfalls nicht viel besser dran.
- Ihr Programm führt eine Form von destruktiven Aktionen aus, z. B. das Schreiben in Dateien oder das Senden von E-Mails, und Sie möchten die Anzahl der Durchlaufvorgänge begrenzen.
- Sie können erkennen, dass Ihr Fehler durch falsche Werte bei Funktion X verursacht wird, aber Sie wissen nicht, woher diese Werte stammen. Das Programm immer wieder durchlaufen zu müssen und Haltepunkte immer weiter zurückzusetzen, kann ein großer Aufwand sein. Insbesondere, wenn die Funktion X an vielen Stellen im Programm aufgerufen wird.
In all diesen Fällen kann es zu mühsam sein, entweder das Programm abrupt zu stoppen, wenn die Endergebnisse abweichen, oder manuell nach der einen Zeile zu suchen, in der der Fehler verursacht wird. Dies kann gleichermaßen passieren, unabhängig davon, ob Ihr Fehler ein falsches Verhalten oder ein Absturz ist. Wenn beispielsweise eine Speicherbeschädigung einen Absturz verursacht, ist sie zum Zeitpunkt des Absturzes zu weit von der Stelle entfernt, an der die Speicherbeschädigung zum ersten Mal aufgetreten ist, und es bleiben keine nützlichen Informationen übrig.
Also, was sind die Alternativen?
Am einfachsten ist einfach die Protokollierung und Zusicherung. Fügen Sie Ihrem Programm an verschiedenen Stellen Protokolle hinzu und vergleichen Sie das, was Sie erhalten, mit dem, was Sie erwarten. Überprüfen Sie beispielsweise, ob die Funktion, bei der Sie glauben, dass ein Fehler vorliegt, überhaupt aufgerufen wird. Überprüfen Sie, ob die Variablen am Anfang einer Methode Ihren Vorstellungen entsprechen. Im Gegensatz zu Haltepunkten ist es in Ordnung, dass es viele Protokollzeilen gibt, in denen nichts Besonderes passiert. Sie können das Protokoll anschließend einfach durchsuchen. Wenn Sie eine Protokollzeile erreicht haben, die sich von Ihren Erwartungen unterscheidet, fügen Sie im selben Bereich weitere hinzu. Grenzen Sie es weiter und weiter ein, bis es klein genug ist, um jede Zeile im fehlerhaften Bereich protokollieren zu können.
Assertions können verwendet werden, um falsche Werte abzufangen, sobald sie auftreten, anstatt wenn sie einen für den Endbenutzer sichtbaren Effekt haben. Je schneller Sie einen falschen Wert erfassen, desto näher sind Sie der Linie, die ihn erzeugt hat.
Refactor und Unit Test. Wenn Ihr Programm zu groß ist, kann es sich lohnen, es jeweils für eine Klasse oder eine Funktion zu testen. Geben Sie ihm Eingaben und schauen Sie sich die Ausgaben an und sehen Sie, welche nicht Ihren Erwartungen entsprechen. Die Möglichkeit, einen Fehler von einem gesamten Programm auf eine einzelne Funktion einzugrenzen, kann einen großen Unterschied in der Debugging-Zeit bewirken.
Verwenden Sie bei Speicherlecks oder Speicherstampfen geeignete Tools, mit denen diese zur Laufzeit analysiert und erkannt werden können. Der erste Schritt besteht darin, zu erkennen, wo die tatsächliche Korruption auftritt. Danach können Sie mithilfe von Protokollen dorthin zurückkehren, wo falsche Werte eingeführt wurden.
Denken Sie daran, dass das Debuggen ein Prozess ist, der rückwärts geht. Sie haben das Endergebnis - einen Fehler - und finden die Ursache, die davor stand. Es geht darum, sich rückwärts zu arbeiten, und leider treten Debugger nur vorwärts. Hier können Sie mit einer guten Protokollierung und Postmortem-Analyse viel bessere Ergebnisse erzielen.