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 gdb den Debugger damit starten. Coredumpctl bietet auch noch verschiedene Filtermöglichkeiten, falls die Liste der Abstürze sehr groß wird.

Debuginformationen unter Debian

Um besser die Sicherungen analysieren zu können sollte man sich die Debuginformationen zu den beteiligten Programmen und Bibliotheken installieren. Debian stellt dafür zu vielen Paketen noch zu jedem Paket ein weiteres Paket mit der Endung -dbgsym bereit, welches man mit dem entsprechenden Eintrag in /etc/apt/sources.list bekommt, 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 noch mal nach der Installation, 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 Dateien 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