Генерация дампов упавших процессов

Описание

При падении процесса его память может быть сохранена в файл для дальнейшей диагностики. Расположение файла дампа зависит от настроек ядра, а его наличие и размер - от настройки исполняющегося процесса. По умолчанию во многих дистрибутивах Linux (например, Ubuntu и Debian) генерация дампов отключена.

Есть ряд причин, по которым дампы упавших процессов могут не сохраняться.

Наиболее частая причина - выставленный в 0 лимит на размер дампов RLIMIT_CORE. Другие причины описаны ниже:

  • У процесса нет доступа для записи файла дампа. (По умолчанию файл дампа называется core или core.pid, где pid - идентификатор упавшего процесса, и сохраняется в текущей папке исполнявшегося приложения. См. ниже об именовании файла дампа.) Запись дампа также не удастся если папка не доступна для записи или файл с таким же именем существует и не доступен для записи или не является обычным файлом (например, является папкой или ссылкой).

  • Файл с таким же именем существует и доступен для записи, но слинкован жёсткой ссылкой.

  • На файловой системе недостаточно места или inode’ов или файловая система примонтирована в режиме только для чтения или квота пользователя превышена для этой файловой системы.

  • Текущая папка процесса отсутствует на файловой системе.

  • Лимит на размер дампа RLIMIT_CORE выставлен в 0 (наиболее частая причина).

  • Лимит на максимальный размер файла который процесс может создать RLIMIT_FSIZE выставлен в 0.

  • У исполняемого файла отсутствует право на чтение (Это мера безопасности чтобы пользователь не получил доступ к содержимому исполняемого файла через дамп).

  • Исполняемый файл имеет выставленный бит setuid и дампы таких файлов явно не разрешены настройкой fs.suid_dumpable.

  • Настройка kernel.core_pattern пуста и настройка kernel.core_uses_pid выставлена в 0. (Если же при пустой настройке kernel.core_pattern значение kernel.core_uses_pid не 0, то дамп будет записан в файл с именем .pid)

  • Ядро собрано без опции CONFIG_COREDUMP.

Настройка

Лимиты процесса

Чтобы включить генерацию дампов, необходимо выставить RLIMIT_CORE в пользовательском процессе.

Целочисленные значения указывают максимальный размер файла дампа в 512-байтных блоках. Ограничение размера применяется только для файлов, то есть в том случае, если настройка kernel.core_pattern (см. ниже) указывает на имя файла для дампа, в отличие от процесса.

Можно это сделать изнутри процесса при старте:

#include <sys/resource.h>

void main(int argc, char** argv)
{
    struct rlimit rlim;
    int res;

    rlim.rlim_cur = RLIM_INFINITY;
    rlim.rlim_max = RLIM_INFINITY;
    res = setrlimit(RLIMIT_CORE, &rlim);
}

А также это можно сделать снаружи процесса перед его стартом (т.к. лимиты наследуются при создании дочерних процессов). Например, через встроенную команду пользовательской оболочки ulimit (в csh - limit).

> ulimit -c
0

При нулевом значении файл дампа не создаётся.

Чтобы включить создание дампов без ограничений по размеру:

> ulimit -c unlimited

Проверяем снова:

> ulimit -c
unlimited

После этого все дочерние процессы запущенные из этой оболочки будут генерировать дампы если нет других причин (см. Описание). Куда дампы будут сохраняться - зависит от переменных ядра sysctl.

Еще одна возможность - прописать лимиты в настройках системы /etc/security/limits.conf и перезагрузиться. После перезагрузки нужно проверить ulimit -c, его значение должно стать unlimited. Если же это не так, возможно в стартовых файлах оболочки (например /etc/profile также происходит выставление лимитов, тогда нужно их исправить ещё и там).

Переменные ядра sysctl

Настройка дампов производится следующими переменными:

  • kernel.core_pattern

  • kernel.core_pipe_limit

  • kernel.core_uses_pid

Для начала можно посмотреть на существующие настройки. В разных дистрибутивах настройки по умолчанию отличаются:

> sysctl kernel 2>/dev/null | grep core
kernel.core_pattern = |/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h
kernel.core_pipe_limit = 0
kernel.core_uses_pid = 0
> sysctl kernel 2>/dev/null | grep core
kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P %E
kernel.core_pipe_limit = 0
kernel.core_uses_pid = 0
> sysctl kernel 2>/dev/null | grep core
kernel.core_pattern = |/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e
kernel.core_pipe_limit = 0
kernel.core_uses_pid = 0
> sysctl kernel 2>/dev/null | grep core
kernel.core_pattern = core
kernel.core_pipe_limit = 0
kernel.core_uses_pid = 1

kernel.core_pattern указывает имя файла для сохранения дампа либо исполняемую программу-обработчик дампов (если первым символом является |).

kernel.core_pipe_limit применяется только в случае использования программы-обработчика дампов.

kernel.core_uses_pid влияет на имя файла для сохранения дампа если не используется программа-обработчик (хотя некоторые обработчики применяют это значение для эмуляции поведения по-умолчанию, например apport).

Если в kernel.core_pattern сконфигурирован обработчик, то можно попробовать использовать его для получения дампов. Например, systemd-coredump вполне юзабелен и может отобразить список упавших процессов, а также выдать дамп для сохранения (см. описание systemd-coredump ниже).

Если же обработчик не юзабелен или не настраивается (например, apport - эмулирует поведение по умолчанию и сохраняет файл в текущую папку процесса с именем core и это не настраивается), то можно настроить сохранение файлов самостоятельно, например:

# sysctl 'kernel.core_pattern=/tmp/core_%e_%p_%t_%s.dump'

Чтобы настройка сохранялясь после перезагрузки, нужно прописать это в файл /etc/sysctl.d/50-core-dumps.conf:

echo 'kernel.core_pattern = /tmp/core_%e_%p_%t_%s.dump' > /etc/sysctl.d/50-core-dumps.conf

Обработчики дампов

Systemd-coredump

Список упавших процессов можно просмотреть:

> coredumpctl
TIME                            PID   UID   GID SIG COREFILE  EXE
Wed 2021-05-26 17:25:07 MSK  482770     0     0   6 missing   /usr/local/bin/node
Wed 2021-05-26 17:25:41 MSK  482958     0     0   6 missing   /usr/local/bin/node
Tue 2021-06-08 13:49:40 MSK  2590838     0     0   7 missing   /home/mmak/devel/r/rostell_mg/priv/unix/rtx_mg3
Wed 2021-07-07 09:25:49 MSK    2242  1000  1000   6 missing   /usr/share/discord/Discord
Thu 2021-07-08 18:53:48 MSK  1514283  1000  1000   6 missing   /usr/bin/bt-adapter
Fri 2021-07-16 11:29:00 MSK    2805  1000  1000  11 missing   /usr/bin/simple-scan
Tue 2021-08-10 13:27:22 MSK   14050     0     0  11 missing   /home/mmak/devel/r/rostell_mg/priv/unix/rtx_mg3
Tue 2021-08-10 13:27:22 MSK   13607     0     0  11 missing   /home/mmak/devel/r/rostell_mg/priv/unix/rtx_mg3
Tue 2021-08-17 19:36:30 MSK  536042     0     0  11 missing   /home/mmak/apps/erl23.2.7/bin/run_erl
Sat 2021-08-21 06:18:37 MSK  280983     0     0  11 missing   /home/mmak/apps/erl23.2.7/bin/run_erl
Mon 2021-08-23 15:58:04 MSK  3574353  1000  1000   6 missing   /usr/share/discord/Discord
Wed 2021-09-08 09:31:35 MSK    3817  1000  1000   5 missing   /usr/libexec/csd-keyboard
Mon 2021-09-13 13:01:08 MSK  368526  1000  1000  11 present   /home/mmak/devel/core.dumps/a.out

Сохранить дамп можно так:

coredumpctl dump {pid} > program.core

Где {pid} - идентификатор упавшего процесса. Его можно найти в таблице (см. выше).

Docker

При использовании контейнеров docker следует понимать, что процессы внутри контейнера исполняются ядром хостовой системы, следовательно внутри контейнера не может существовать других настроек ядра, отличающихся от настроек хоста. Здесь начинается различие в обработке настройки kernel.core_pattern. Если она указывает на файл, то интерпретация пути и текущей папки происходит в контексте точек монтирования контейнера (т.е. внутри). Если же настройка указывает на процесс, то обработка происходит в хостовом окружении и точки монтирования контейнера не применяются. Таким образом, если, например, в системе установлен пакет systemctl-coredump, то дампы нужно снимать в хосте, где они и генерятся.

По умолчанию настройка ulimit -c в контейнере уже выставлена в unlimited, но если это не так, то при запуске контейнера нужно указать --ulimit core=-1.