Crash-Dump, Memory-Dump, Core-Dump, System-Dump … alle erzeugen das gleiche Ergebnis:eine Datei, die den Zustand des Speichers einer Anwendung zu einem bestimmten Zeitpunkt enthält – normalerweise, wenn die Anwendung abstürzt.
Zu wissen, wie man mit diesen Dateien umgeht, kann Ihnen helfen, die Grundursache(n) eines Fehlers zu finden. Selbst wenn Sie kein Entwickler sind, können auf Ihrem System erstellte Dump-Dateien sehr hilfreich (und zugänglich) sein, um Software zu verstehen.
Dies ist ein praxisorientierter Artikel. Sie können dem Beispiel folgen, indem Sie das Beispielanwendungs-Repository klonen mit:
git clone https://github.com/hANSIc99/core_dump_example.git
Wie sich Signale auf Dumps beziehen
Weitere Linux-Ressourcen
- Spickzettel für Linux-Befehle
- Spickzettel für fortgeschrittene Linux-Befehle
- Kostenloser Online-Kurs:RHEL Technical Overview
- Spickzettel für Linux-Netzwerke
- SELinux-Spickzettel
- Spickzettel für allgemeine Linux-Befehle
- Was sind Linux-Container?
- Unsere neuesten Linux-Artikel
Signale sind eine Art Interprozesskommunikation zwischen dem Betriebssystem und den Benutzeranwendungen. Linux verwendet die im POSIX-Standard definierten Signale. Auf Ihrem System finden Sie die in /usr/include/bits/signum-generic.h
definierten Standardsignale . Es gibt auch eine informative Man-Signal-Seite, wenn Sie mehr über die Verwendung von Signalen in Ihrer Anwendung erfahren möchten. Einfach ausgedrückt verwendet Linux Signale, um weitere Aktivitäten auszulösen, je nachdem, ob sie erwartet oder unerwartet waren.
Wenn Sie eine laufende Anwendung beenden, erhält die Anwendung normalerweise den SIGTERM
Signal. Da diese Art von Exit-Signal erwartet wird, erzeugt diese Aktion kein Speicherabbild.
Die folgenden Signale bewirken, dass eine Dump-Datei erstellt wird (Quelle:GNU C Library):
- SIGFPE:Fehlerhafte arithmetische Operation
- Siegel:Illegale Anweisung
- SIGSEGV:Ungültiger Zugriff auf Speicher
- SIGBUS:Busfehler
- SIGABRT:Ein vom Programm erkannter und durch Aufruf von abort gemeldeter Fehler
- SIGIOT:Auf Fedora als archaisch bezeichnet, wird dieses Signal verwendet, um auf
abort()
auszulösen auf einem PDP-11 und wird jetzt auf SIGABRT abgebildet
Dump-Dateien erstellen
Navigieren Sie zu core_dump_example
Verzeichnis, führen Sie make
aus , und führen Sie das Beispiel mit -c1
aus Schalter:
./coredump -c1
Die Anwendung sollte in Zustand 4 mit einem Fehler beendet werden:
"Abgebrochen (Speicherabzug geschrieben)" bedeutet grob übersetzt "Segmentation fault (core dumped)."
Ob ein Core-Dump erstellt wird oder nicht, wird durch die Ressourcengrenze des Benutzers bestimmt, der den Prozess ausführt. Sie können die Ressourcenlimits mit ulimit
ändern Befehl.
Überprüfen Sie die aktuelle Einstellung für die Core-Dump-Erstellung:
ulimit -c
Wenn es unlimited
ausgibt , dann verwendet es den (empfohlenen) Standardwert. Korrigieren Sie andernfalls das Limit mit:
ulimit -c unlimited
Geben Sie Folgendes ein, um das Erstellen von Core-Dumps zu deaktivieren:
ulimit -c 0
Die Zahl gibt die Ressource in Kilobyte an.
Was sind Core-Dumps?
Die Art und Weise, wie der Kernel Core-Dumps handhabt, ist definiert in:
/proc/sys/kernel/core_pattern
Ich verwende Fedora 31 und auf meinem System enthält die Datei:
/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h
Dies zeigt, dass Core-Dumps an systemd-coredump
weitergeleitet werden Nützlichkeit. Der Inhalt von core_pattern
kann zwischen den verschiedenen Geschmacksrichtungen von Linux-Distributionen stark variieren. Wenn systemd-coredump
verwendet wird, werden die Dump-Dateien komprimiert unter /var/lib/systemd/coredump
gespeichert . Sie müssen die Dateien nicht direkt berühren; stattdessen können Sie coredumpctl
verwenden . Zum Beispiel:
coredumpctl list
zeigt alle verfügbaren Dump-Dateien, die auf Ihrem System gespeichert sind.
Mit coredumpctl dump
, können Sie Informationen aus der zuletzt gespeicherten Dump-Datei abrufen:
[stephan@localhost core_dump_example]$ ./coredump
Application started…
(…….)
Message: Process 4598 (coredump) of user 1000 dumped core.
Stack trace of thread 4598:
#0 0x00007f4bbaf22625 __GI_raise (libc.so.6)
#1 0x00007f4bbaf0b8d9 __GI_abort (libc.so.6)
#2 0x00007f4bbaf664af __libc_message (libc.so.6)
#3 0x00007f4bbaf6da9c malloc_printerr (libc.so.6)
#4 0x00007f4bbaf6f49c _int_free (libc.so.6)
#5 0x000000000040120e n/a (/home/stephan/Dokumente/core_dump_example/coredump)
#6 0x00000000004013b1 n/a (/home/stephan/Dokumente/core_dump_example/coredump)
#7 0x00007f4bbaf0d1a3 __libc_start_main (libc.so.6)
#8 0x000000000040113e n/a (/home/stephan/Dokumente/core_dump_example/coredump)
Refusing to dump core to tty (use shell redirection or specify — output).
Dies zeigt, dass der Prozess von SIGABRT
gestoppt wurde . Der Stack-Trace in dieser Ansicht ist nicht sehr detailliert, da er keine Funktionsnamen enthält. Allerdings mit coredumpctl debug
, können Sie die Dump-Datei einfach mit einem Debugger (standardmäßig GDB) öffnen. Geben Sie bt
ein (kurz für backtrace) um eine detailliertere Ansicht zu erhalten:
Core was generated by `./coredump -c1'.
Program terminated with signal SIGABRT, Aborted.
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 return ret;
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007fc37a9aa8d9 in __GI_abort () at abort.c:79
#2 0x00007fc37aa054af in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fc37ab14f4b "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3 0x00007fc37aa0ca9c in malloc_printerr (str=str@entry=0x7fc37ab130e0 "free(): invalid pointer") at malloc.c:5339
#4 0x00007fc37aa0e49c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:4173
#5 0x000000000040120e in freeSomething(void*) ()
#6 0x0000000000401401 in main ()
Die Speicheradressen:main()
und freeSomething()
sind im Vergleich zu nachfolgenden Frames ziemlich niedrig. Da Shared Objects auf einen Bereich am Ende des virtuellen Adressraums abgebildet werden, können Sie davon ausgehen, dass die SIGABRT
wurde durch einen Aufruf in einer gemeinsam genutzten Bibliothek verursacht. Speicheradressen von gemeinsam genutzten Objekten sind zwischen Aufrufen nicht konstant, daher ist es völlig in Ordnung, wenn Sie zwischen Aufrufen unterschiedliche Adressen sehen.
Der Stacktrace zeigt, dass nachfolgende Aufrufe von malloc.c
stammen , was darauf hinweist, dass etwas mit der Speicher(aufhebung)zuweisung schief gelaufen sein könnte.
Im Quellcode sieht man (auch ohne C++-Kenntnisse), dass versucht wurde, einen Zeiger freizugeben, der von einer Speicherverwaltungsfunktion nicht zurückgegeben wurde. Dies führt zu undefiniertem Verhalten und verursacht das SIGABRT
:
void freeSomething(void *ptr){
free(ptr);
}
int nTmp = 5;
int *ptrNull = &nTmp;
freeSomething(ptrNull);
Das Dienstprogramm systemd coredump kann unter /etc/systemd/coredump.conf
konfiguriert werden . Die Rotation der Dump-Dateibereinigung kann in /etc/systemd/system/systemd-tmpfiles-clean.timer
konfiguriert werden .
Weitere Informationen zu coredumpctl
finden Sie hier auf seiner Manpage.
Kompilierung mit Debug-Symbolen
Öffnen Sie das Makefile
und kommentieren Sie den letzten Teil von Zeile 9 aus. Es sollte jetzt so aussehen:
CFLAGS =-Wall -Werror -std=c++11 -g
Das -g
switch ermöglicht es dem Compiler, Debug-Informationen zu erstellen. Starten Sie die Anwendung, diesmal mit dem -c2
Schalter:
./coredump -c2
Sie erhalten eine Gleitkommaausnahme. Öffnen Sie den Dump in GDB mit:
coredumpctl debug
Diesmal werden Sie direkt auf die Zeile im Quellcode verwiesen, die den Fehler verursacht hat:
Reading symbols from /home/stephan/Dokumente/core_dump_example/coredump…
[New LWP 6218]
Core was generated by `./coredump -c2'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0 0x0000000000401233 in zeroDivide () at main.cpp:29
29 nRes = 5 / nDivider;
(gdb)
Geben Sie list
ein um einen besseren Überblick über den Quellcode zu bekommen:
(gdb) list
24 int zeroDivide(){
25 int nDivider = 5;
26 int nRes = 0;
27 while(nDivider > 0){
28 nDivider--;
29 nRes = 5 / nDivider;
30 }
31 return nRes;
32 }
Verwenden Sie den Befehl info locals
um die Werte der lokalen Variablen von dem Zeitpunkt abzurufen, an dem die Anwendung fehlgeschlagen ist:
(gdb) info locals
nDivider = 0
nRes = 5
In Kombination mit dem Quellcode können Sie sehen, dass Sie auf eine Division durch Null gestoßen sind:
nRes = 5 / 0
Schlussfolgerung
Wenn Sie wissen, wie Sie mit Speicherauszugsdateien umgehen, können Sie zufällige Fehler in einer Anwendung finden und beheben, die schwer zu reproduzieren sind. Und wenn es nicht Ihre Anwendung ist, hilft die Weiterleitung eines Core-Dumps an den Entwickler, das Problem zu finden und zu beheben.