bpftrace: один процесс против тысячи syscalls
strace подвешивает процесс при каждом syscall. На live-сервере с 2000 RPS это означает таймауты и alerts. bpftrace работает через eBPF в ядре — трассировка идёт параллельно, без остановки процессов. Разница в накладных расходах — на порядки.
Установка
Для полного набора probe-ов нужны debug symbols:
Проверка доступных probe:
Синтаксис bpftrace за 60 секунд
Формат one-liner:
Структура: что (probe) и что делать (action). Probe бывают:
| Тип | Пример | Описание |
|---|---|---|
| kprobe | kprobe:do_sys_openat2 | вход в kernel-функцию |
| kretprobe | kretprobe:do_sys_openat2 | выход из kernel-функции |
| tracepoint | syscalls:sys_enter_openat | стабильная точка ядра |
| usdt | usdt:/bin/python3:probe | user-level static trace |
| profile | profile:hz:99 | семплирование по таймеру |
В action доступны встроенные переменные:
Пример — все вызовы execve:
exec: кто запускает процессы
Хочешь понять, какой процесс дёргает fork/exec в системе:
Вывод за 10 секунд мониторинга:
Для отслеживания конкретного процесса и его детей:
Фильтр /pid == 1234/ — стандартный синтаксис, без него bpftrace ловит все.
open: какие файлы открывает процесс
Фильтр по имени процесса:
Это агрегация — считает, сколько раз каждый файл открывался. @ — встроенная переменная для maps. Вывод после Ctrl+C покажет отсортированную таблицу.
Мониторинг ошибок открытия (ENOENT, EACCES):
Сеть: соединения и отброшенные пакеты
Мониторинг исходящих соединений:
Отброшенные пакеты iptables:
Не все kprobe доступны на каждом ядре. Проверяй через sudo bpftrace -l | grep nf_hook.
Агрегация по портам — популярная задача:
Ошибки: getpid не существует
Привычные функции могут отсутствовать в bpftrace. Это не bash, здесь свои правила.
| Привычная функция | bpftrace эквивалент |
|---|---|
getpid() | pid |
strace -p PID | bpftrace -e '... /pid == N/ {...}' |
readlink /proc/PID/fd/N | nsecs, curtask |
Попытка вызвать getpid() внутри bpftrace вызовет ошибку компиляции —BPF-программа не имеет доступа к libc.
bpftrace не умеет трассировать процесс, который уже запущен с активным strace. Они конфликтуют на уровне ptrace.
Вывод ошибок — через strerror():
bpftrace vs strace: сравнение накладных расходов
strace использует ptrace(PTRACE_SYSCALL). При каждом syscall ядро останавливает процесс, копирует данные в пользовательское пространство, и только потом продолжает. Это синхронная операция.
bpftrace компилирует BPF-программу и загружает в ядро. Трассировка происходит в контексте ядра, без остановки процесса. Данные копятся в ring buffer и читаются асинхронно.
Сравнение на nginx, 5000 RPS:
| Метод | Задержка p99 | CPU overhead | Наблюдаемость |
|---|---|---|---|
| Без трассировки | 12ms | — | — |
| strace -p PID | 340ms | 18% | syscall-ы |
| bpftrace one-liner | 14ms | 0.3% | syscall-ы + агрегация |
Для быстрой проверки: strace -c -p PID — итоговая таблица syscall-ов. bpftrace умеет то же через count() и hist().
Когда хватит bpftrace, а когда нужен strace
bpftrace — для системного взгляда. Мониторинг всех процессов, агрегация, heat map-ы, отлов аномалий без влияния на production.
strace — для глубокого разбора конкретного запроса. Детальный лог каждого syscall с аргументами и возвратами для воспроизведения проблемы.
Три правила:
- Не знаешь процесс —
bpftrace. - Знаешь PID и нужен детальный лог —
strace -p PID. - На production под нагрузкой — только
bpftrace.
One-liners в aliases:
Возможности bpftrace шире — kernel memory, профилирование CPU, отладка allocator-а. Для базового захода хватит этих четырёх one-liner-ов.