strace показывает системные вызовы на входе и на выходе. То, что происходит внутри - остаётся чёрным ящиком. Когда приложение подвисает на системном вызове на несколько секунд, strace сообщает: "вызов был, вызов завершился". Что происходило между этими двумя событиями - молчание. Именно для ответа на такие вопросы и существует ftrace.
ftrace появился в ядре Linux в 2008 году в версии 2.6.27. Его создал Стивен Ростедт на основе более ранних инструментов измерения задержек. За эти годы из простого трассировщика функций он превратился в полноценный фреймворк наблюдения за ядром: трассировка вызовов, граф выполнения функций, измерение латентности прерываний, профилирование стека, отслеживание событий планировщика. Всё это без дополнительных пакетов, без отдельных daemon-процессов и без перекомпиляции ядра в базовых сценариях.
Как ftrace устроен внутри и почему его overhead близок к нулю
Реализация ftrace опирается на механизм компиляторной инструментации. При сборке ядра с опцией CONFIG_FTRACE компилятор вставляет вызов функции mcount в начало каждой ядерной функции. В обычном режиме эти вызовы заменяются инструкциями NOP - буквально "ничего не делать". Накладные расходы при выключенном трассировщике практически нулевые, потому что NOP выполняется за один такт процессора.
Когда трассировка включается, ftrace на лету перезаписывает NOP реальными инструкциями перехода к обработчику. Это называется dynamic ftrace и требует опции CONFIG_DYNAMIC_FTRACE. Именно этот механизм делает ftrace безопасным для production-систем: накладные расходы появляются только там и тогда, где трассировка активна.
Интерфейс управления ftrace реализован через виртуальную файловую систему tracefs, которая монтируется по умолчанию:
# Проверить монтирование
mount | grep tracefs
# Если не смонтирована - смонтировать вручную
mount -t tracefs nodev /sys/kernel/tracing
# Перейти в рабочую директорию
cd /sys/kernel/tracing
Всё управление трассировщиком - это чтение и запись обычных файлов через echo и cat. Никакого специального синтаксиса, никаких бинарных форматов. Посмотреть доступные трассировщики на конкретном ядре:
cat /sys/kernel/tracing/available_tracers
# function_graph wakeup_dl wakeup_rt wakeup preemptirqsoff irqsoff function nop
Трассировщик function и первый запуск за пять команд
Самый простой способ понять ftrace - запустить базовый трассировщик функций и посмотреть на поток событий. Это занимает ровно пять команд:
cd /sys/kernel/tracing
# Включить трассировщик функций
echo function > current_tracer
# Запустить запись
echo 1 > tracing_on
# Подождать 1-2 секунды, затем остановить
echo 0 > tracing_on
# Прочитать результат
cat trace | head -30
Вывод покажет поток ядерных функций с временными метками, номерами CPU и именами задач. Объём данных окажется огромным - ядро вызывает сотни тысяч функций в секунду. Именно поэтому первое, чему учатся при работе с ftrace, - фильтрация.
Ограничить трассировку конкретными функциями или паттернами:
# Трассировать только функции ext4
echo 'ext4_*' > set_ftrace_filter
# Добавить ещё один паттерн к существующим
echo 'vfs_read' >> set_ftrace_filter
# Посмотреть активные фильтры
cat set_ftrace_filter
# Исключить функции из трассировки
echo 'schedule' > set_ftrace_notrace
# Сбросить все фильтры
echo > set_ftrace_filter
Трассировать конкретный процесс по PID, не засоряя вывод событиями других задач:
echo $$ > set_ftrace_pid
echo function > current_tracer
echo 1 > tracing_on
# запустить команду для трассировки
ls /proc
echo 0 > tracing_on
cat trace
После каждого сеанса трассировки буфер нужно очищать, иначе новые данные смешаются со старыми:
echo > trace
function_graph и визуализация дерева вызовов
Трассировщик function показывает плоский поток вызовов. Трассировщик function_graph делает значительно больше: он отображает вложенность вызовов в виде дерева и измеряет время выполнения каждой функции. Это принципиально меняет читаемость вывода.
echo function_graph > current_tracer
echo 1 > tracing_on
sleep 1
echo 0 > tracing_on
cat trace | head -40
Вывод function_graph выглядит так, словно кто-то нарисовал дерево вызовов от руки. Отступы показывают глубину вложенности, время в микросекундах - справа от каждой функции. Функции с долгим временем выполнения сразу бросаются в глаза.
Ограничить глубину трассировки, чтобы не утонуть в деталях:
# Трассировать не глубже 3 уровней
echo 3 > max_graph_depth
# Трассировать только граф вызовов tcp_sendmsg и всего, что она вызывает
echo tcp_sendmsg > set_graph_function
echo function_graph > current_tracer
echo 1 > tracing_on
Латентные трассировщики для диагностики RT-систем и задержек планировщика
Отдельный класс трассировщиков в ftrace спроектирован специально для измерения латентности. Они работают иначе: не записывают всё подряд, а непрерывно ищут максимальную задержку и сохраняют трассу только для неё. Каждый раз, когда обнаруживается задержка больше предыдущего максимума, старая трасса заменяется новой.
Трассировщик irqsoff фиксирует максимальное время, на которое были отключены прерывания. Это критически важная метрика для real-time систем: отключённые прерывания означают, что никакое внешнее событие не может быть обработано в это время.
echo irqsoff > current_tracer
echo 1 > tracing_on
# Запустить нагрузку, которую нужно проверить
stress-ng --cpu 4 --timeout 10s
echo 0 > tracing_on
cat trace
Трассировщик wakeup измеряет задержку от момента пробуждения высокоприоритетной задачи до момента, когда планировщик реально передаёт ей управление:
echo wakeup > current_tracer
echo 1 > tracing_on
# ждём несколько секунд
echo 0 > tracing_on
cat tracing_max_latency
cat trace
Файл tracing_max_latency содержит текущий максимум в микросекундах. Сбросить счётчик и начать измерение заново:
echo 0 > tracing_max_latency
preemptirqsoff - наиболее полный из латентных трассировщиков: он отслеживает максимальное время, когда одновременно отключены и прерывания, и вытеснение планировщика. Именно это время определяет реальную латентность системы в худшем случае.
trace-cmd как удобная обёртка над tracefs
Прямая работа с файлами в /sys/kernel/tracing - это точность и контроль, но и многословность. Для повседневных задач существует утилита trace-cmd, созданная тем же Стивеном Ростедтом. Она автоматизирует типичные сценарии и сохраняет результаты в бинарный файл для последующего анализа.
Установка:
apt install trace-cmd # Debian/Ubuntu
dnf install trace-cmd # Fedora/RHEL
Базовые сценарии с trace-cmd:
# Записать трассу функций во время выполнения команды
trace-cmd record -p function myapp
# Записать только функции планировщика
trace-cmd record -p function -l 'sched_*' myapp
# Запись с function_graph трассировщиком
trace-cmd record -p function_graph -g tcp_sendmsg -- sleep 5
# Посмотреть результат
trace-cmd report
# Запустить трассировщик irqsoff
trace-cmd start -p irqsoff
trace-cmd stop
trace-cmd show
trace-cmd особенно полезен, когда нужно сохранить трассу с одной машины и проанализировать на другой. Бинарный файл trace.dat переносится и открывается графической утилитой KernelShark, которая рисует временные линии событий на всех CPU.
Сброс состояния и безопасная работа с трассировщиком
Одна из частых ошибок при работе с ftrace - забыть вернуть систему в исходное состояние. Активный трассировщик не опасен для стабильности, но потребляет ресурсы и может влиять на производительность. Правильная процедура сброса:
# Отключить запись
echo 0 > tracing_on
# Вернуть пустой трассировщик
echo nop > current_tracer
# Очистить буфер
echo > trace
# Сбросить фильтры функций
echo > set_ftrace_filter
echo > set_ftrace_notrace
echo > set_ftrace_pid
# Сбросить фильтры graph
echo > set_graph_function
Хорошей практикой является оформление всего сеанса трассировки в виде shell-скрипта, который настраивает трассировщик, запускает тест и гарантированно выполняет сброс в конце через trap. Это избавляет от ситуации, когда трассировщик остаётся активным после прерывания скрипта по Ctrl+C.
ftrace - это инструмент, который живёт прямо в ядре и не требует ничего, кроме доступа к смонтированной tracefs. Системный администратор, столкнувшийся с необъяснимой задержкой в production, разработчик модуля ядра, который хочет понять последовательность вызовов, инженер реального времени, измеряющий задержки прерываний - у каждого из них разные вопросы, но один инструмент. Умение читать вывод трассировщика и правильно формулировать фильтры отличает тех, кто действительно понимает, что происходит внутри системы, от тех, кто только догадывается.