Begriff
Debugger
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Dein Programm stürzt ab.
Anfänger: Bauen überall print("Hier bin ich") und print(variable) ein.
Profi: Nutzt einen Debugger.
Er erlaubt dir, das Programm mitten im Lauf anzuhalten (Breakpoint).
Die Zeit steht still.
Du kannst dich umsehen: "Welchen Wert hat Variable X gerade?"
Du kannst Schritt für Schritt weitergehen (Step Over).
Es ist wie eine Zeitlupe für Code.
Merksatz: Ein Werkzeug, mit dem Entwickler ein Programm Schritt für Schritt ausführen, den internen Zustand (Variablen, Speicher) untersuchen und Fehler (Bugs) lokalisieren können.
Die wichtigsten Buttons:
- Breakpoint (Roter Punkt): "Halt hier an!"
- Continue (Play): "Lauf weiter bis zum nächsten Breakpoint."
- Step Over: "Führe die nächste Zeile aus (aber geh nicht in Funktionen rein)."
- Step Into: "Geh in diese Funktion rein."
- Step Out: "Führe den Rest der aktuellen Funktion aus und geh raus."
1. Call Stack (Der Pfad)
Der Debugger zeigt dir nicht nur, wo du bist, sondern wie du hergekommen bist.
main() -> calculate() -> add() -> ERROR.
Das ist der Call Stack.
Lebenswichtig, um zu verstehen, wer die Funktion mit den falschen Daten aufgerufen hat.
2. Conditional Breakpoints
Du hast eine Schleife, die 10.000 Mal läuft. Der Fehler passiert nur beim 9999. Mal.
Du willst nicht 9999 Mal "Continue" klicken.
Lösung: Bedingter Breakpoint.
"Halte nur an, wenn i == 9999 ist."
3. Watch Expressions
Du kannst dem Debugger sagen: "Behalte mal den Wert von user.name im Auge."
Er zeigt dir eine Liste live an.
Noch cooler: Du kannst Werte im Debugger ändern.
"Was passiert, wenn x jetzt null wäre?" -> Einfach überschreiben und weiterlaufen lassen.
Man kann Bugs fixen, ohne den Code neu zu starten (Hot Patching).
1. Ptrace und CPU-Interrupts (Wie friert man Zeit ein?)
Wie zum Teufel zwingt der Debugger einen laufenden kompilierten C++ Prozess auf CPU-Ebene hart in den Stillstand?
Softwareseitig existiert in Unix und Linux der System-Call ptrace (Process Trace). Ein Elternprozess (der Debugger) hängt sich invasiv in einen Gast ("attach").
Wenn der Programmierer einen Breakpoint auf Zeile 42 anlegt, schaut der Debugger in seine Symbol Tabelle, findet die exakte RAM-Adresse des Befehls und überschreibt hinterhältig im RAM exakt dort den echten Maschinencode mit einer winzigen Falle: In x86 Assembly nennt man das den %int 3 Opcode (0xCC). Ein purer Interrupt. Sobald der CPU-Sheduler diesen Befehl frisst, feuert er einen Hardware-Interrupt Trap. Der Execution State friert augenblicklich in Silizium ein. Der Kernel wacht auf, wirft das Kommando an den Ptrace-Debugger ("Dein Kind ist in die Falle getappt, was jetzt?"), und die IDE auf Windows blitzt auf. Wenn man "Continue" drückt, pflastert der Debugger den Original-Code zurück, schiebt den Zeiger (Instruction Pointer RAM Register) einen Schritt zurück, und re-startet den Stream.
2. Time-Travel Debugging (RR & OMNI)
Es ist ein Fluch, in einem Breakpoint "Step Over" gedrückt zu haben, und die wichtige Zeile verpasst zu haben. Man muss mühselig das Programm vom Reset her Neustarten.
Der Stand der absoluten Königsklasse ist "Record and Replay" (z.B. der Mozilla rr Debugger oder WinDbg TTD).
Er zeichnet absolut jede noch so kleine Input/Output-Systemaktion des Programmes hochkomprimiert aus dem Linux Kernel auf Bandbreite auf (Network Calls, Thread-Sheduling Kontext-Switches, rand()).
Das Resultat ist eine deterministische Replay-Aufzeichnung ("Trace"). Spielt man sie dem Debugger vor, verhält sich das Problem zu 100% reproduktions-sicher, inklusive aller Memory-Races. Und Magie: Man kann im Debugger endlich einen "Reverse-Continue"-Button (Rückwärts an den letzten Breakpoint In-Time reisen) drücken.
3. Symbol Files (.pdb, .dSYM)
Setzt du im Visual Studio Code einen Breakpoint auf main.go Zeile 10, starrt der GCC oder LLVM Compiler vs. Interpreter dich nur entsetzt an. Er weiß nicht was Zeile 10 oder user_mail ist – die CPU rechnet nur in Hexadezimal-Offsets und Memory-Pointern.
Die Brücke ist das Debug Symbol File (.pdb in Windows, .dSYM auf Mac OSX, oder DWARF Sektion im Linux ELF Format).
Die Symbols sind quasi die Enzyklopädie: Memory Addresse 0x510 entspricht Datei main.go, Zeile 10, Variable "User", Datentyp 32-Bit-String, Start Adresse Pointer 55.
Quick-Check
Heisenbug?
Ein Bug, der verschwindet, wenn man ihn debuggt. (Weil der Debugger das Timing ändert). Extrem nervig bei Race Conditions.Remote Debugging?
Der Code läuft auf dem Server, der Debugger auf deinem Laptop. Sie reden übers Netzwerk. Standard bei Java und Node.js.Rubber Duck Debugging?
Kein Tool, sondern eine Methode. Erkläre dein Problem laut einer Quietscheente. Oft findet man den Fehler dabei selbst, weil man gezwungen ist, logisch zu denken.