Wenn ein Prozess unter Linux abstützt, kann der Kernel wichtige Informationen wie den Speicherinhalt sichern, damit man im Nachhinein das Problem untersuchen kann. Viele Probleme sind sehr komplex und lassen sich nur schwer reproduzieren, weshalb diese Coredumps hilfreich sind.
Bisher hatte ich die notwendigen Einstellungen immer von Hand vorgenommen, aber ich habe die Pakete systemd-coredump und corekeeper entdeckt. Das Paket systemd-coredump existiert noch nicht unter Debian 8, weshalb ich auf meinem Server corekeeper installiert habe. Beide scheinen auf den ersten Blick das selbe zu tun, nur ist corekeeper eine reine Debian-Entwicklung und systemd-coredump die Integration in systemd.
Prozesse ohne Coredumps finden
Die beiden Paketen kümmern sich um die geordnete Ablage der Coredumps und
die Benachrichtigung des Admins über vorhandene Coredumps. Dass der Kernel
einen Coredump bei einem Prozessabsturz tatsächlich schreibt, kann man für
eine Shell (und deren Kinder) mit ulimit -c unlimited
aktivieren und mit
ulimit -c 0
deaktivieren.
Um zu sehen, für welche Prozesse keine Coredumps erzeugt werden, kann man in /proc nach diesen suchen, denn die Limits eines Prozesses werden unter /proc/PID/limits vom Kernel veröffentlicht.
# ps $(grep -l 'Max core file size 0' /proc/*/limits |cut -f3 -d/) |grep -v '\]$'
PID TTY STAT TIME COMMAND
1 ? Ss 0:01 /sbin/init
163 ? Ss 0:00 /lib/systemd/systemd-journald
174 ? Ss 0:00 /lib/systemd/systemd-udevd
334 ? Ss 0:00 /lib/systemd/systemd-logind
336 ? Ss 0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
342 ? Ss 0:00 /lib/systemd/systemd-networkd
343 ? Ss 0:00 /usr/sbin/acpid
346 ? S<L 0:00 /usr/bin/atop -a -w /var/log/atop/atop_20170529 600
356 tty1 Ss+ 0:00 /sbin/agetty --noclear tty1 linux
575 ? S 0:00 avahi-daemon: chroot helper
2606 ? Ss 0:00 /usr/bin/ssh-agent /usr/bin/ck-launch-session /home/joerg/.xsession
2619 pts/0 Ss 0:00 sudo HOME=/home/joerg zsh -69JNXZhims
Einige Prozesse wie ssh-agent und sudo scheinen Coredumps jedoch selbst wieder zu deaktivieren. Allerdings muss nach der Installation der Pakete auch erst einmal die Erzeugung der Coredumps aktiviert werden.
Coredumps für alle Prozesse bei systemd aktivieren
Damit man von den Diensten die entsprechenden Coredumps bekommt, muss man in /etc/systemd/system.conf die Einstellung
DefaultLimitCORE=infinity
setzen. Systemd startet dann alle Prozesse mit aktiviertem Coredump. Für Benutzerprozesse muss man in der Datei /etc/security/limits.conf die Einträge
# enable core dumps
* soft core unlimited
* hard core unlimited
ergänzen, so dass von PAM die Coredumps bei der Anmeldung aktiviert werden. Für weitere Einstellungen zur Verarbeitung von Coredumps sei nur auf /etc/systemd/coredump.conf verwiesen.
Coredumps mit coredumpctl auswerten
Die aufgezeichneten Sicherungen der Abstürze kann man sich leicht mit
coredumpctl list
auflisten oder mit coredumpctl info
auswerten lassen oder
mit coredumpctl debug
den Debugger damit starten. Coredumpctl bietet auch noch
verschiedene Filtermöglichkeiten, falls die Liste der Abstürze sehr groß wird.
Umfangreichere Ausgabe des Stacks
Für eine Fehlermeldung eines Programmabsturzes versuche ich immer so viele Informationen wie möglich mitzuteilen. Ab der Version 248 kennt coredumpctl die Option --debugger-args, womit es leicht ist, einen ausführlichen Stackverlauf auszugeben:
coredumpctl debug --debugger-arguments="-quiet -batch -ex 'bt full' -ex 'echo --- END ---\n\n'"
Mit älteren Versionen bedurfte es eines kleinen Hilfsprogramms gdb-bt-full, um gdb die notwendigen Optionen mitzugeben:
#!/bin/sh
exec gdb "$@" -quiet -batch -ex 'bt full' -ex 'echo --- END ---\n\n'
Dieses rufe ich dann mit coredumpctl --debugger ~/bin/gdb-bt-full debug
auf
und kann die Ausgabe komplett in den Fehlerbericht einfügen. Mehr Informationen
kann man ohne zusätzliche Arbeit auch nicht liefern. Im Gegensatz zur Situation
vor Jahren ist es jetzt mit coredumpctl sehr komfortabel und ich kann binnen
von fünf Minuten einen Bugreport erstellen.
Debuginformationen unter Debian
Um die Sicherungen detailierter analysieren zu können, benötigt man die Debuginformationen zu den beteiligten Programmen und Bibliotheken. Debian stellt dafür zwei Möglichkeiten zur Auswahl: * die sehr einfache Variante über den Debuginfo-Server von Debian oder * die Pakete mit den Debuginformationen.
Debuginfo-Server einrichten
Ab Debian-Bullseye unterstützt gdb den Abruf der Debuginformationen von einem Server. Hierfür muss nur die Umgebungsvariable DEBUGINFOD_URLS gesetzt werden und gdb lädt beim Start die notwendigen Informationen vom Server: »Downloading separate debug info for …«.
Debian stellt hierfür den Server debuginfod.debian.net bereit. Um diesen zu nutzen ergänzt man entweder den Aufruf von coredumpctl zu
DEBUGINFOD_URLS="https://debuginfod.debian.net" coredumpctl debug --debugger-arguments=…
oder man setzt die Variable mit export
DEBUGINFOD_URLS="https://debuginfod.debian.net"
in dem Hilfsprogramms
gdb-bt-full.
Die Dateien werden im Verzeichnis ~/.cache/debuginfod_client gespeichert, dessen Bereinigung man über die beiden Dateien cache_clean_interval_s und max_unused_age_s in dem Verzeichnis steuern kann.
Debuginfos aus dbgsym-Paketen
Wenn man keine Internetverbindung beim Debuggen verwenden will, kann man die Debuginformationen in den Paketen mit der Endung -dbgsym nutzen. Hierfür muss man in /etc/apt/sources.list einen entsprechenden Eintrag für seine Debian-Variante einfügen; zum Beispiel:
deb http://ftp.de.debian.org/debian-debug/ unstable-debug main non-free contrib
Coredumpctl zeichnet die Informationen leider nur beim Entstehen der Sicherung
auf und ergänzt sie nicht nachdem die Informationen installiert wurden. Aber mit
coredumpctl gdb
kann man im gdb mit dem Befehl bt
(backtrace) sich die
Stelle des Absturzes ansehen:
(gdb) bt
#0 0x00007f0b5ffe4ce7 in __GI___select (nfds=1, readfds=0x7ffd83ca7e80, writefds=0x0, exceptfds=0x0, timeout=0x0) at ../sysdeps/unix/sysv/linux/select.c:41
#1 0x00007f0b5fcf3e14 in ?? () from /usr/lib/python2.7/lib-dynload/readline.x86_64-linux-gnu.so
#2 0x000055dd60d82496 in PyOS_Readline ()
#3 0x000055dd60d5ca96 in ?? ()
#4 0x000055dd60ded854 in PyParser_ASTFromFile ()
#5 0x000055dd60d8221d in PyRun_InteractiveOneFlags ()
#6 0x000055dd60d820d8 in PyRun_InteractiveLoopFlags ()
#7 0x000055dd60d67fe8 in ?? ()
#8 0x000055dd60dcd345 in Py_Main ()
#9 0x00007f0b5ff18f2a in __libc_start_main (main=0x55dd60dccc90 , argc=1, argv=0x7ffd83ca83d8, init=, fini=,
rtld_fini=, stack_end=0x7ffd83ca83c8) at ../csu/libc-start.c:310
#10 0x000055dd60dccbaa in _start ()
Anhand der … from …
sieht man, welche Bibliotheken beteiligt waren, für die
man mit dpkg -S …
das Paket finden und dafür die Debuginformationen
installieren kann. Nachdem die Informationen alle vorhanden sind, kann man gdb
wieder aufrufen und erhält dann im Backtrace die genauen Angaben.
Mit apt source …
kann man sich auch den Quelltext zu den Paketen holen, so
dass man Probleme noch genauer analysieren kann. Manchmal zeigt sich, dass
Abstürze durch Konfigurationseinstellungen bedingt sind, womit man vielleicht
erst einmal um ein Problem herumarbeiten kann, bis es behoben ist.
Ich habe auch bemerkt, dass die Namen der betroffenen Dateien ebenso bei
coredumpctl info
erkennbar sind:
% coredumpctl info -1
PID: 11333 (python)
UID: 1000 (joerg)
GID: 1000 (joerg)
Signal: 3 (QUIT)
Timestamp: Fri 2018-01-05 12:56:09 CET (9min ago)
Command Line: python
Executable: /usr/bin/python2.7
Control Group: /user.slice/user-1000.slice/session-7.scope
Unit: session-7.scope
Slice: user-1000.slice
Session: 7
Owner UID: 1000 (joerg)
Boot ID: ffb88c73dd1b40c29dd7f9280176f919
Machine ID: 523cb54753234ed08c13ec497d0d3b64
Hostname: zenbook
Storage: /var/lib/systemd/coredump/core.python.1000.ffb88c73dd1b40c29dd7f9280176f919.11333.1515153369000000.lz4
Message: Process 11333 (python) of user 1000 dumped core.
Stack trace of thread 11333:
#0 0x00007f0b5ffe4ce7 __GI___select (libc.so.6)
#1 0x00007f0b5fcf3e14 n/a (readline.x86_64-linux-gnu.so)
#2 0x000055dd60d82496 PyOS_Readline (python2.7)
#3 0x000055dd60d5ca96 n/a (python2.7)
#4 0x000055dd60ded854 PyParser_ASTFromFile (python2.7)
#5 0x000055dd60d8221d PyRun_InteractiveOneFlags (python2.7)
#6 0x000055dd60d820d8 PyRun_InteractiveLoopFlags (python2.7)
#7 0x000055dd60d67fe8 n/a (python2.7)
#8 0x000055dd60dcd345 Py_Main (python2.7)
#9 0x00007f0b5ff18f2a __libc_start_main (libc.so.6)
#10 0x000055dd60dccbaa _start (python2.7)
Coredumps für alle Prozesse bei corekeeper aktivieren
Für Benutzer werden von corekeeper bei der Anmeldung Coredumps aktiviert. Allerdings nicht für Dienste und andere Prozesse. Dies kann man aber leicht erreichen, indem man auf der initrd die Coredumps aktiviert, so dass der eigentliche Init-Prozess bereits mit aktivierten Coredumps gestartet wird und diese Einstellung auf alle Kinder vererbt.
Dies kann man mit folgendem Skript erreichen, das man als
/etc/initramfs-tools/scripts/init-bottom/local speichert und diese Datei
noch ausführbar setzt (chmod +x …
). Damit die Initrd neu bauen
(update-initramfs -u
) und das System neu starten. Somit erhält man auch
die abgestürzte PHP-Prozesse einen Coredump und kann eventuell mehr über
die Ursache herausfinden oder ggf. den Coredump an einen Entwickler
weiterleiten.
#!/bin/sh
case "$1" in
# get pre-requisites
prereqs)
# Print the soft prerequisites here. This is a space separated list of
# names, of scripts that are in the same directory as this one, that
# must be run before this one can be.
exit 0
;;
esac
# Diese Datei wird von dem init-Prozess auf der initrd immer wieder ausgeführt
# und so kann für den init-Prozess selbst und alle Kinderprozesse der coredump
# aktiviert werden.
echo "ulimit -c unlimited" >> /conf/param.conf