Linux

Как сгенерировать дамп ядра в Linux при ошибке сегментации

У меня есть процесс в 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

Чтобы активировать дамп ядра, сделайте следующее:

  1. В /etc/profile закомментируйте строку:

# ulimit -S -c 0 > /dev/null 2>&1

  1. В /etc/security/limits.conf закомментируйте строку: 

* soft core 0

  1. Выполните команду 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

  1. Для проверки записи файла ядра можно завершить соответствующий процесс командой kill -s SEGV <PID> (необязательно, просто в случае, если файл ядра не записывается, это можно использовать как проверку):

# kill -s SEGV <PID>

  1. После записи 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>

 Дополнительная информация:

 

  1. В некоторых ответах предлагается изменить core_pattern. Имейте в виду, что этот файл может быть перезаписан службой apport при перезапуске.

  2. Простая остановка apport может не помочь.

  3. Значение 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));

}

 

Схожие статьи

REOS. Игровой Linux
Linux

REOS. Игровой Linux

Linux

Linux API для отображения запущенных процессов

Linux

Поиск файла по содержимому в Linux. Поиск текста в файлах Linux

Как установить компилятор GCC на Ubuntu в Linux последней версии
Linux

Как установить компилятор GCC на Ubuntu в Linux последней версии

×