Wie kann man feststellen, wo sich der Fehler im Code befindet, der einen Segmentierungsfehler verursacht ?
Kann mein compiler ( gcc
) den Ort des Fehlers im Programm anzeigen?
Wie kann man feststellen, wo sich der Fehler im Code befindet, der einen Segmentierungsfehler verursacht ?
Kann mein compiler ( gcc
) den Ort des Fehlers im Programm anzeigen?
Antworten:
GCC kann das nicht, aber GDB (ein Debugger ) kann es sicher. Kompilieren Sie Ihr Programm mit dem -g
Schalter wie folgt:
gcc program.c -g
Dann benutze gdb:
$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
Hier ist ein nettes Tutorial, um Ihnen den Einstieg in GDB zu erleichtern.
Wo der Segfault auftritt, ist im Allgemeinen nur ein Hinweis darauf, wo "der Fehler, der ihn verursacht", im Code enthalten ist. Der angegebene Ort ist nicht unbedingt der Ort, an dem das Problem liegt.
bt
als Abkürzung für backtrace
.
Sie können es auch valgrind
versuchen: Wenn Sie installieren valgrind
und ausführen
valgrind --leak-check=full <program>
Anschließend wird Ihr Programm ausgeführt und Stack-Traces für alle Segfaults sowie für ungültige Speicherlese- oder -schreibvorgänge und Speicherlecks angezeigt. Es ist wirklich sehr nützlich.
--leak-check=full
wird nicht helfen, Segfaults zu debuggen. Es ist nur zum Debuggen von Speicherlecks nützlich.
Sie können auch einen Core-Dump verwenden und ihn dann mit gdb untersuchen. Um nützliche Informationen zu erhalten, müssen Sie auch mit dem -g
Flag kompilieren .
Wann immer Sie die Nachricht erhalten:
Segmentation fault (core dumped)
Eine Kerndatei wird in Ihr aktuelles Verzeichnis geschrieben. Und Sie können es mit dem Befehl überprüfen
gdb your_program core_file
Die Datei enthält den Status des Speichers, als das Programm abstürzte. Ein Core-Dump kann während der Bereitstellung Ihrer Software hilfreich sein.
Stellen Sie sicher, dass Ihr System die Größe der Core-Dump-Datei nicht auf Null setzt. Sie können es auf unbegrenzt einstellen mit:
ulimit -c unlimited
Vorsicht! dass Core Dumps riesig werden können.
Es gibt eine Reihe von Tools, die beim Debuggen von Segmentierungsfehlern helfen, und ich möchte mein Lieblingswerkzeug zur Liste hinzufügen: Address Sanitizers (häufig als ASAN abgekürzt) .
Moderne¹ Compiler werden mit dem praktischen -fsanitize=address
Flag geliefert , das einige Kompilierungs- und Laufzeitkosten hinzufügt, wodurch mehr Fehler überprüft werden.
Gemäß der Dokumentation umfassen diese Überprüfungen standardmäßig das Abfangen von Segmentierungsfehlern. Der Vorteil hierbei ist, dass Sie einen Stack-Trace erhalten, der der Ausgabe von gdb ähnelt, ohne das Programm jedoch in einem Debugger auszuführen. Ein Beispiel:
int main() {
volatile int *ptr = (int*)0;
*ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
#0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
#1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
#2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING
Die Ausgabe ist etwas komplizierter als die Ausgabe von GDB, aber es gibt Vorteile:
Es ist nicht erforderlich, das Problem zu reproduzieren, um eine Stapelverfolgung zu erhalten. Es reicht aus, das Flag während der Entwicklung zu aktivieren.
ASANs erfassen weit mehr als nur Segmentierungsfehler. Viele Zugriffe außerhalb der Grenzen werden abgefangen, selbst wenn dieser Speicherbereich für den Prozess zugänglich war.
¹ Das sind Clang 3.1+ und GCC 4.8+ .
Lucas 'Antwort zu Core Dumps ist gut. In meiner .cshrc habe ich:
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
um die Rückverfolgung durch Eingabe von 'core' anzuzeigen. Und der Datumsstempel, um sicherzustellen, dass ich mir die richtige Datei ansehe :(.
Hinzugefügt : Liegt ein Stapel Korruption Fehler, dann wird der Backtrace auf den Core - Dump angewandt wird oft Müll. In diesem Fall kann das Ausführen des Programms innerhalb von gdb gemäß der akzeptierten Antwort zu besseren Ergebnissen führen (vorausgesetzt, der Fehler ist leicht reproduzierbar). Achten Sie auch darauf, dass mehrere Prozesse gleichzeitig den Kern entleeren. Einige Betriebssysteme fügen die PID zum Namen der Kerndatei hinzu.
ulimit -c unlimited
Core Dumps zu aktivieren.
Alle oben genannten Antworten sind korrekt und werden empfohlen. Diese Antwort ist nur als letztes Mittel gedacht, wenn keiner der oben genannten Ansätze verwendet werden kann.
Wenn alles andere fehlschlägt, können Sie Ihr Programm jederzeit mit verschiedenen temporären Debug-Print-Anweisungen (z. B. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
) neu kompilieren, die über die Ihrer Meinung nach relevanten Teile Ihres Codes verteilt sind. Führen Sie dann das Programm aus und beobachten Sie, was der letzte Debug-Druck war, der kurz vor dem Absturz gedruckt wurde. Sie wissen, dass Ihr Programm so weit gekommen ist. Der Absturz muss also nach diesem Zeitpunkt stattgefunden haben. Fügen Sie Debug-Ausdrucke hinzu oder entfernen Sie sie, kompilieren Sie sie erneut und führen Sie den Test erneut aus, bis Sie ihn auf eine einzelne Codezeile eingegrenzt haben. An diesem Punkt können Sie den Fehler beheben und alle temporären Debug-Ausdrucke entfernen.
Es ist ziemlich langweilig, aber es hat den Vorteil, dass es fast überall funktioniert - das einzige Mal, wenn Sie aus irgendeinem Grund keinen Zugriff auf stdout oder stderr haben oder wenn der Fehler, den Sie beheben möchten, ein Rennen ist -Bedingung, deren Verhalten sich ändert, wenn sich das Timing des Programms ändert (da die Debug-Drucke das Programm verlangsamen und sein Timing ändern)