Настройка сбора дампов (Core dump) в AlterOS

Материал из Wiki AlterOS
Перейти к: навигация, поиск

Введение

Дамп ядра реализуется в Linux на основе сигналов. Сигнал в Linux - это механизм асинхронной обработки событий. Каждый сигнал соответствует операции обработки исключений по умолчанию. Операция по умолчанию включает игнорирование сигнала (Ignore), приостановку процесса (Stop), завершение процесса (Terminate), завершение и создание дампа памяти (Core) и так далее. Обычно дамп ядра запускается следующими сигналами: SIGABRT, SIGBUS, SIGEMPT, SIGFPE, SIGILL, SIGOT, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGXCPU, SIGXFSZ. В core dump сохраняется память сбойного процесса при его падении, а также некоторая служебная информация. При помощи функции ядра Linux, мы можем настроить генерацию и экспорт файла, формата .ELF(Executable and Linkable Format) используемый для дальнейшей диагностики и отладки ошибок в компьютерных программах.

Следующие ситуации приведут к аварийному завершению запущенного процесса и дампу ядра:

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

Настройка генерации и сохранения файлов дампа ядра

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

1.Отредактируйте конфигурационный файл /etc/security/limits.conf, определяющий размеры дампа:

   sudo nano /etc/security/limits.conf

найдите и раскомментируйте строку:

   *soft core 0

поставьте значение unlimited для того, чтобы сделать размер дампа памяти неограниченным для всех пользователей:

   *soft core unlimited

2.Отредактируйте файл /etc/sysctl.conf, определяющий место сохранения и параметры дампа, добавив путь к дампу ядра и формат файла ядра(по умолчанию основной файл будет создан в рабочем каталоге запущенного процесса):

   sudo nano /etc/sysctl.conf 

добавьте строки:

   kernel.core_pattern = /var/crash/core.%e.%p.%h.%t
   fs.suid_dumpable = 1

Здесь: /var/crash — это путь, а core.%e.%p.%h.%t — формат файла, где: %e — имя исполняемого файла(имя файла программы); %p – PID (идентификатор выгруженного процесса); %t – время дампа (в секундах с 0:00 1 января 1970 г.); %h – имя хоста.

setuid – это бит разрешения, который позволяет пользователю запускать исполняемый файл с правами владельца этого файла. Другими словами, использование этого бита позволяет нам поднять привилегии пользователя в случае, если это необходимо. Параметр fs.suid_dumpable для sysctl может принимать следующие значения:

  • 0 – отключено;
  • 1 – включено;
  • 2 – включено с ограничениями. Делает дампы ядра доступными для чтения только пользователю root.

3.Загрузите новые настройки при помощи команды:

   sudo sysctl -p

4.Чтобы собрать дампы ядра из неподписанных пакетов и неупакованного программного обеспечения, отредактируйте файл /etc/abrt/abrt-action-save-package-data.conf:

   sudo nano /etc/abrt/abrt-action-save-package-data.conf 

установите соответствующие значения в строках:

   OpenGPGCheck = no
   ProcessUnpackaged = yes

5.перезапустите демон abrtd, чтобы новые настройки вступили в силу:

   sudo systemctl restart abrtd.service
   sudo systemctl restart abrt-ccpp.service

6.Включите создание дампа командой:

   sudo ulimit -c unlimited

Теперь Alter OS готов генерировать файлы дампа ядра, когда процессы завершаются с соответствующими сигналами. Файлы, формата ELF и вида по типу core.[номер] будут генерироваться при падении процесса и сохраняться в текущем каталоге программы. Данные файлы можно использовать для анализа проблемы.

Проверка создания файла дампа ядра

1.Создайте сбойную программу для проверки сохранения дампа на языке С: создайте новую директорию:

   mkdir test

перейдите в нее:

   cd test

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

   touch main.c

откройте файл:

   nano main.c

добавьте в файл следующий код:

   int main(){
     int a = 1;
     int b = 0;
     double d = a / b;
     return 0;
   }

2.Скомпилируйте и запустите программу, которая запустит дамп ядра (Примечание: параметр -g должен быть добавлен при компиляции, что означает, что добавляется отладочная информация, чтобы отладчик gdb можно было использовать для отладки):

   gcc -g -o checkprog main.c
   sudo ./checkprog
    

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

   Исключение в операции с плавающей точкой (core dumped)

где (core dumped) будет означать, что дамп создан при падении нашей сбойной программы.

3.Убедиться, что файл создался, можно командой:

   sudo ls

В ответе терминала увидим:

   checkprog  core.4473  main.c

Чтение файла дампа

В текущем каталоге создается файл ядра, убедиться, что основным типом файла является формат ELF, вы можете запустив файл, используя команду readelf с параметром -h для просмотра информации заголовка файла ELF следующим образом:

   sudo readelf -h core.4473

В ответе терминала увидим:

   Заголовок ELF:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Класс:                             ELF64
  Данные:                            дополнение до 2, little endian
  Версия:                            1 (current)
  OS/ABI:                            UNIX - System V
  Версия ABI:                        0
  Тип:                               CORE (Основной файл)
  Машина:                            Advanced Micro Devices X86-64
  Версия:                            0x1
  Адрес точки входа:               0x0
  Начало заголовков программы:          64 (байт в файле)
  Начало заголовков программы:          0 (байт в файле)
  Флаги:                             0x0
  Размер этого заголовка:            64 (байт)
  Размер заголовков программы:       56 (байт)
  Число заголовков программы:        19
  Размер заголовков раздела:         0 (байт)
  Число заголовков раздела:          0
  Индекс табл. строк загол. раздела: 0

В поле Тип видно, что данный файл является основным файлом

Просмотр информации о сбое в работе приложения при помощи встроенного отладчика GDB

В текущем каталоге введите команду:

   sudo gdb checkprog core.4473

где checkprog - имя исполняемого файла программы, а core.4473 - файл дампа ядра. Терминал ответит выводом:

   GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7.alteros
   Copyright (C) 2013 Free Software Foundation, Inc.
   License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
   This is free software: you are free to change and redistribute it.
   There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
   and "show warranty" for details.
   This GDB was configured as "x86_64-redhat-linux-gnu".
   For bug reporting instructions, please see:
   <http://www.gnu.org/software/gdb/bugs/>...
   Reading symbols from /home/anastas/test/checkprog...done.
   [New LWP 4473]
   Core was generated by `./checkprog'.
   Program terminated with signal 8, Arithmetic exception.
   #0  0x00000000004004e3 in main () at main.c:4
   4	            double d = a / b;
   Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7.alteros.x86_64
   (gdb) quit

Команда выводит на печать причину дампа ядра, то есть арифметическое исключение,в то же время печатается проблемная строка кода result = a / b.

При помощи отладчика GDB можно отображать информацию о стеке вызовов функций с помощью bt -n (обратная трассировка), где n представляет количество отображаемых слоев стека вызовов. Но так как данная программа не включает вызовы функций, мы можем видеть только информацию о стеке основной функции.

Команда disassemble может распечатать фрагмент кода сборки при возникновении ошибки, где стрелка указывает на строку ошибки. Не выходя из режима GDB, введите команду:

   disassemble

Терминал ответит выводом:

   Dump of assembler code for function main:
      0x00000000004004cd <+0>:	push   %rbp
      0x00000000004004ce <+1>:	mov    %rsp,%rbp
      0x00000000004004d1 <+4>:	movl   $0x1,-0x4(%rbp)
      0x00000000004004d8 <+11>:	movl   $0x0,-0x8(%rbp)
      0x00000000004004df <+18>:	mov    -0x4(%rbp),%eax
      0x00000000004004e2 <+21>:	cltd   
   => 0x00000000004004e3 <+22>:	idivl  -0x8(%rbp)
      0x00000000004004e6 <+25>:	cvtsi2sd %eax,%xmm0
      0x00000000004004ea <+29>:	movsd  %xmm0,-0x10(%rbp)
      0x00000000004004ef <+34>:	mov    $0x0,%eax
      0x00000000004004f4 <+39>:	pop    %rbp
      0x00000000004004f5 <+40>:	retq   
   End of assembler dump.
   (gdb)

Где можно увидеть, что инструкция div вызывается для операции деления, делимое равно -0x8 (% ebp), что относится к значению блока памяти, в котором текущий базовый адрес стека смещен на 8 байтов, а EBP - регистр базового адреса стека. В то же время мы видим, что 0 был сохранен в блок памяти через movl $ 0x0, -0x8 (% ebp), что доказывает, что дивиденд равен 0.