Welchen Prozess verwenden Sie normalerweise, wenn Sie versuchen, ein Problem / ein Problem / einen Fehler mit Ihrer Software zu beheben? [geschlossen]

15

Die meisten Menschen scheinen das Debuggen eher als Kunst denn als Wissenschaft zu betrachten. Für diejenigen hier, die es eher als Wissenschaft denn als Kunst betrachten - welche Prozesse wenden Sie normalerweise an, wenn Sie mit einem neuen Problem konfrontiert werden?

Blueberryfields
quelle

Antworten:

13

Ganz allgemein mache ich:

  1. Versuchen Sie, das Problem einzugrenzen. Denken Sie daran, was sich geändert hat, als der Fehler zum ersten Mal auftrat. An was arbeitest du? Welchen Teil des Codes haben Sie geändert? 99% meiner Fehler werden auf diese Weise behoben. Es ist normalerweise etwas Dummes.

  2. Wenn ich weiß, wo das Problem liegt, schauen Sie sich den Code an, der die Ursache zu sein scheint. Lies es. Lies es sogar vor. Fragen Sie sich: "Was versuche ich zu erreichen?" Für einige Arten von Problemen: Könnte es einige Nebenwirkungen haben oder könnte es durch Code an einer anderen Stelle auf eine Weise beeinflusst werden, an die ich nicht gedacht hatte?

  3. Versuchen Sie auf verschiedene Arten zu analysieren, was wo und wann schief geht (siehe unten).

  4. Wenn ich immer noch keine Ahnung habe, überprüfe ich, ob eine ältere Version meiner Quelle das gleiche Problem aufweist. Versuchen Sie herauszufinden, wann das Problem in meiner Entwicklungszeitleiste zum ersten Mal aufgetreten ist. Um dies zu tun, müssen Sie mit einem guten Versionskontrollsystem wie git arbeiten (git hat eine Funktion namens bisect, die genau für diese Art von Debugging geeignet ist).

  5. Wenn Sie immer noch keine Ahnung haben, machen Sie eine Pause ... es hilft tatsächlich oft.

  6. Kehren Sie zum Zeichenbrett zurück und überprüfen Sie, wie Ihr Programm funktionieren soll und ob dies tatsächlich Sinn macht.

Es hängt wirklich von der Art des Problems ab, aber wenn ich eine allgemeine Vorstellung davon habe, wo das Problem sein könnte, dann:

  • Wenn ich den Verdacht habe, dass das Problem in einem Teil des Codes / der letzten Änderung liegt, versuche ich zuerst, den Fehler durch Vereinfachung des Codes zu beseitigen / zu kommentieren / zu ändern oder was auch immer, und bringe dann den problematischen Code zurück und nehme a schau es dir gut an

  • Führen Sie einen Debugger mit Haltepunkten aus (falls überhaupt möglich) und überprüfen Sie, wie meine Daten aussehen, wenn sie sich schlecht verhalten, um eine bessere Vorstellung davon zu bekommen, wo etwas schief läuft.

sinelaw
quelle
1
+1 für eine Pause. Die schwierigsten Probleme werden nur dann schwerer, wenn Sie frustriert sind und sie in der 6. Stunde debuggen. Zu wissen, wann ich eine Pause einlegen soll, ist eine der nützlichsten Fähigkeiten zum Debuggen, die ich erworben habe.
Brad Gardner
Geniale Antwort. Ich kann es nicht besser machen.
EricBoersma
1
Ähnlich wie bei meinem Ansatz, aber Sie haben vergessen, wo Sie einen Kollegen bitten, einen kurzen Blick darauf zu werfen, und sie bemerken sofort den Rechtschreibfehler ...
ChrisAnnODell
1
Hervorragende Antwort. Ich möchte nur hinzufügen, dass eine Unze Prävention ein Pfund Heilung wert ist. Ein großer Teil meines Debugging-Prozesses besteht darin, dass ich während des Programmierens nur kleine, inkrementelle Änderungen vornehme und zwischen den einzelnen lokal kompiliere, teste und festschreibe. Wenn auf diese Weise plötzlich ein Fehler auftritt, ist die Liste der wahrscheinlichen Verdächtigen sehr klein und mit einem bzr qdiffBefehl leicht zu sehen .
Karl Bielefeldt
8

Ich versuche, testgetriebene Entwicklung ( TDD ) zu verwenden. Ich schreibe einen Test, der den Fehler reproduziert, und versuche dann, den Test zum Bestehen zu bringen. Manchmal hilft das Schreiben des Tests, den Fehler zu finden.

Das hält mich die meiste Zeit vom Debugger fern und bietet Regressionstests, um zu verhindern, dass der Fehler erneut auftritt.

Einige Links:

TrueWill
quelle
4
Ich denke, diese Antwort ist äußerst unvollständig. Ich verstehe nicht so viele Gegenstimmen.
Alex
1
Es gibt nur so viele positive Stimmen, weil es das magische Akronym enthält: TDD.
Bjarke Freund-Hansen
@Alex - Ich habe einige Links hinzugefügt. Die "Finden Sie einen Fehler, schreiben Sie einen Test" hat ein Beispiel. Darauf kann ich noch eingehen, aber es ist wirklich so einfach.
TrueWill
7

Es gibt eine Reihe von Definitionen für das Wort Wissenschaft, aber es hört sich so an, als würden Sie sich möglicherweise auf das beziehen, was man genauer als " wissenschaftliche Methode " bezeichnen könnte. Die wissenschaftliche Methode kann zusammengefasst werden, indem einige Phänomene beobachtet werden (vermutlich ein Fehler oder ein unerwartetes Programmverhalten), eine Hypothese oder Hypothesen formuliert werden, um das Verhalten zu erklären, und am wahrscheinlichsten experimentiert wird, um es zu beweisen (Schreiben eines Tests, der das Problem zuverlässig reproduziert).

Die Arten von Fehlern (Phänomenen), die auftreten können, sind praktisch endlos und einige erfordern nicht unbedingt einen genau definierten Prozess. Zum Beispiel beobachten Sie manchmal einen Fehler und wissen sofort, was ihn verursacht hat, einfach weil Sie mit dem Code sehr vertraut sind. In anderen Fällen wissen Sie, dass bei bestimmten Eingaben (Aktionen, Abfolgen von Schritten usw.) ein falsches Ergebnis auftritt (Absturz, schlechte Ausgabe usw.). In diesen Fällen erfordert es oft nicht viel "wissenschaftliches" Denken. Einige Überlegungen können helfen, den Suchraum zu verkleinern. Eine übliche Methode besteht jedoch darin, den Code in einem Debugger schrittweise durchzugehen und festzustellen, wo Fehler aufgetreten sind.

Die für mich interessantesten und möglicherweise eines wissenschaftlichen Prozesses würdigen Situationen sind jedoch die, in denen Ihnen ein Endergebnis ausgehändigt und Sie gebeten werden, zu erklären, wie es passiert ist. Ein offensichtliches Beispiel hierfür ist ein Crash-Dump. Sie können den Crash-Dump laden und den Status des Systems beobachten. Ihre Aufgabe besteht darin, zu erklären, wie es in diesen Status gelangt ist. Der Absturz- (oder Kern-) Speicherauszug kann eine Ausnahme, einen Deadlock, einen internen Fehler oder einen vom Benutzer definierten "unerwünschten" Zustand (z. B. Trägheit) anzeigen. In diesen Situationen folge ich im Allgemeinen den folgenden Schritten:

  • Eingeschränkte Beobachtung : Studieninformationen, die sich direkt auf das spezifische Problem beziehen, falls zutreffend. Die offensichtlichen Dinge hier sind der Aufrufstapel, die lokalen Variablen, wenn Sie sie sehen können, die Codezeilen, die das Problem umgeben. Diese Art der spezifischen Standortstudie ist nicht immer anwendbar. Zum Beispiel hat das Studium eines "langsamen" Systems möglicherweise keinen offensichtlichen Startort wie diesen, aber eine Absturz- oder interne Fehlersituation wird wahrscheinlich einen unmittelbaren und offensichtlichen Punkt von Interesse haben. Ein konkreter Schritt könnte darin bestehen, Tools wie windbg zu verwenden (führen Sie! Analyse -v auf einem geladenen Crash-Dump aus und sehen Sie sich an, was es Ihnen sagt).

  • Breite Beobachtung : Studieren Sie andere Teile des Systems. Überprüfen Sie den Status aller Threads im System, und überprüfen Sie alle globalen Informationen (Anzahl der Benutzer / Vorgänge / Elemente, aktive Transaktionen / Prozesse / Widgets usw.), Systeminformationen (Betriebssystem) usw. Wenn der Benutzer externe Details angegeben hat Denken Sie über die Zusammenhänge nach, die Sie beobachtet haben. Wenn Ihnen beispielsweise mitgeteilt wird, dass das Problem jeden Dienstagnachmittag auftritt, fragen Sie sich, was dies bedeuten könnte.

  • Hypothese: Dies ist der wirklich lustige Teil (und ich bin nicht scherzhaft darüber, dass es Spaß macht). Es erfordert oft viel logisches Umdenken. Es kann sehr unterhaltsam sein, darüber nachzudenken, wie das System in den aktuellen Zustand gelangt ist. Ich vermute, dass dies der Teil ist, den viele Leute für eine Kunst halten. Und ich nehme an, es könnte sein, dass der Programmierer nur zufällig Dinge darauf wirft, um zu sehen, was steckt. Aber mit der Erfahrung kann dies ein ziemlich gut definierter Prozess sein. Wenn Sie an dieser Stelle sehr logisch denken, ist es oft möglich, mögliche Sätze von Pfaden zu definieren, die zu dem gegebenen Zustand führten. Ich weiß, dass wir uns im Zustand S5 befinden. Damit dies geschehen konnte, musste S4a oder S4b auftreten und möglicherweise S3 vor S4a usw. Oftmals kann es mehrere Elemente geben, die zu einem bestimmten Zustand führen könnten. Manchmal kann es hilfreich sein, ein einfaches Ablauf- oder Zustandsdiagramm oder eine Reihe von zeitbezogenen Schritten auf ein Notizbuch zu schreiben. Die tatsächlichen Abläufe variieren in Abhängigkeit von der jeweiligen Situation erheblich, aber ernsthafte Überlegungen (und eine erneute Prüfung in den vorherigen Schritten) zu diesem Zeitpunkt liefern häufig eine oder mehrere plausible Antworten. Beachten Sie auch, dass ein äußerst wichtiger Teil dieses Schritts darin besteht, Dinge zu beseitigen, die unmöglich sind. Das Entfernen des Unmöglichen kann dazu beitragen, den Lösungsraum zu verkleinern (denken Sie daran, was Sherlock Holmes über die Überreste gesagt hat, nachdem Sie das Unmögliche beseitigt haben). Beachten Sie auch, dass ein äußerst wichtiger Teil dieses Schritts darin besteht, Dinge zu beseitigen, die unmöglich sind. Das Entfernen des Unmöglichen kann dazu beitragen, den Lösungsraum zu verkleinern (denken Sie daran, was Sherlock Holmes über die Überreste gesagt hat, nachdem Sie das Unmögliche beseitigt haben). Beachten Sie auch, dass ein äußerst wichtiger Teil dieses Schritts darin besteht, Dinge zu beseitigen, die unmöglich sind. Das Entfernen des Unmöglichen kann dazu beitragen, den Lösungsraum zu verkleinern (denken Sie daran, was Sherlock Holmes über die Überreste gesagt hat, nachdem Sie das Unmögliche beseitigt haben).

  • Experiment : Versuchen Sie in dieser Phase, das Problem auf der Grundlage der im vorherigen Schritt abgeleiteten Hypothesen zu reproduzieren. Wenn Sie im vorherigen Schritt ernsthaft darüber nachgedacht haben, sollte dies sehr einfach sein. Manchmal "betrüge" ich und ändere die Codebasis, um einen gegebenen Test zu unterstützen. Ich habe zum Beispiel kürzlich einen Sturz untersucht, von dem ich schlussfolgerte, dass er von einem Rennzustand herrührt. Um dies zu verifizieren, setze ich einfach einen Sleep (500) zwischen ein paar Codezeilen, damit ein anderer Thread zur "richtigen" Zeit seine schlechten Sachen erledigen kann. Ich weiß nicht, ob dies in der "echten" Wissenschaft erlaubt ist, aber es ist in Code, den Sie besitzen, durchaus vernünftig.

Wenn es Ihnen gelingt, es zu reproduzieren, sind Sie wahrscheinlich fast fertig (alles, was noch übrig ist, ist der einfache Schritt, es zu reparieren ... aber das ist für einen anderen Tag). Stellen Sie sicher, dass der neue Test in das Regressionstestsystem eingegeben wird. Und ich sollte darauf hinweisen, dass ich diese frühere Aussage darüber beabsichtigt habe, wie man es einfach macht, humorvoll zu sein. Das Finden und Implementieren einer Lösung kann umfangreiche Arbeiten erfordern. Ich bin der Meinung, dass das Beheben eines Fehlers nicht Teil des Debugging-Prozesses ist, sondern vielmehr eine Weiterentwicklung. Und wenn das Update überhaupt beteiligt ist, sollte es einige Design- und Überprüfungsarbeiten erfordern.

Mark Wilkins
quelle
Die meisten der Fehler, die ich gesehen habe, waren nicht zuverlässig reproduzierbar, und für die Teilmenge, die es gab, erforderte die Mehrheit nach der Reproduktion noch erhebliche Fehlerbehebungsarbeiten, bevor mit den Arbeiten zur Fehlerbehebung begonnen werden konnte. Auch wenn Sie sagen, anstatt "erfolgreich zu reproduzieren", "erfolgreich einen Unit-Test einzugrenzen, der den Fehler eindeutig auslöst", würde ich sagen, dass die Debug-Arbeit noch nicht abgeschlossen ist. Für mich ist das Debuggen vorbei, sobald ich beide einen Fix habe, den ich nachweisen kann, dass er das Problem behebt, und ich habe den zuverlässigen Beweis, dass mein Fix das ist, was die Dinge tatsächlich behebt.
blueberryfields
Ich bin damit einverstanden, dass es ziemlich viel Arbeit sein kann, es zu beheben. Ich habe in der Tat Sarkasmus in meinen Worten "einfacher Schritt, es zu beheben" verwendet, aber das kommt vom Typ her nicht sehr gut durch.
4

Versuchen Sie, den Testfall zu reduzieren. Wenn es klein genug ist, ist es normalerweise einfacher, den entsprechenden Code zu finden, der das Problem verursacht.

Es ist wahrscheinlich, dass ein neuer Check-in das Problem verursacht und der vorherige tägliche Build in Ordnung war. In diesem Fall sollte Ihnen Ihr Änderungsprotokoll aus der Quellcodeverwaltung bei der Entscheidung helfen, wen Sie fangen möchten.

Wenn Sie mit C / C ++ arbeiten, sollten Sie erwägen, valgrind oder purify auszuführen, um speicherbezogene Probleme zu isolieren.

Fanatic23
quelle
2

Der schwierigste Teil des Debuggens besteht darin, das Problem zu isolieren, insbesondere, wenn das Problem unter mehreren Ebenen vergraben ist. Am College habe ich Musikaufnahmen studiert und seltsamerweise gab es eine Studio-Elektronik-Klasse, die sich hier direkt bewirbt. Ich werde das Debuggen einer Studioumgebung als Beispiel für den systematischen Debugging-Prozess verwenden.

  1. Testen Sie Ihre Zähler. Unter Verwendung eines Testtons bei einer bekannten kalibrierten Spannung sollte das Messgerät "U" (Einheitsverstärkung) anzeigen. Übersetzung: Wenn Ihre Werkzeuge kaputt sind, können Sie sie nicht verwenden, um herauszufinden, was sonst noch falsch ist.
  2. Testen Sie jede Komponente / Verstärkungsstufe von Ende an rückwärts. Bei Verwendung desselben Testtons am Eingang der Bühne sollte sich am Ausgang der Bühne nichts ändern. Übersetzung: Indem wir jedes Objekt von der Ausgabe abwärts isolieren, bauen wir Vertrauen in unseren Code auf, bis wir die Stelle finden, an der es durcheinander kommt. Wenn Ihre Werkzeuge einige Ebenen benötigen, um das Problem zu signalisieren, müssen Sie wissen, dass die dazwischen liegenden Ebenen keinen Beitrag dazu leisten.

Das Debuggen von Code ist wirklich nicht so unterschiedlich. Das Debuggen ist viel einfacher, wenn der Code eine Ausnahme auslöst. Sie können von der Stapelverfolgung dieser Ausnahme aus rückwärts verfolgen und Haltepunkte an Schlüsselpositionen festlegen. Normalerweise direkt nach dem Festlegen einer Variablen oder in der Zeile, die die Methode aufruft, die die Ausnahme auslöst. Möglicherweise stimmen einer oder mehrere der Werte nicht. Wenn es nicht richtig ist (eine Null, wenn es nicht sein sollte, oder der Wert außerhalb des Bereichs liegt), müssen Sie herausfinden, warum es nicht richtig ist. Die Unterbrechungspunkte in einer IDE entsprechen elektronischen Testpunkten (die für die Prüfung des Stromkreises durch eine Messsonde ausgelegt sind).

Wenn ich erst einmal herausgefunden habe, wo mein eigentliches Problem liegt, schreibe ich einige Komponententests, um dies in Zukunft zu überprüfen.

Berin Loritsch
quelle
2

Angesichts der üblen Wanzen, die ich am späten Nachmittag nicht ausfindig machen kann, ist es meine effektivste Strategie, aufzustehen und ein paar Minuten wegzulaufen. In der Regel fließen bereits nach 30 Sekunden neue Ideen zu möglichen Fehlerquellen ein.

Jay
quelle
2

Für einen praktischeren Ansatz:

  1. Wenn der Fehler mit einer nicht behandelten Ausnahme zusammenhängt, sehen Sie sich den Stack-Trace an. NULL-Verweise, Index außerhalb der Grenzen usw. und Ihre eigenen definierten Ausnahmen sind die häufigsten. Sie können diesen Fehler einem Junior-Entwickler zuweisen, es ist wahrscheinlich einfach und eine gute Lernerfahrung.

  2. Wenn dies nicht auf allen Maschinen der Fall ist, liegt wahrscheinlich eine Art Race Condition / Threading-Problem vor. Es macht riesigen Spaß, diese aufzuspüren und Ihren gelangweilten Senior-Programmierer darauf abzustellen. Viel Protokollierung, gute Kenntnisse und gute Werkzeuge erledigen dies.

  3. Eine weitere große Klasse von Fehlern ist, wenn das Testteam oder die Kunden ein bestimmtes Verhalten nicht mögen. Sie mögen es beispielsweise nicht, dass Sie Benutzer-IDs anzeigen oder dass Sie bei der Suche keine automatische Vervollständigung erhalten. Dies sind echte Fehler, die ein besseres Produktmanagement und Entwickler mit einer breiteren Sichtweise in Betracht ziehen. Es sollte relativ kurze Zeit dauern, bis ein Entwickler dieses Problem behoben hat, wenn er das System unter Berücksichtigung der Erweiterung erstellt.

  4. 80% aller anderen Fehler werden behoben, indem ein gutes Protokollierungssystem vorhanden ist und genügend Informationen gesammelt werden, um sie zu beheben. Verwenden Sie die integrierte Ablaufverfolgung mit mehreren Ebenen komplexer Protokollierungssysteme wie Log4Net / Log4J

  5. Performance-Fehler sind eine eigene Kategorie. Die Regel lautet hier: "Erst messen, später beheben!" später nur 3-4% weniger Reaktionszeit.

Bogdan Gavril MSFT
quelle
Wenn ich jeden dieser 5 Punkte einzeln vergeben könnte, würde ich das tun!
jmort253
1

Ich habe zwei Ansätze:

  1. Teilen Sie gegebenes Problem in kleinere Teile und erobern Sie dann alle kleineren Teile, indem Sie dem Divide and ConquerParadigma folgen .
  2. Wenn ich Zweifel an Werten habe, drucke ich einfach die Werte der Variablen aus, um zu sehen, was genau in die Variable eingeht und was aus ihr herausgeht.

Diese Ansätze haben mir die meiste Zeit geholfen.

Rachel
quelle