Lernen Sie Ihren Debugger
Es ist sehr hilfreich, sich mit dem Debugger vertraut zu machen, egal ob es sich um eine textbasierte, vollständige IDE oder eine Mischung daraus handelt. Sie geben nicht viele Details an, deshalb beschreibe ich den allgemeinen Fall:
1) Haltepunkte
Bei vielen Debuggern können Sie nicht nur an einer Codezeile anhalten, sondern auch festlegen, dass eine Unterbrechung auftreten soll, wenn eine Bedingung auftritt (z. B. "x> 5"), nachdem der Code mehrere Male durchlaufen wurde oder wenn sich der Wert eines Speichers ändert. Dies ist sehr nützlich, um zu verstehen, wie Ihr Code in einen schlechten Zustand gerät, z. B. um zu beobachten, wann ein Zeiger null wird, anstatt den Absturz zu fangen, wenn er dereferenziert wird.
2) Code durchgehen
Sie können zeilenweise entlang des Codes in Funktionen eintreten, über Zeilen springen ('nächste Anweisung setzen') und dann die Funktionen verlassen. Es ist eine wirklich leistungsstarke Methode, um die Codeausführung zu verfolgen und zu überprüfen, ob sie das tut, was Sie denken :-)
3) Ausdrücke auswerten
Sie können also Variablen in eine Überwachungsliste / ein Überwachungsfenster einfügen und sehen, wie sich ihr Wert ändert, wenn Sie einen Haltepunkt erreichen oder Code durchlaufen. In der Regel können Sie aber auch komplexe Ausdrucksauswertungen durchführen, z. B. wird "x + y / 5" ausgewertet. Bei einigen Debuggern können Sie auch Funktionsaufrufe in die Überwachungslisten aufnehmen. Sie können Dinge wie "time ()", "MyFunction (...)", "time ()" ausführen und die Uhrzeit ermitteln, wie lange Ihre Funktion gedauert hat.
4) Ausnahmen und Signalbehandlung
Wenn Ihre Sprache Ausnahmen und / oder Signale unterstützt, können Sie den Debugger normalerweise so konfigurieren, dass er darauf reagiert. Bei einigen Debuggern können Sie an dem Punkt in den Code einbrechen, an dem die Ausnahme auftreten wird, und nicht, nachdem sie nicht abgefangen wurde. Dies ist nützlich, um seltsame Probleme wie "Datei nicht gefunden" -Fehler aufzuspüren, da das Programm als anderes Benutzerkonto ausgeführt wird.
5) Anhängen an einen Prozess / Kern
Manchmal müssen Sie den Debugger verwenden, um auf einen vorhandenen Prozess zu springen, der schief läuft. Wenn Sie Quellcode in der Nähe haben und die Debug-Symbole intakt sind, können Sie eintauchen, als hätten Sie überhaupt erst im Debugger begonnen. Dies ist auch für Core-Dumps ähnlich, außer dass Sie das Debuggen in diesen normalerweise nicht fortsetzen können (der Prozess ist bereits beendet).
Konfiguration erstellen
Es gibt eine Reihe von Build-Variationen, die Sie durch Ein- oder Ausschalten von Funktionen wie Debug-Symbolen, Optimierungen und anderen Compiler-Flags vornehmen können:
1) Debuggen
Traditionell ist dies ein einfacher Build ohne besondere Eigenschaften, der das Debuggen einfach und vorhersehbar macht. Es variiert ein wenig je nach Plattform, es kann jedoch zusätzlichen Spielraum geben, z. B. Zuweisungen und Puffergrößen, um die Zuverlässigkeit zu gewährleisten. Normalerweise ist ein Compilersymbol wie DEBUG oder Conditional ("Debug") vorhanden, sodass debugspezifischer Code eingezogen wird. Dies ist häufig der Build, der mit intakten Symbolen auf Funktionsebene ausgeliefert wird, insbesondere wenn Zuverlässigkeit und / oder Wiederholbarkeit a sind Besorgnis, Sorge.
2) Release / Optimierter Build
Durch das Aktivieren von Compiler-Optimierungen können einige Funktionen zur Codegenerierung auf niedriger Ebene im Compiler basierend auf Annahmen über Ihren Code schnelleren oder kleineren Code erstellen. Die möglichen Geschwindigkeitssteigerungen sind irrelevant, wenn Ihre Algorithmusauswahl schlecht ist. Bei intensiven Berechnungen kann dies jedoch durch Common Subexpression Elimination und Loop Unrolling usw. einen ausreichenden Unterschied bewirken. Manchmal sind die vom Optimierer getroffenen Annahmen nicht mit Ihrem Code und damit dem Optimierer kompatibel muss eine Kerbe runtergekurbelt werden. Compiler-Fehler in optimiertem Code waren in der Vergangenheit ebenfalls ein Problem.
3) Instrumentierter / profilierter Build
Ihr Code wird mit einem bestimmten Instrumentierungscode erstellt, um zu messen, wie oft eine Funktion aufgerufen wird und wie lange sie in dieser Funktion verbracht wird. Dieser vom Compiler generierte Code wird am Ende des Analyseprozesses ausgeschrieben. Manchmal ist es einfacher, dafür ein spezielles Software-Tool zu verwenden - siehe unten. Diese Art von Build wird niemals ausgeliefert.
4) Sicherer / geprüfter Build
Alle 'Sicherheitsventile' werden über Präprozessorsymbole oder Compilereinstellungen aktiviert. Beispielsweise überprüfen ASSERT-Makros Funktionsparameter, Iteratoren prüfen auf nicht geänderte Sammlungen, Kanarienvögel werden in den Stapel gelegt, um Beschädigungen zu erkennen, Heap-Zuordnungen werden mit Sentinel-Werten gefüllt (0xdeadbeef ist ein denkwürdiger Wert), um Heap-Beschädigungen zu erkennen. Für Kunden mit anhaltenden Problemen, die nur auf ihrer Website reproduziert werden können, ist dies eine praktische Sache.
5) Feature-Build
Wenn Sie unterschiedliche Kunden haben, die unterschiedliche Anforderungen an Ihr Softwareprodukt haben, ist es üblich, für jeden Kunden einen Build zu erstellen, der die verschiedenen Teile beim Testen ausführt. Beispielsweise möchte ein Kunde Offline-Funktionen und ein anderer nur Online. Es ist wichtig, beide Möglichkeiten zu testen, wenn der Code unterschiedlich aufgebaut ist.
Protokollierung und Ablaufverfolgung
Es gibt also einige hilfreiche Anweisungen für printf () und anschließend umfassende, strukturierte Trace-Informationen für Datendateien. Diese Informationen können dann abgebaut werden, um das Laufzeitverhalten / die Laufzeitmerkmale Ihrer Software zu verstehen. Wenn Ihr Code nicht abstürzt oder die Reproduktion einige Zeit in Anspruch nimmt, ist es hilfreich, ein Bild von z. B. einer Liste von Threads, deren Statusübergängen, Speicherzuordnungen, Poolgrößen, freiem Speicher, Anzahl der Dateihandles usw. zu haben hängt wirklich von der Größe, Komplexität und Leistungsanforderungen Ihrer Anwendung ab. Als Beispiel möchten Spieleentwickler jedoch sicherstellen, dass die CPU- oder Speicherauslastung während eines laufenden Spiels keine "Spitzen" aufweist, da dies wahrscheinlich die Framerate beeinflusst. Einige dieser Informationen werden vom System verwaltet, andere von Bibliotheken und der Rest vom Code.
Andere Werkzeuge
Es ist nicht immer so, dass Sie einen anderen Build erstellen müssen, um diese Szenarien abzudecken: Einige Aspekte können zur Laufzeit durch Prozesskonfiguration (Windows-Registrierungstricks) ausgewählt werden, wodurch alternative Bibliotheken mit höherer Priorität für die Standardbibliotheken verfügbar gemacht werden, z. B. efence on your Loader-Pfad oder Verwendung eines Software-ICE oder eines speziellen Debuggers, um Ihre Software auf Laufzeitmerkmale zu prüfen (z. B. Intel v-Tune). Einige davon kosten viel Geld, andere sind kostenlose Xcode-Tools.