Linux: создание coredump памяти процесса, systemd-coredump и Debian

Автор: | 10/03/2020

Возникла необходимость получить дамп РНР-процесса на Debian 9.

Рассмотрим механизм ядра, позволящий создать дамп, и настройку создания дампов в Linux.

Ниже будем говорить о создании дампа памяти процесса в Linux, а не дампа ядра при kernel panic – там он иной, см. Kdump на Arch Wiki.

Linux Core Dump

Ядро создаёт дамп памяти процесса, если он выполнил недопустимую операцию, и должен быть остановлен.

Для этого при выполнении такой операции ядро отправляет один из аварийных сигналов процессу, после чего процесс обрабатывает сигнал сам, или с помощью сторонних обработчиков, что запускает механизм ядра, который создаёт дамп.

Список таких сигналов задаётся в макросе SIG_KERNEL_COREDUMP_MASK:

...
#define SIG_KERNEL_COREDUMP_MASK (\
        rt_sigmask(SIGQUIT)   |  rt_sigmask(SIGILL)    | \
  rt_sigmask(SIGTRAP)   |  rt_sigmask(SIGABRT)   | \
        rt_sigmask(SIGFPE)    |  rt_sigmask(SIGSEGV)   | \
  rt_sigmask(SIGBUS)    |  rt_sigmask(SIGSYS)    | \
        rt_sigmask(SIGXCPU)   |  rt_sigmask(SIGXFSZ)   | \
  SIGEMT_MASK	
...

Который используется в другом макросе – sig_kernel_coredump:

...
#define sig_kernel_coredump(sig)	siginmask(sig, SIG_KERNEL_COREDUMP_MASK)
...

Который срабывает в случае Fatal-ошибок, и вызывает do_coredump():

...
  fatal:
                ...
    if (sig_kernel_coredump(signr)) {
      if (print_fatal_signals)
        print_fatal_signal(ksig->info.si_signo);
      proc_coredump_connector(current);
                        ...
      do_coredump(&ksig->info);
    }
...

Ну а сама do_coredump() создаёт дамп.

Сигналы и создание дампа

Проверим работу дампа.

Берём простой код на Си:

#include <stdio.h>
#include <unistd.h>

int main() {

    while(1) {
        pid_t pid = getpid();
        printf ("Working with PID %lu\n", pid);
        sleep(5);
    }
}

Собираем, и запускаем:

[simterm]

$ gcc make_dump.c -o make_dump
$ ./make_dump 
Working with PID 2714790

[/simterm]

Во второй консоли – отправляем один из сигналов, например SIGSEGV (Segmentation violation), код 11:

[simterm]

$ kill -s SIGSEGV 2714790

[/simterm]

В окне с приложением проверяем:

[simterm]

$ ./make_dump 
Working with PID 2714790
...
Working with PID 2714790
Segmentation fault (core dumped)

[/simterm]

Проверяем файл дампа:

[simterm]

$ ls -l /tmp/ | grep 2714790
-rw------- 1 setevoy setevoy  380928 Mar 10 11:24 coredump-make_dump.2714790

[/simterm]

Аналогично можно создать дамп практически любого процесса, например – запустим sleep:

[simterm]

$ sleep 100 &
[1] 2761144

$ kill -s SIGSEGV 2761144
[1]+  Segmentation fault      (core dumped) sleep 100

$ file /tmp/coredump-sleep.2761144 
/tmp/coredump-sleep.2761144: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from 'sleep 100', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: '/usr/bin/sleep', platform: 'x86_64'

[/simterm]

GDB – создать core dump

Кроме отправки сигнала – с помощью gdb, а именно gcore, можно создать дамп работающего процесса:

[simterm]

$ sleep 100 &
[1] 2762961

$ sudo gcore 2762961
...
Saved corefile core.2762961

$ file core.2762961
core.2762961: ELF 64-bit LSB core file

[/simterm]

GDB – прочитать core dump

Что бы просмотреть содержимое – можем использовать gdb, которому передаём путь к исполняемому файлу, процесс которого сгенерировал дамп, и путь к самому файлу дампа:

[simterm]

$ gdb make_dump coredump-make_dump.2714790 
...
[New LWP 2714790]
Core was generated by `./make_dump'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007f50d566e27e in clock_nanosleep@GLIBC_2.2.5 () from /usr/lib/libc.so.6
(gdb)

[/simterm]

kernel.core_pattern

Во время создания дампа – ядро проверяет параметр kernel.core_pattern, который определяет то, как будет обработан дамп.

Тут можно задать путь и имя файла, в который будет записан дамп, либо передать создание дампа внешнему обработчику, например systemd-coredump (рассмотрим ниже).

См. документацию Naming of core dump files тут>>>.

Из наиболее используемых опций тут:

  • %e executable filename (without path prefix)
  • %p PID of dumped process, as seen in the PID namespace in which the process resides
  • %t time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC)

Собственно файл /tmp/coredump-make_dump.2714790, который мы открыли в GDB, и состоит из kernel.core_pattern = /tmp/coredump-%e.%p:

  1. /tmp каталог
  2. %e – имя файла начинается с coredump + имя исполняемого файла make_dump
  3. %p – и PID убитого процесса – 2714790

Помимо указания на путь к файлу и созданию его имени – в core_pattern можно указать пайп |, и передать данные через него, например в /dev/null или в хендлер типа systemd-coredump.

limits.conf

Кроме имени файла – ядро проверит значение soft и hard лимитов для core в /etc/security/limits.conf:

[simterm]

$ cat /etc/security/limits.conf | grep core
#        - core - limits the core file size (KB)
#*               soft    core            0

[/simterm]

Либо проверяем в рабочем окружении:

[simterm]

$ ulimit -c
unlimited

[/simterm]

Лимиты конкретного процесса можно получить через его дескриптор в /proc:

[simterm]

$ ./make_dump 
Working with PID 2753034

[/simterm]

И лимиты:

[simterm]

$ cat /proc/2753034/limits 
Limit                     Soft Limit           Hard Limit           Units     
...  
Max core file size        unlimited            unlimited            bytes
...

[/simterm]

fs.suid_dumpable

Иногда дамп может не создаваться, если процесс в процессе выполнения выполнил запрос на смену UID, например через вызов setuid().

Определяется флагом fs.suid_dumpable:

[simterm]

$ sysctl fs.suid_dumpable
fs.suid_dumpable = 2

[/simterm]

Может принимать значение 0, 1 или 2, см. man proc:

  • 0: (default) This provides the traditional (pre-Linux 2.6.13) behaviour. A core dump will NOT be produced for a process which has changed credentials (by calling seteuid or similar) or whose binary does not have read permission enabled.
  • 1: (“debug“) All processes dump core when possible.
  • 2: (“suidsafe“) Any binary which normally would not be dumped (see “0” above) is dumped readable by root only.

systemd-coredump

В systemd, разумеется, добавлен свой обработчик дампов – уже упомянутый systemd-coredump.

На Arch Linux он используется по-умолчанию, на Debian 9 ставим из репозитория:

[simterm]

$ sudo apt -y install systemd-coredump

[/simterm]

Файл параметров – /etc/systemd/coredump.conf.

После установки настраиваем ядро – через пайп передаём создание дампа в systemd-coredump:

[simterm]

root@bttrm-production-app-1:/home/admin#  echo '|/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %e' > /proc/sys/kernel/core_pattern

[/simterm]

Проверяем:

[simterm]

root@bttrm-production-app-1:/home/admin# cat /proc/sys/kernel/core_pattern
|/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %e

[/simterm]

Создаём дамп какого-то процесса:

[simterm]

root@bttrm-production-app-1:/home/admin# sleep 10 &
[1] 27117
root@bttrm-production-app-1:/home/admin# kill -s 11 27117
[1]+  Segmentation fault      (core dumped) sleep 10

[/simterm]

coredumpctl

Для работы с дампами через systemd-coredump – используем coredumpctl:

[simterm]

root@bttrm-production-app-1:/home/admin# coredumpctl 
TIME                            PID   UID   GID SIG COREFILE EXE
Mon 2020-03-09 20:10:16 EET   20882     0     0  11 present  /home/admin/dump_test
...
Tue 2020-03-10 09:04:14 EET   16786  1003  1003  11 present  /usr/sbin/php-fpm7.4
Tue 2020-03-10 12:23:43 EET   27117     0     0  11 present  /bin/sleep

[/simterm]

В колонке SIG сразу видим код сигнала, с которым был убит процесс, в данном случае 11 == SIGSEGV.

Сами файлы дампов systemd-coredump сохраняет в /var/lib/systemd/coredump:

[simterm]

root@bttrm-production-app-1:/home/admin# ll /var/lib/systemd/coredump/
total 125376
-rw-r-----  1 root root    25208 Mar  9 20:10 core.dump_test.0.6bb23c691d354e9dbf4382d109a5c1d4.20882.1583777416000000000000.lz4
...
-rw-r-----  1 root root    33962 Mar 10 12:23 core.sleep.0.6bb23c691d354e9dbf4382d109a5c1d4.27117.1583835823000000000000.lz4

[/simterm]

Просмотреть дамп – передаём условие для MATCH, например – PID:

[simterm]

root@bttrm-production-app-1:/home/admin# coredumpctl info 27117
           PID: 27117 (sleep)
           UID: 0 (root)
           GID: 0 (root)
        Signal: 11 (SEGV)
     Timestamp: Tue 2020-03-10 12:23:43 EET (3min 3s ago)
  Command Line: sleep 10
    Executable: /bin/sleep
...
      Hostname: bttrm-production-app-1
       Storage: /var/lib/systemd/coredump/core.sleep.0.6bb23c691d354e9dbf4382d109a5c1d4.27117.1583835823000000000000.lz4
       Message: Process 27117 (sleep) of user 0 dumped core.
                
                Stack trace of thread 27117:
                #0  0x00007fc1a8053270 __nanosleep (libc.so.6)
                #1  0x000056159fa6f91f n/a (sleep)
                #2  0x000056159fa6f700 n/a (sleep)
                #3  0x000056159fa6c9a4 n/a (sleep)
                #4  0x00007fc1a7fbb2e1 __libc_start_main (libc.so.6)
                #5  0x000056159fa6ca7a n/a (sleep)

[/simterm]

Debian – core dump не создаётся

Всё вышеописанное отлично работает на двух нотбуках с Arch Linux – но на серверах проекта с Debian 9 дамп обычным способом, без systemd-coredump, не создаётся.

Конфиг:

[simterm]

root@bttrm-production-app-1:/home/admin# cat /proc/sys/kernel/core_pattern
/tmp/core-%e.%p

[/simterm]

Запускаем процесс, убиваем – файл дампа в /tmp не появляется.

Запускаем приложение ещё раз:

[simterm]

root@bttrm-production-app-1:/home/admin# ./dump_test 
Working with PID 27954

[/simterm]

И проверяем лимиты процесса

[simterm]

root@bttrm-production-app-1:/home/admin# cat /proc/27954/limits 
Limit                     Soft Limit           Hard Limit           Units     
... 
Max core file size        0                    unlimited            bytes
...

[/simterm]

Soft лимит размера файла дампа задан в ноль, т.е. создание дампа отключено вообще.

Задаём его в unlimited:

[simterm]

root@bttrm-production-app-1:/home/admin# ulimit -S -c unlimited

[/simterm]

Запускаем программу, проверяем лимит теперь:

[simterm]

root@bttrm-production-app-1:/home/admin# ./dump_test 1>/dev/null &
[3] 28399
root@bttrm-production-app-1:/home/admin# cat /proc/$!/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        unlimited            unlimited            bytes
...

[/simterm]

Создаём дамп:

[simterm]

root@bttrm-production-app-1:/home/admin# sleep 10 &
[4] 28613

root@bttrm-production-app-1:/home/admin# kill -s 11 $!

[/simterm]

И проверяем его:

[simterm]

root@bttrm-production-app-1:/home/admin# ls -l /tmp/coredump-sleep.28613 
-rw------- 1 root root 380928 Mar 10 12:47 /tmp/coredump-sleep.28613

[/simterm]

Готово.

Ссылки по теме