Представь: ты сидишь за сервером, всё гудит, как отлаженный механизм, и вдруг — тишина. Экран заливает текст 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 и начни собирать следы. Каждый лог — это шаг к разгадке. А что может быть лучше, чем момент, когда ты наконец понимаешь, почему система упала? Это как поймать тень за хвост — сложно, но захватывающе.