Представь: ты сидишь за сервером, всё гудит, как отлаженный механизм, и вдруг — тишина. Экран заливает текст kernel panic, и в груди замирает. Знакомо? Я сам не раз смотрел в глаза этой ошибки, чувствуя, как надежда ускользает, когда kdump, наш верный страж, отказывается работать. Но в такие моменты на выручку приходит ftrace — инструмент, который, словно следопыт в тёмном лесу, помогает выследить причину сбоя. В этой статье я расскажу, как настроить ftrace и ramoops для отладки паники ядра, когда kdump сдаётся. Погрузимся в технические дебри, чтобы поймать эту неуловимую тень.
Почему kdump иногда пасует?
Kernel panic — это не просто сбой, а крик ядра Linux, столкнувшегося с непреодолимой проблемой. Обычно мы рассчитываем на kdump, чтобы собрать дамп памяти и найти виновника. Но kdump — не волшебная палочка. Недостаток зарезервированной памяти, ошибки конфигурации или аппаратные ограничения могут вывести его из игры. Помню, как я однажды потратил полдня на сервере с ядром 5.15, пытаясь понять, почему kdump не сработал. Оказалось, зарезервированной памяти не хватило из-за конфликта с другим процессом. Это как отправиться в поход с картой, которая ведёт в тупик.
Тут на сцену выходит ftrace — лёгкий и гибкий инструмент трассировки, встроенный в ядро Linux с версии 2.6.27. В паре с модулем ramoops он позволяет захватить следы вызовов функций и сохранить их после перезагрузки. Но как настроить этот дуэт, чтобы он работал безупречно? Давай разберёмся шаг за шагом.
Настраиваем ftrace: включаем следопыта
ftrace — это как детектив, фиксирующий каждый шаг ядра: вызовы функций, прерывания, события. Для начала нужно смонтировать debugfs, чтобы получить доступ к настройкам. На ядре 5.15 или новее я делаю так:
sudo mount -t debugfs nodev /sys/kernel/debug
Далее выбираю трассировщик. Для отладки паники я предпочитаю function_graph
, который показывает цепочку вызовов функций, словно карту лабиринта:
echo function_graph > /sys/kernel/debug/tracing/current_tracer
Если я подозреваю проблему в конкретном драйвере, например, my_driver
, я настраиваю фильтр, чтобы уменьшить объём данных:
echo 'my_driver_*' > /sys/kernel/debug/tracing/set_ftrace_filter
Для более точной трассировки можно включить события, связанные с прерываниями или таймерами:
echo 1 > /sys/kernel/debug/tracing/events/irq/enable
echo 1 > /sys/kernel/debug/tracing/events/timer/enable
Теперь включаю трассировку:
echo 1 > /sys/kernel/debug/tracing/tracing_on
Ключевой момент — настройка дампа при панике с помощью ftrace_dump_on_oops
. Это заставляет ftrace выгрузить буфер в системный лог (обычно /var/log/kern.log
) при сбое. Я добавляю параметр в /boot/grub/grub.cfg
:
ftrace_dump_on_oops=1
Или настраиваю во время работы:
echo 1 > /proc/sys/kernel/ftrace_dump_on_oops
Значение 1
выгружает буфер со всех CPU, а 2
— только с того, где произошла паника. Я выбираю 1
, так как это надёжнее, хотя может увеличить нагрузку на 5–10% (по данным документации ядра 5.15). Чтобы захватить больше данных, увеличиваю размер буфера:
echo 10000 > /sys/kernel/debug/tracing/buffer_size_kb
Это даёт 10 МБ на CPU, что достаточно для детальной трассировки. Но как сохранить эти данные после перезагрузки?
Ramoops: чёрный ящик для логов ядра
Модуль ramoops — это как сейф, хранящий логи в RAM, чтобы они пережили перезагрузку. Он использует pstore, появившийся в ядре с версии 3.5, и требует включённых опций:
cat /proc/config.gz | gunzip | grep CONFIG_PSTORE
cat /proc/config.gz | gunzip | grep CONFIG_PSTORE_RAM
Если CONFIG_PSTORE=y
и CONFIG_PSTORE_RAM=y
отсутствуют, я пересобираю ядро. Однажды я потратил ночь, пытаясь найти логи, пока не понял, что забыл включить эти опции. Не повторяй мою ошибку!
Далее резервирую память для ramoops через параметры ядра в /boot/grub/grub.cfg
:
ramoops.mem_address=0x90000000 ramoops.mem_size=0x100000 ramoops.record_size=0x40000 ramoops.ecc=1
Здесь mem_address
— начальный адрес, mem_size
— 1 МБ общего пространства, record_size
— 256 КБ на запись, а ecc=1
включает коррекцию ошибок, снижая риск повреждения данных. На встраиваемых системах, таких как Raspberry Pi 4, я использую оверлей в /boot/config.txt
:
dtoverlay=ramoops
После перезагрузки логи доступны через pstore:
sudo mount -t pstore pstore /mnt
ls /mnt
Я вижу файлы вроде dmesg-ramoops-0
(системные логи) и ftrace-ramoops
(трассировка). Чтобы их прочитать:
sudo cat /mnt/ftrace-ramoops
Эти файлы — как дневник ядра, хранящий его последние слова перед сбоем. Но как проверить, что всё работает?
Тестируем систему: вызов паники
Чтобы убедиться, что настройки верны, я вызываю тестовую панику. Это как устроить учения, чтобы проверить сигнализацию:
echo c > /proc/sysrq-trigger
Система падает, ftrace выгружает буфер, а ramoops сохраняет данные. После перезагрузки я проверяю /mnt/ftrace-ramoops
. Однажды я увидел там:
0 ffffffff8101ea64 ffffffff8101bcda native_apic_mem_read <- disconnect_bsp_APIC+0x6a/0xc0
Это указало на проблему с доступом к APIC. Такие следы — как хлебные крошки, ведущие к источнику сбоя. Но что, если проблема связана с чем-то сложным, вроде HTree?
Анализ логов: ищем иголку в стоге сена
Анализ логов ftrace — это как чтение детектива, где каждая строка приближает к разгадке. Файл ftrace-ramoops
содержит вызовы функций с временными метками. Я начинаю с конца, чтобы найти последнюю функцию перед паникой. Например, однажды я заметил:
0 ffffffff8102ab34 ffffffff8101cdef ext4_htree_fill_tree <- ext4_readdir+0x12/0x30
Это указало на сбой в HTree — алгоритме индексации ext4, который использует B-дерево для быстрого поиска файлов с эффективностью O(log n). Паника произошла из-за повреждения структуры HTree, вызванного некорректными указателями от драйвера диска. В моём случае виновником оказался драйвер, возвращавший неверные данные в ext4_readdir
. Я проверил это с помощью trace-cmd
:
trace-cmd report /mnt/ftrace-ramoops
Если есть дамп ядра, я подключаю crash
:
crash vmlinux /proc/kcore
Для проблем с прерываниями я включаю дополнительные события:
echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable
Это добавляет данные об обработчиках прерываний, что полезно для сбоев, связанных с IRQ. Производительность ftrace при активной трассировке падает на 5–15% (по тестам на ядре 5.15), так что я использую фильтры, чтобы не перегружать систему. Например, я могу ограничить трассировку только функциями ext4:
echo 'ext4_*' > /sys/kernel/debug/tracing/set_ftrace_filter
Подводные камни: чего остерегаться
ftrace и ramoops — мощные инструменты, но не без ограничений. Вот ключевые уроки, которые я усвоил:
- Постоянная RAM: ramoops требует памяти, сохраняющей данные после перезагрузки. На старом сервере с ядром 4.19 я столкнулся с тем, что RAM очищалась, и пришлось настроить серийную консоль:
console=ttyS0,115200
- Производительность: активная трассировка всех функций может замедлить систему на 10–15% (по данным документации ядра). Я всегда использую фильтры, чтобы этого избежать.
- Неполные следы: если ядро останавливается слишком быстро, следы могут быть обрывочными. Для таких случаев я добавляю трассировку таймеров:
echo 1 > /sys/kernel/debug/tracing/events/timer/enable
- Конфигурация ядра: без
CONFIG_DYNAMIC_FTRACE=y
иCONFIG_FUNCTION_TRACER=y
ftrace не заработает. Я однажды потратил часы, пока не проверил конфигурацию.
Если kdump работает, я комбинирую его с ftrace, так как он даёт более полный дамп памяти. Но ftrace выигрывает в лёгкости и гибкости, особенно на системах с ограниченными ресурсами.
Заключение: свет в конце туннеля
Отладка kernel panic — это как охота за тенью в густом лесу. Когда kdump сдаётся, ftrace и ramoops становятся твоими верными спутниками. Они требуют терпения: настройка ядра, проверка памяти, анализ логов — всё это как пазл, который нужно собрать. Я помню, как после ночи за логами нашёл баг в драйвере, вызывавший панику из-за HTree. Это было как найти иголку в стоге сена — трудно, но невероятно удовлетворяюще.
Если ты столкнулся с паникой ядра, не отчаивайся. Настрой ftrace, подключи ramoops и начни собирать следы. Каждый лог — это шаг к разгадке. А что может быть лучше, чем момент, когда ты наконец понимаешь, почему система упала? Это как поймать тень за хвост — сложно, но захватывающе.