Как получить содержимое буфера ядра с зависшей системы

Материал из MediaWiki
Версия от 21:39, 27 февраля 2023; Admin (обсуждение | вклад) (1 версия импортирована)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Перейти к навигации Перейти к поиску

Введение

Когда происходит неисправимая ошибка в ядре, оно переходит в состояние, называемое kernel panic. Стандартный обработчик выводит на экран информацию, которая должна помочь в решении проблемы, и входит в бесконечный цикл. Зачастую этой информации недостаточно для идентификации проблемы, а буфер ядра (dmesg) оказывается потерян. Кроме того, в силу различных причин, стандартный обработчик не всегда может переключить видеоадаптер в текстовый режим, и из той информации не видно вообще ничего.

В этой ситуации обычно советуют использовать консоль, подключенную последовательному порту (COM), так как это единственный надёжный способ получить сообщение от запаниковавшего ядра. В зависимости от характера бага, к моменту вызова panic() <ref>функция реализующая состояние kernel panic</ref> прерывания могут быть блокированы и отсылка сообщения по сети (удалённый syslog), может быть невозможна. Однако не всегда есть второй компьютер для отладки и не всегда есть COM-порт (большинство ноутбуков). В этом случае поможет использование метода "crash kernel".

В дальнейшем команды будут указаны для Debian. Предполагается, что на машине установлены пакеты fakeroot, kernel-package. <ref>Возможно понадобится что-то ещё, не могу сразу вспомнить. Правки приветствуются.</ref>

crash kernel

Вкратце метод состоит в следующем: подготавливаются два ядра, system kernel — основное и dump kernel — ядро для сбора дампа памяти. При загрузке основного ядра резервируется память, куда впоследствии загружается dump kernel. В функции panic() есть код, загружающий dump kernel с помощью kexec. Так как память под dump kernel резервируется заранее, при загрузке оно не трогает память основного ядра, но имеет к нему доступ через специальный файл /proc/vmcore.

System- и dump- ядра могут быть одним и тем же ядром.

Требования к system kernel

Нужно включить:

  1. "kexec system call" в разделе "Processor type and features." (CONFIG_KEXEC=y)
  2. "sysfs file system support" в разделе "Filesystem" → "Pseudo filesystems" (CONFIG_SYSFS=y)
  3. "Compile the kernel with debug info" в разделе "Kernel hacking" (CONFIG_DEBUG_INFO=Y)

Требования к dump kernel

Нужно включить:

  1. поддержку high memory в разделе "Processor type and features" (CONFIG_HIGHMEM64G=y или CONFIG_HIGHMEM4G)
  2. "Build a relocatable kernel" в разделе "Processor type and features" (CONFIG_RELOCATABLE=y)
  3. "kernel crash dumps" в разделе "Processor type and features" (CONFIG_CRASH_DUMP=y)
  4. "/proc/vmcore support" в разделе "Filesystems" → "Pseudo filesystems" (CONFIG_PROC_VMCORE=y)

По желанию, можно выключить SMP (CONFIG_SMP=n). Можно не выключать, а загружать dump ядро с параметром maxcpus=1. "high memory" для x86_64 не нужно.

Сборка ядра

В некоторых дистрибутивах эти параметры задействованы по умолчанию, и всё работает из коробки <ref>вроде бы, в RHEL</ref>, но в Debian это не так, ядро нужно компилировать вручную. Два ядра собирать не обязательно, будем собирать одно.

Желательно использовать максимально близкую версию ядра, либо взять версию из дистрибутива, конфигурацию взять текущую. Далее, как обычно:

$ make menuconfig

устанавливаем параметры, указанные и для system и для dump ядер. Собираем собственно ядро:

$ CONCURRENCY_LEVEL=3 make-kpkg --revision 1 --append-to-version -crsh --initrd --rootcmd fakeroot kernel_image kernel_headers

Если нет необходимости собирать сторонние модули под новое ядро, параметр kernel_headers можно опустить. После запуска компиляции можно расслабиться, откинуться на кресле и наблюдать за процессом компиляции. Через некоторое время рядом с директорией с исходниками ядра будет сформировано два (один, если опущено kernel_headers) .deb файла. Это ядро с модулями и заголовки ядра. Оба нужно установить. Из директории, в которой проходила сборка, для отладки могут понадобиться: файл vmlinux — несжатое ядро с отладочными символами, файлы модулей — модули с отладочными символами. <ref>Мой опыт закончился на вытаскивании буфера ядра, поэтому что с ними делать, в подробностях я не знаю.</ref>

Настройка загрузки

Dump ядро загружается в специальную область памяти, которую нужно специально выделить при загрузке основного ядра. Это можно сделать, указав в параметрах ядра (в загрузчике)

crashkernel=128M

Эта опция откусывает 128 МиБ оперативной памяти, резервируя её для dump ядра. Опыт показывает, что указывать значения, меньшие 128 МиБ для x86_64 не имеет смысла, так как при меньшем объёме (проверялось =64 МиБ) и на стабильном железе ядро может выдать несколько oops'ов ввиду нехватки памяти. Через @ можно указать точное место, куда будет грузиться ядро, но с включенной опцией CONFIG_RELOCATABLE в этом нет необходимости.

После того, как память выделена, нужно настроить загрузку ядра и сохранение дампа. Большинство действий автоматизируется с использованием пакетов kexec-tools и kdump-tools, их нужно установить:

# apt-get install kexec-tools kdump-tools

kexec-tools содержит /sbin/kexec, используемую для загрузки ядер и как бонус — возможность быстрой перезагрузки (без запуска BIOS POST). В kdump-tools есть всё необходимое для снятия дампа памяти и автоматической загрузки dump ядра в зарезервированную память.

Для того, чтобы всё это заработало, нужно в файле /etc/default/kdump заменить USE_KDUMP=0 на USE_KDUMP=1.

Перезагрузиться. Теперь при падении ядра в панику будет загружаться dump ядро, в котором автоматически будет производиться сохранения дампа памяти в /var/crash.

Особые настройки

Настройки по умолчанию - это скучно. К тому же не всегда нужно сохранять всю память, даже в сжатом виде это может занять много места. Мне нужно было сохранять только буфер ядра, причём делать это в автоматическом режиме — видеоадаптер не инициализируется, ничего не видно и приходится действовать вслепую.

Для реализации этого нужно в /etc/default/kdump-tools дописать:

KDUMP_CMDLINE_APPEND="irqpoll maxcpus=1 nousb panicreboot 4 "

а в /etc/rc.local добавить:

if grep panicreboot /proc/cmdline > /dev/null 2>&1 ; then
    echo "panic reboot at `date`" >> /var/log/panic.log
    LOGDIR=/var/crash/`date +%Y-%m-%d_%H-%M-%S`
    mkdir -p $LOGDIR
    /usr/bin/makedumpfile --dump-dmesg /proc/vmcore $LOGDIR/dmesg.log
    /bin/sync
    echo panic logging done
    /sbin/poweroff
fi

код в /etc/rc.local проверяет, есть ли в параметрах ядра строка panicreboot и если есть, сохраняет буфер ядра и выключает компьютер. Добавленная в /etc/default/kdump-tools строчка передаёт dump ядру параметр panicreboot и устанавливает целевой runlevel 4. Польза от загрузки в runlevel отличный от основного состоит в возможности отключить все ненужные для сброса дампа сервисы. Это можно сделать при помощи утилиты sysv-rc-conf.

Возможные ошибки

В некоторых версиях (обнаружено в 3.2.x) CONFIG_DEBUG_PAGEALLOC=y приводит к неработоспособности panic kexec. Обычная перезагрузка через kexec работает.

Что делать дальше

Если всё сделано правильно, у вас на руках есть буфер ядра со всеми сообщениями вплоть до вызова panic(). В большинстве случаев это указывает на непосредственную причину неисправности. Если вы сохранили дамп памяти, можно загрузить его в gdb как обычный core файл (для этого и понадобится vmlinux с символами) и посмотреть backtrace всех нитей. Возможно это даст более подробную картину.

Альтернативный вариант (Если нужно только текст падения - регистр и стек)

Можно воспользоваться ramoops (начиная с 3.10)

Настройка проста

1) Конфигурация ядра (в Miscellaneous filesystems )

CONFIG_PSTORE_CONSOLE=y

CONFIG_PSTORE_RAM=m|y

2) Параметры ядра

memmap=256K$0xfc0000 ramoops.mem_address=0xfc0000 ramoops.mem_size=0x40000

Если используется grub или grub2, то не забывайте экранировать «$»

3) При старте системы

[ -d /dev/pstore ] || mkdir /dev/pstore || exit 1
mount -t pstore none /dev/pstore
#если ramoops модуль то
modprobe ramoops mem_address=0xfc0000 mem_size=0x40000

#получаем старые сообщения с консоли и если был дамп, то его сообщения
D="`date +%Y%m%d%H%M%S`"
[ -f /dev/pstore/console-ramoops ] && cp /dev/pstore/console-ramoops /var/log/ramoops.$D.console-ramoops
for i in /dev/pstore/dmesg-ramoops-* ; do
    [ -n "$i" -a -f "$i" ] || continue
    cp "$i" /var/log/ramoops.$D.${i##*/} && rm "$i"
done
#включаем "kernel panic"
echo 30 >/proc/sys/kernel/panic
# паника при первом oops (нужна не всем)
echo 1 >/proc/sys/kernel/panic_on_oops
echo 1 >/proc/sys/kernel/panic_on_io_nmi
echo 1 >/proc/sys/kernel/panic_on_unrecovered_nmi

Ссылки

  1. http://www.opennet.ru/base/sys/rhel_kdump_kexec.txt.html
  2. http://www.mjmwired.net/kernel/Documentation/kdump/

Сноски

<references/>