Wie findet man die Zeile eines Absturzes?
Ein Debugger ist ein Werkzeug, das ein Programm anhält und zeigt, in welcher Zeile es steht und welche Werte seine Variablen haben. Mit gdb finden Sie die Zeile eines Absturzes, ohne überall printf einzufügen.
Eine Notenverwaltung rechnet zum Beispiel einen falschen Durchschnitt aus. Mit einem Haltepunkt sehen Sie die Werte genau dort, wo der Fehler entsteht.
Am Ende dieser Lektion
Notiz
Ich kann mit einem Debugger die Zeile finden, in der ein Programm abstürzt.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Debugger | ein Werkzeug, das ein Programm schrittweise untersucht | gdb |
| Haltepunkt | eine Stelle, an der das Programm anhält | break main |
| Backtrace | die Liste der gerade aktiven Funktionsaufrufe | bt |
| Signal | eine Meldung des Systems an einen Prozess | SIGSEGV |
| Absturz | ein unfreiwilliges Ende durch ein Signal | |
| Debuginformationen | Zeilen und Namen, die gcc mit der Option -g ablegt |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein Haltepunkt zeigt die Werte mitten im Lauf
Übersetzen Sie mit gcc -g, dann legt gcc Debuginformationen ab: Zeilennummern und Namen. Ohne sie kennt gdb nur Adressen.
Mit break durchschnitt setzen Sie einen Haltepunkt. gdb hält dort an, print zeigt Variablen, und next führt eine Zeile aus. Das Programm unten teilt 17 Punkte auf 4 Personen und gibt 4 aus statt 4,25.
Das Programm debug.c
#include <stdio.h>
int durchschnitt(int summe, int anzahl) {
int ergebnis = summe / anzahl;
return ergebnis;
}
int main(void) {
int punkte = 17;
int personen = 4;
printf("Durchschnitt %d\n", durchschnitt(punkte, personen));
return 0;
}
In den Arbeitsordner wechseln
mkdir -p ~/csys && cd ~/csysHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Programm in eine Datei schreiben
printf '#include <stdio.h>\nint durchschnitt(int summe, int anzahl) {\n int ergebnis = summe / anzahl;\n return ergebnis;\n}\nint main(void) {\n int punkte = 17;\n int personen = 4;\n printf("Durchschnitt %%d\\n", durchschnitt(punkte, personen));\n return 0;\n}\n' > debug.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Mit Debuginformationen übersetzen
gcc -g -O0 -o debug debug.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Anhalten, Werte lesen, eine Zeile weitergehen
gdb -batch -ex 'break durchschnitt' -ex run -ex 'print summe' -ex 'print anzahl' -ex next -ex 'print ergebnis' -ex bt ./debug 2>&1 | grep -E '^(#0|\$|Breakpoint 1,)'Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Beispiel: was die fünf Zeilen bedeuten
| Zeile der Ausgabe | Bedeutung |
|---|---|
| Breakpoint 1, durchschnitt (summe=17, anzahl=4) at debug.c:3 | Halt vor Zeile 3, mit den Parametern |
| $1 = 17 | Der erste print: summe |
| $2 = 4 | Der zweite print: anzahl |
| $3 = 4 | ergebnis nach next: 17 geteilt durch 4 ergibt ganzzahlig 4 |
| #0 durchschnitt (summe=17, anzahl=4) at debug.c:4 | Der Backtrace: jetzt steht das Programm in Zeile 4 |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein Absturz ist ein Signal an den Prozess
Passiert etwas Unmögliches, schickt das System dem Prozess ein Signal und beendet ihn. Das ist ein Absturz. gdb fängt das Signal ab und zeigt, wo es ankam.
Das Programm unten bricht mit assert selbst ab, weil personen 0 ist. Das ist wohldefiniert und ein sicheres Beispiel. Den Zugriff auf NULL oder die Division durch null führen wir nie aus, denn ihr Ergebnis ist in C undefiniert.
Ein Programm mit einer Prüfung schreiben
printf '#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\nint main(void) {\n int personen = 0;\n assert(personen > 0);\n return 0;\n}\n' > absturz.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Mit Debuginformationen übersetzen
gcc -g -O0 -o absturz absturz.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Den Absturz in gdb lesen
gdb -batch -ex run ./absturz 2>&1 | grep -E 'Assertion|SIGABRT'Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Beispiel: häufige Signale eines Absturzes
| Signal | Nummer | Bedeutung |
|---|---|---|
| SIGSEGV | 11 | ungültiger Zugriff auf den Speicher |
| SIGFPE | 8 | ein Rechenfehler, etwa Teilen durch null |
| SIGABRT | 6 | das Programm bricht sich selbst ab, etwa mit assert |
| SIGBUS | 7 | ein Zugriff, den die Hardware nicht erlaubt |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
So lesen Sie einen Absturzbericht
Ein Bericht hat zwei Teile. Die Zeile Program received signal nennt das Signal und damit die Art des Fehlers. Die Zeile #0 des Backtrace nennt Funktion, Datei und Zeile, in der es geschah.
Die Zeile #1 nennt den Aufrufer. So gehen Sie rückwärts, bis Sie den Wert finden, der falsch war.
Ein häufiger Fehler
Warnung
Viele glauben, die Zeile des Absturzes enthalte den Fehler. Oft wurde der falsche Wert schon früher gesetzt, und erst später kracht es. Gehen Sie mit bt zum Aufrufer und prüfen Sie mit print, wo der Wert zum ersten Mal falsch ist.