Возникла необходимость получить дамп РНР-процесса на 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
:
/tmp
каталог%e
– имя файла начинается с coredump + имя исполняемого файла make_dump%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]
Готово.
Ссылки по теме
- Understand and configure core dumps on Linux
- Hunting the Core
- Core dump (Arch Wiki)
- Generate PHP core dumps on segfaults in PHP-FPM
- Generating a gdb backtrace
- Проблема с eaccelerator и генерация coredump для php-fpm
- How to get a core dump for a segfault on Linux
- kill: Creating a core dump