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

Aug 19 2014

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.

Antworten

68 Raedwald Aug 19 2014 at 20:49

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 .

39 SlugFiller Apr 05 2015 at 19:15

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 das plötzliche Stoppen Ihres Programms zu unterschiedlichen Endergebnissen führen, oder das manuelle Durchsuchen der einen Zeile, in der der Fehler verursacht wird, ist zu mühsam. 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.