Was ist ein Debugger und wie kann er mir bei der Diagnose von Problemen helfen?

98

Dies soll eine allgemeine Frage sein, um neuen Programmierern zu helfen, die ein Problem mit einem Programm haben, aber nicht wissen, wie sie einen Debugger verwenden können, um die Ursache des Problems zu diagnostizieren.

Diese Frage umfasst drei Klassen spezifischerer Fragen:

  • Wenn ich mein Programm starte, erzeugt es nicht die Ausgabe, die ich für die Eingabe erwarte, die ich ihm gegeben habe.
  • Wenn ich mein Programm ausführe, stürzt es ab und gibt mir einen Stack-Trace. Ich habe die Stapelverfolgung untersucht , kenne aber die Ursache des Problems immer noch nicht, da die Stapelverfolgung mir nicht genügend Informationen liefert.
  • Wenn ich mein Programm ausführe, stürzt es aufgrund eines Segmentierungsfehlers (SEGV) ab.
Raedwald
quelle
3
Gute Arbeit - es wäre auch gut, eine entsprechende Frage und Antwort zum Debuggen von Techniken zu haben , z. B. mithilfe eines Debuggers, anderer Debug-Tools (z. B. Valgrind), strategischer Drucke, Stresstests, Teilen und Erobern usw.
Paul R
1
Ich stimme @PaulR zu, die FAQ sollte solche Sachen enthalten.
Nicu Stiurca

Antworten:

62

Ein Debugger ist ein Programm, das den Status Ihres Programms überprüfen kann, während Ihr Programm ausgeführt wird. Die technischen Mittel, die dazu verwendet werden, sind nicht wichtig, um die Grundlagen der Verwendung eines Debuggers zu verstehen. Sie können einen Debugger verwenden, um die Ausführung Ihres Programms anzuhalten, wenn es eine bestimmte Stelle in Ihrem Code erreicht, und dann die Werte der Variablen im Programm untersuchen. Sie können einen Debugger verwenden, um Ihr Programm sehr langsam auszuführen, eine Codezeile nach der anderen (als Einzelschritt bezeichnet ), während Sie die Werte seiner Variablen untersuchen.

Die Verwendung eines Debuggers ist eine erwartete Grundkompetenz

Ein Debugger ist ein sehr leistungsfähiges Tool zur Diagnose von Problemen mit Programmen. Und Debugger sind für alle praktischen Programmiersprachen verfügbar. Daher wird die Verwendung eines Debuggers als Grundkompetenz eines jeden professionellen oder begeisterten Programmierers angesehen. Und mit einem Debugger selbst wird als grundlegende Arbeit Sie tun sollten , sich vor anderen um Hilfe zu bitten. Da diese Site für professionelle und begeisterte Programmierer gedacht ist und keine Helpdesk- oder Mentoring-Site, wird Ihre Frage sehr wahrscheinlich geschlossen und abgelehnt, wenn Sie eine Frage zu einem Problem mit einem bestimmten Programm haben, aber keinen Debugger verwendet haben. Wenn Sie weiterhin solche Fragen haben, werden Sie möglicherweise daran gehindert, weitere Fragen zu stellen.

Wie ein Debugger Ihnen helfen kann

Mithilfe eines Debuggers können Sie feststellen, ob eine Variable den falschen Wert hat und wo in Ihrem Programm ihr Wert auf den falschen Wert geändert wurde.

Mit einem einzigen Schritt können Sie auch feststellen, ob der Kontrollfluss Ihren Erwartungen entspricht. Zum Beispiel, ob ein ifZweig ausgeführt werden soll, wenn Sie es erwarten.

Allgemeine Hinweise zur Verwendung eines Debuggers

Die Besonderheiten der Verwendung eines Debuggers hängen vom Debugger und in geringerem Maße von der von Ihnen verwendeten Programmiersprache ab.

  • Sie können einen Debugger an einen Prozess anhängen , auf dem Ihr Programm bereits ausgeführt wird. Sie können dies tun, wenn Ihr Programm nicht funktioniert.

  • In der Praxis ist es oft einfacher, Ihr Programm von Anfang an unter der Kontrolle eines Debuggers auszuführen.

  • Sie geben an, wo die Ausführung Ihres Programms beendet werden soll, indem Sie die Quellcodedatei und die Zeilennummer der Zeile angeben, in der die Ausführung gestoppt werden soll, oder indem Sie den Namen der Methode / Funktion angeben, bei der das Programm gestoppt werden soll (wenn Sie als anhalten möchten sobald die Ausführung in die Methode eingeht). Das technische Mittel, das der Debugger verwendet, um das Stoppen Ihres Programms zu bewirken, wird als Haltepunkt bezeichnet, und dieser Vorgang wird als Festlegen eines Haltepunkts bezeichnet .

  • Die meisten modernen Debugger sind Teil einer IDE und bieten Ihnen eine praktische Benutzeroberfläche zum Überprüfen des Quellcodes und der Variablen Ihres Programms sowie eine Point-and-Click-Oberfläche zum Festlegen von Haltepunkten, Ausführen Ihres Programms und Einzelschritt.

  • Die Verwendung eines Debuggers kann sehr schwierig sein, es sei denn, Ihre ausführbaren Programm- oder Bytecodedateien enthalten Debugging- Symbolinformationen und Querverweise auf Ihren Quellcode. Möglicherweise müssen Sie Ihr Programm etwas anders kompilieren (oder neu kompilieren), um sicherzustellen, dass Informationen vorhanden sind. Wenn der Compiler umfangreiche Optimierungen durchführt, können diese Querverweise verwirrend werden. Möglicherweise müssen Sie Ihr Programm daher mit deaktivierten Optimierungen neu kompilieren .

Raedwald
quelle
4
Dies ist unvollständig, da der wichtigste Debugger von allen fehlt, der die Anzahl der Fragen zu Stackoverflow erheblich reduzieren kann (ich prognostiziere um mindestens 20%) - Javascript-Debugger: Firebug, Chrome, Firefox, IE9 + integrierter Debugger , IE8 - Visual Studio usw.
Slebetman
1
Auch für node.js - Node Inspector. Die Programmierer von node.js stellen jedoch nicht so viele grundlegende und / oder Fix-my-Code-Fragen wie allgemeine Javascript-Programmierer.
Slebetman
38

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.

SlugFiller
quelle
9
Dies wäre eine gute Antwort ... auf eine andere Frage. Es ist eine schlechte Antwort auf diese Frage. Vielleicht sollten Sie diese Frage stellen und als Antwort darauf posten.
Raedwald
7
Die eigentliche Frage wird beschrieben als "Unterstützung neuer Programmierer, die ein Problem mit einem Programm haben", "es erzeugt nicht die erwartete Ausgabe" und "Ich habe die Stapelverfolgung untersucht, aber ich kenne die Ursache des Problems immer noch nicht". . All dies wird durch diese Antwort unterstützt. Wenn Sie erklären, was ein Debugger tut, ist es ebenso wichtig zu erklären, was er nicht tut.
SlugFiller
5
Gute Antwort. Ich habe den Debugger immer als Hauptwerkzeug verwendet, um Fehler zu finden. Aber jetzt arbeite ich in einem Projekt, in dem eine riesige Infrastrukturkomponente viele Threads und viel Netzwerkcode (Client / Server) verwendet, und stelle fest, dass der Debugger das Letzte ist, was mir hilft. Sie haben viele Dinge erwähnt, bei denen Sie wirklich ein anderes Tool verwenden sollten, anstatt sich auf Ihren guten alten Debugger zu verlassen (am wichtigsten: Protokollierung).
Tim Schmelter
"Sie können erkennen, dass Ihr Fehler durch falsche Werte bei Funktion X verursacht wird, aber Sie wissen nicht, woher diese Werte stammen." Dies ist besonders schwer zu debuggen. Wie geht man normalerweise vor, um so etwas zu reparieren?
Ayxan Haqverdili
2
@Ayxan Wenn Sie es bis zu einem gewissen Grad geschafft haben, die Funktion bei einem Assert zu unterbrechen, können Sie den Aufrufer mithilfe des Aufrufstapels abrufen. Dies allein gibt Ihnen jedoch nicht die Quelle des Werts an, da der Wert höchstwahrscheinlich aus einer früheren Zeile stammt. Grundsätzlich müssen Sie dem Wert durch die verschiedenen Variablen folgen, die er durchläuft. Wenn Sie eine gute Vorstellung von dem Pfad haben, den die Daten nehmen, können Sie einfach eine Reihe von Protokolldrucken erstellen und versuchen, einzugrenzen, wo es "schief geht". Wenn nicht, benötigen Sie grundsätzlich für jeden Schritt einen separaten Programmlauf (der den Fehler reproduziert).
SlugFiller