У меня есть процесс в Linux, в котором возникает ошибка сегментации. Как я могу сообщить ему генерировать дамп ядра при сбое?
Ответ 1
Это зависит от того, какую оболочку вы используете. Если вы используете bash, то команда ulimit управляет несколькими параметрами, относящимися к выполнению программы, например, следует ли сбрасывать ядро. Если вы введете:
ulimit -c unlimited
то это сообщит bash, что ее программы могут сбрасывать ядра любого размера. Вы можете указать размер, например, 52 M вместо неограниченного, если хотите, но на практике в этом нет необходимости, поскольку размер файлов ядра, вероятно, никогда не будет иметь для вас значения. А вот в tcsh необходимо выполнить:
limit coredumpsize unlimited
Ответ 2
Как объяснялось в предыдущем ответе, реальный вопрос, задаваемый здесь, заключается в том, как включить дампы ядра на системе, где они выключены. Ответ на этот вопрос следующий: для того чтобы сгенерировать дамп ядра для зависшего процесса, необходимо выполнить следующий код:
gcore <pid>
Если же gcore недоступен в вашей системе, тогда:
kill -ABRT <pid>
Не используйте kill -SEGV, так как это часто приводит к вызову обработчика сигналов, что затрудняет диагностику зависшего процесса.
Ответ 3
Чтобы проверить, где создаются дампы ядра, выполните команду:
sysctl kernel.core_pattern
или:
cat /proc/sys/kernel/core_pattern
где %e — имя процесса, а %t — системное время. Вы можете изменить его в /etc/sysctl.conf и перезагрузить с помощью sysctl -p. Если файлы ядра не генерируются и далее (проверить это можно с помощью: sleep 10 & и killall -SIGSEGV sleep), проверьте ограничения с помощью команды:
ulimit -a.
Если размер файла ядра ограничен, выполните:
ulimit -c unlimited
чтобы сделать его неограниченным.
Затем снова проверьте, что дамп ядра выполнен успешно — вы увидите "(core dumped)" после индикации ошибки сегментации, как показано ниже:
Segmentation fault: 11 (core dumped)
В Ubuntu дампы ядра обрабатываются Apport и могут быть расположены в /var/crash/. Однако в стабильных релизах он отключен по умолчанию.
Ответ 4
Возможно, вы можете сделать это следующим образом: программа, показанная ниже, демонстрирует, как отловить ошибку сегментации и передать ее отладчику (это оригинальный код, используемый под AIX), и он также печатает трассировку стека до момента ошибки сегментации. Для системы Linux вам нужно будет изменить переменную sprintf для использования в gdb.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
static void signal_handler(int);
static void dumpstack(void);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);
struct sigaction sigact;
char *progname;
int main(int argc, char **argv) {
char *s;
progname = *(argv);
atexit(cleanup);
init_signals();
printf("About to seg fault by assigning zero to *s\n");
*s = 0;
sigemptyset(&sigact.sa_mask);
return 0;
}
void init_signals(void) {
sigact.sa_handler = signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGBUS);
sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGQUIT);
sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaction(SIGHUP, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGKILL);
sigaction(SIGKILL, &sigact, (struct sigaction *)NULL);
}
static void signal_handler(int sig) {
if (sig == SIGHUP) panic("FATAL: Program hanged up\n");
if (sig == SIGSEGV || sig == SIGBUS){
dumpstack();
panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
}
if (sig == SIGQUIT) panic("QUIT signal ended program\n");
if (sig == SIGKILL) panic("KILL signal ended program\n");
if (sig == SIGINT) ;
}
void panic(const char *fmt, ...) {
char buf[50];
va_list argptr;
va_start(argptr, fmt);
vsprintf(buf, fmt, argptr);
va_end(argptr);
fprintf(stderr, buf);
exit(-1);
}
static void dumpstack(void) {
/* Эта процедура взята с http://www.whitefang.com/unix/faq_toc.html
** Раздел 6.5. Изменено на перенаправление в файл для предотвращения неопределенного поведения
*/
/* Это нужно изменить... */
char dbx[160];
sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname);
/* Измените dbx на gdb */
system(dbx);
return;
}
void cleanup(void) {
sigemptyset(&sigact.sa_mask);
/* Выполните здесь любые действия по очистке */
}
Возможно, вам придется дополнительно добавить некие параметры, чтобы заставить gdb сделать дамп ядра.
Ответ 5
Чтобы активировать дамп ядра, сделайте следующее:
В /etc/profile закомментируйте строку:
# ulimit -S -c 0 > /dev/null 2>&1
В /etc/security/limits.conf закомментируйте строку:
* soft core 0
Выполните команду limit coredumpsize unlimited и проверьте ее с помощью cmd limit:
# limit coredumpsize unlimited
# limit
cputime unlimited
filesize unlimited
datasize unlimited
stacksize 10240 kbytes
coredumpsize unlimited
memoryuse unlimited
vmemoryuse unlimited
descriptors 1024
memorylocked 32 kbytes
maxproc 528383
Для проверки записи файла ядра можно завершить соответствующий процесс командой kill -s SEGV <PID> (необязательно, просто в случае, если файл ядра не записывается, это можно использовать как проверку):
# kill -s SEGV <PID>
После записи corefile убедитесь, что настройки coredump снова отключены в соответствующих файлах.
Ответ 6
Стоит упомянуть, что если у вас установлен systemd, то ситуация немного меняется. Обычно файлы ядра передаются через systemd-coredump(8) посредством значения core_pattern sysctl. Размер rlimit файла ядра, как правило, уже настроен как «неограниченный».
Затем можно получить дампы ядра с помощью coredumpctl(1).
Хранение дампов ядра и т. д. настраивается coredump.conf(5). Примеры получения файлов ядра есть на man-странице coredumpctl, но вкратце это выглядит следующим образом:
[vps@phoenix]~$ coredumpctl list test_me | tail -1
Sun 2019-01-20 11:17:33 CET 16163 1224 1224 11 present /home/vps/test_me
Получите файл ядра:
[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163
Ответ 7
Для Ubuntu
Создайте ~/.config/apport/settings со следующим содержимым:
[main]
unpackaged=true
(эта команда указывает apport писать дампы ядра для пользовательских приложений).
Выполните:
ulimit -c.
Если выдает 0, исправьте это с помощью:
ulimit -c unlimited
На всякий случай перезапустите apport:
sudo systemctl restart apport
Файлы дампа теперь записываются в /var/crash/. Но вы не можете использовать их с gdb. Чтобы использовать их в gdb, выполните:
apport-unpack <location_of_report> <target_directory>
Дополнительная информация:
В некоторых ответах предлагается изменить core_pattern. Имейте в виду, что этот файл может быть перезаписан службой apport при перезапуске.
Простая остановка apport может не помочь.
Значение ulimit -c может измениться автоматически. Обязательно проверяйте его регулярно во время настройки создания дампа ядра.
Ответ 8
Лучше включить дамп ядра программно, используя системный вызов setrlimit. Пример:
#include <sys/resource.h>
bool enable_core_dump(){
struct rlimit corelim;
corelim.rlim_cur = RLIM_INFINITY;
corelim.rlim_max = RLIM_INFINITY;
return (0 == setrlimit(RLIMIT_CORE, &corelim));
}
Linux