- 1 Section
- 10 Lessons
- unbegrenzt
- Debugging & Fehleranalyse10
- 1.1Fehlertypen: Syntaxfehler, Laufzeitfehler, Logikfehler
- 1.2Systematische Fehlersuche: Duck-Debugging, Divide & Conquer
- 1.3Debugger-Grundkonzepte: Breakpoints, Watches, Call Stack
- 1.4Debugging in Java mit IntelliJ / Eclipse
- 1.5Debugging in Python mit VS Code und pdb
- 1.6Debugging in JavaScript: Browser DevTools
- 1.7Logging: Fehler protokollieren und auswerten
- 1.8Fehler in Datenbankabfragen finden
- 1.9Fehler in Netzwerkverbindungen und APIs debuggen
- 1.10Praxisaufgaben: Fehlerhafte Programme korrigieren
Debugger-Grundkonzepte: Breakpoints, Watches, Call Stack
In L2 hast du die Methodik gelernt – jetzt geht's um das wichtigste Werkzeug das diese Methodik elektronisch unterstützt: den Debugger. Ein Debugger ist ein Programm das ein anderes Programm kontrolliert ausführen kann: Schritt für Schritt, mit Pause-Möglichkeit, mit Einblick in jede Variable. Wer das nicht beherrscht, debuggt mit print()-Statements – funktioniert auch, aber zehnmal langsamer.
Diese Lektion zeigt die Sprach-unabhängigen Konzepte: Breakpoints, Watches, der Call Stack, Step-Befehle. Die nächsten Lektionen vertiefen das pro Sprache: Java mit IntelliJ, Python mit VS Code/pdb, JavaScript im Browser. Die Grundlagen die du hier lernst, gelten aber überall – sogar in einem GDB für C, einem LLDB für Swift, einem Delve für Go.
1) Was ist ein Debugger?
Stell dir vor du würdest einen Python-Film nicht in normaler Geschwindigkeit ansehen, sondern Bild für Bild, mit Pause-Knopf. Bei jedem Stop kannst du jede Variable inspizieren, dir den Aufrufpfad anschauen, sogar Werte ändern und weiterlaufen. Das ist ein Debugger.
Technisch: der Debugger ist ein Programm das deinen Code unter sich startet und dabei besondere Privilegien hat. Er kann an beliebigen Stellen anhalten, den Memory inspizieren, Stack-Frames untersuchen. Im Hintergrund nutzt er Mechanismen wie INT-3-Instruktionen (CPU-Level) oder Tracing-APIs (Sprach-Runtime-Level).
Drei Hauptanwendungsfälle:
- Bug suchen: wo geht der Code falsch? Was sind die Variablenwerte an einer kritischen Stelle?
- Verstehen: wie verhält sich fremder Code wirklich? Schritt-für-Schritt-Durchgang zeigt was wirklich passiert
- Hypothese testen: die Variable enthält ja vielleicht einen leeren String. Mit Debugger sofort verifizierbar
2) Die Grundkonzepte im Überblick
Bevor wir tief einsteigen, hier die vier Konzepte die jeder Debugger hat – egal ob IntelliJ, VS Code, GDB oder Chrome DevTools:
x = calc(5) ist, gehen wir in calc rein.i > 100". Pflichtfeature für Schleifen-Bugs.3) Interaktives Debugger-Mockup
Bevor wir alle Konzepte einzeln erklären: schau dir einen vollständigen Debugger an. Wir debuggen eine kleine Funktion, die den Durchschnitt einer Zahlenliste berechnen soll. Klick durch die Schritte und beobachte wie sich Variablen-Werte, Call Stack und die aktive Zeile verändern:
total + numbers[i] – aber numbers[i] sollte addiert werden, total wird nie weiterverwendet (Bug: kein +=!).4) Breakpoints im Detail
Ein Breakpoint sagt dem Debugger: „Halte das Programm an, sobald diese Zeile erreicht wird." Du setzt ihn meistens durch Klick links neben der Zeilennummer – ein roter Punkt erscheint. Beim Programmstart läuft alles normal, bis der Punkt erreicht ist. Dann steht das Programm still und du kannst alles inspizieren.
Es gibt verschiedene Breakpoint-Arten:
- Standard-Breakpoint: immer anhalten
- Conditional Breakpoint: nur anhalten wenn eine Bedingung erfüllt ist, z.B.
i == 100. Pflicht für Schleifen-Bugs – ohne das müsstest du in einer Schleife mit 1000 Iterationen 100x „Continue" drücken - Logpoint (auch „Tracepoint" oder „Action Breakpoint"): hält NICHT an, sondern loggt einen Wert. Wie ein temporäres
print()ohne Code-Änderung. Sehr nützlich! - Exception Breakpoint: hält automatisch bei einer Exception – egal wo. Praktisch um den Crash-Ort sofort zu finden
- Method Breakpoint: hält bei jedem Aufruf einer bestimmten Methode, egal wo sie aufgerufen wird
- Watchpoint (auch „Field-Breakpoint"): hält wenn eine bestimmte Variable verändert wird. Sehr nützlich um zu finden „wer hat den Wert kaputt gemacht?"
Wichtige Regel: Breakpoints sind nicht für die Ewigkeit. Setze sie temporär, lösche sie wieder. Wer 20 Breakpoints liegen lässt, blockiert sich selbst beim nächsten Debug.
5) Watches – Variablen beobachten
Während das Programm pausiert ist, willst du wissen was die Variablen enthalten. Dafür gibt es im Debugger das Variables- oder Watches-Panel. Es zeigt automatisch alle lokalen Variablen der aktuellen Funktion, plus von dir hinzugefügte Ausdrücke.
Drei Arten von Beobachtungen:
- Lokale Variablen: automatisch sichtbar, alle Variablen der aktuellen Funktion
- Watch-Expressions: explizit gewählter Ausdruck. Beispiel:
user.profile.age > 18. Der Debugger evaluiert das bei jedem Stopp neu - Inline-Werte: moderne IDEs zeigen Werte direkt neben dem Code („total = 60") – sehr praktisch
Bei komplexen Objekten kannst du sie aufklappen und durch die Struktur navigieren. Bei einem User-Objekt siehst du dann user.name, user.email, user.profile und so weiter. Bei Listen siehst du die Elemente. Geänderte Werte werden in vielen IDEs farblich hervorgehoben – grün für „neu/verändert", rot für „gelöscht".
6) Der Call Stack
Eine der unterschätztesten Funktionen: der Call Stack. Wenn du an einem Breakpoint angehalten hast, zeigt er dir die Aufrufkette: wer hat diese Funktion aufgerufen? Und wer den Aufrufer? Bis hinauf zum main.
Analogie: Stell dir vor du gehst in einem Hochhaus Treppen hoch. Der Call Stack ist deine „Wo bin ich gerade?"-Anzeige. Du bist in der 5. Etage in Raum 203. Vorher warst du in 4. Etage Raum 100, davor in 3. Etage Raum 17. Diese Geschichte deines Weges – das ist der Stack.
Drei Anwendungsfälle:
- Exception verstehen: bei einem Crash zeigt die Stack Trace wie es zum Crash kam. Diese Information ist Gold wert für Diagnose. Mehr in der Fehlertypen-Übersicht in L1
- Rückwärts schauen: am Breakpoint kannst du auf einen Frame im Stack klicken und siehst die lokalen Variablen dort. Praktisch wenn du wissen willst: „mit welchen Werten wurde die Funktion eigentlich aufgerufen?"
- Rekursion verstehen: bei rekursiven Funktionen siehst du jeden Selbst-Aufruf als eigenen Stack-Frame. Sehr instruktiv zum Verstehen.
Bei einem StackOverflowError gibt's übrigens nicht „ein paar" Frames – sondern Tausende, weil die Rekursion endlos lief. Das ist genau der Punkt.
7) Stepping – die fünf Arten
Wenn du am Breakpoint stehst, willst du das Programm meistens nicht einfach weiterlaufen lassen – sondern kontrolliert Schritt für Schritt ausführen. Dafür gibt es verschiedene „Step"-Befehle:
F8 / F10
x = calc(5) stehst, läuft calc komplett durch, du landest auf der nächsten Zeile. Häufigster Step-Befehl im Alltag.F7 / F11
x = calc(5) landest du in der ersten Zeile von calc. Praktisch wenn du wissen willst was calc drinnen wirklich tut.Shift+F8
F9 / F5
Cursor
8) Conditional Breakpoints – die Spezialwaffe
Bei einer Schleife mit tausend Iterationen will man nicht jede stoppen. Beispiel: ein Bug tritt nur bei Iteration #487 auf. Mit einem Conditional Breakpoint sagst du: „Stoppe nur wenn i == 487" oder „Stoppe nur wenn user.name == null".
So setzt man das in den meisten IDEs:
- Breakpoint setzen (Klick am Rand)
- Rechtsklick auf den roten Punkt → „Condition..." oder „Edit Breakpoint"
- Bedingung eingeben:
i == 487 - Programm laufen lassen – stoppt nur bei der gewünschten Iteration
Ein paar nützliche Bedingungs-Patterns:
i == 100– konkrete Iterationuser == null– nur bei Null-Wertenprice < 0– nur bei Anomalienlist.size() > 1000– nur bei großen Datenmengenuser.id == 42 && user.role == "admin"– komplexe Bedingungen
Pro-Tipp: viele IDEs erlauben auch Hit-Count-Bedingungen: „Erst beim 50. Erreichen stoppen". Oder „Jedes 10. Mal stoppen". Sehr mächtig.
9) Hot Code Replacement
Eine moderne Debugger-Funktion: während das Programm pausiert, kannst du Code ändern und der Debugger lädt die Änderung sofort ein. Das nennt sich Hot Code Replacement oder Edit and Continue. In IntelliJ/Java und Chrome DevTools sehr ausgereift, in Python begrenzt funktional.
Nutzen: du kannst Hypothesen testen ohne Neustart. „Was wäre wenn ich hier +1 einbaue?" → Code ändern, weiterlaufen, sehen. Limitierungen: nicht alle Änderungen sind hot-replacebar (z.B. Klassen-Struktur), und der laufende Programm-Zustand bleibt – also keine Wirkung „retrospektiv".
10) Set Value / Evaluate Expression
Zwei mächtige Power-User-Funktionen die viele übersehen:
Set Value: am Breakpoint kannst du eine Variable direkt setzen. Statt das Programm neu zu starten mit anderen Eingaben, sagst du im Debugger: „Variable user.age jetzt 65." Programm läuft weiter mit diesem Wert. Perfekt für „Was passiert wenn..."-Tests.
Evaluate Expression: im Debug-Kontext kannst du beliebigen Code ausführen. „Wie groß ist diese Liste?" → list.size() evaluieren. „Wie sieht der String aus?" → str.toLowerCase().trim(). Praktisch um ad-hoc Erkundungen zu machen ohne neue print-Statements ins Programm einzubauen.
11) Debugging vs. Logging
Eine wichtige Frage: wann nimmst du den Debugger, wann Logging?
| Situation | Tool | Warum |
|---|---|---|
| Bug reproduzierbar, lokal | Debugger | Interaktiv, schnell, kein Code-Change |
| Bug nur in Production | Logging | Debugger geht oft nicht aus der Ferne |
| Bug betrifft viele Stellen | Logging | Du siehst auf einen Blick die Sequenz |
| Bug bei seltener Eingabe | Conditional Breakpoint | Stoppt nur unter den richtigen Bedingungen |
| Performance-Problem | Profiler | Debugger verfälscht Zeitmessungen |
| Race Condition / Threading | Logging + manchmal Profiler | Debugger verändert das Timing |
Beide haben ihre Berechtigung. Ein guter Entwickler nutzt beide flüssig. In Production gewinnt fast immer Logging – kein Debugger an Live-Servern. In Dev/Lokal gewinnt der Debugger – schneller und tieferer Einblick.
Zusammenfassung
Ein Debugger führt dein Programm kontrolliert aus – mit Pause-Möglichkeit, Variablen-Einblick, Step-für-Step-Durchgang. Die vier Säulen: Breakpoints (rote Anhaltepunkte), Watches (Variablen beobachten), Call Stack (Aufrufkette), Stepping (Schritt-Befehle). Breakpoint-Typen: Standard, Conditional (mit Bedingung), Logpoint (loggt statt anzuhalten), Exception-BP, Method-BP, Watchpoint (bei Variablen-Änderung). Step-Befehle: Step Over (Funktion komplett), Step Into (rein), Step Out (raus), Continue (weiter), Run to Cursor (bis zum Cursor). Der Call Stack zeigt die Aufrufkette – wichtig bei Exceptions und Rekursionen. Conditional Breakpoints sind Pflicht für Schleifen-Bugs. Set Value und Evaluate Expression erlauben Live-Manipulation. Hot Code Replacement erlaubt Code-Änderungen ohne Neustart. Debugger vs. Logging: Debugger lokal+interaktiv, Logging für Production+breite Streuung+Performance-sensitive Bereiche. Konzepte sind sprachunabhängig: nächste Lektionen zeigen Java/IntelliJ, Python/VS Code, JavaScript/DevTools.
