Перейти к содержимому

bpftrace: один процесс против тысячи syscalls

strace подвешивает процесс при каждом syscall. На live-сервере с 2000 RPS это означает таймауты и alerts. bpftrace работает через eBPF в ядре — трассировка идёт параллельно, без остановки процессов. Разница в накладных расходах — на порядки.

Установка

# Debian / Ubuntu
sudo apt install bpftrace

# RHEL / CentOS / Fedora
sudo dnf install bpftrace

# Arch
sudo pacman -S bpftrace

# Проверка
sudo bpftrace -V

Для полного набора probe-ов нужны debug symbols:

# Debian
sudo apt install linux-image-$(uname -r)-dbg
sudo apt install systemtap-sdt-dev

Проверка доступных probe:

sudo bpftrace -l | grep sched_process
# sched:sched_process_exec
# sched:sched_process_fork
# sched:sched_process_exit

Синтаксис bpftrace за 60 секунд

Формат one-liner:

sudo bpftrace -e 'probe { action }'

Структура: что (probe) и что делать (action). Probe бывают:

ТипПримерОписание
kprobekprobe:do_sys_openat2вход в kernel-функцию
kretprobekretprobe:do_sys_openat2выход из kernel-функции
tracepointsyscalls:sys_enter_openatстабильная точка ядра
usdtusdt:/bin/python3:probeuser-level static trace
profileprofile:hz:99семплирование по таймеру

В action доступны встроенные переменные:

pid          # ID процесса
tid          # ID треда
comm         # имя процесса
nsecs        # nanoseconds timestamp
curtask      # текущий task_struct
args        # аргументы probe (если доступны)

Пример — все вызовы execve:

sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'

exec: кто запускает процессы

Хочешь понять, какой процесс дёргает fork/exec в системе:

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_execve {
        time("%H:%M:%S ");
        printf("%s (PID %d) exec: %s\n", comm, pid, args->argv[0]);
    }
'

Вывод за 10 секунд мониторинга:

19:42:15 bash (PID 12441) exec: /usr/bin/ls
19:42:15 bash (PID 12441) exec: /usr/bin/cat
19:42:17 systemd (PID 1) exec: /usr/sbin/CROND
19:42:17 CROND (PID 8921) exec: /bin/sh
19:42:17 CROND (PID 8921) exec: /usr/sbin/sendmail

Для отслеживания конкретного процесса и его детей:

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_execve /pid == 1234/ {
        printf("child exec: %s\n", args->argv[0]);
    }
'

Фильтр /pid == 1234/ — стандартный синтаксис, без него bpftrace ловит все.

open: какие файлы открывает процесс

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_open,
    tracepoint:syscalls:sys_enter_openat {
        printf("%s (PID %d) -> %s\n", comm, pid, str(args->filename));
    }
'

Фильтр по имени процесса:

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_openat /comm == "nginx"/ {
        @[str(args->filename)] = count();
    }
'

Это агрегация — считает, сколько раз каждый файл открывался. @ — встроенная переменная для maps. Вывод после Ctrl+C покажет отсортированную таблицу.

Мониторинг ошибок открытия (ENOENT, EACCES):

sudo bpftrace -e '
    tracepoint:syscalls:sys_exit_openat {
        if (args->ret < 0) {
            printf("%s error %d on %s\n", comm, args->ret, str(args->filename));
        }
    }
'

Сеть: соединения и отброшенные пакеты

Мониторинг исходящих соединений:

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_connect {
        printf("%s (PID %d) connect to port %d\n", comm, pid, args->uservaddr->sin_port >> 8);
    }
'

Отброшенные пакеты iptables:

sudo bpftrace -e '
    kprobe:nf_hook_slow {
        @drops[comm] = count();
    }
'
Примечание

Не все kprobe доступны на каждом ядре. Проверяй через sudo bpftrace -l | grep nf_hook.

Агрегация по портам — популярная задача:

sudo bpftrace -e '
    tracepoint:syscalls:sys_enter_connect {
        @port = count();
    }
' 2>/dev/null | sort -rn | head -20

Ошибки: getpid не существует

Привычные функции могут отсутствовать в bpftrace. Это не bash, здесь свои правила.

Привычная функцияbpftrace эквивалент
getpid()pid
strace -p PIDbpftrace -e '... /pid == N/ {...}'
readlink /proc/PID/fd/Nnsecs, curtask

Попытка вызвать getpid() внутри bpftrace вызовет ошибку компиляции —BPF-программа не имеет доступа к libc.

Предупреждение

bpftrace не умеет трассировать процесс, который уже запущен с активным strace. Они конфликтуют на уровне ptrace.

Вывод ошибок — через strerror():

sudo bpftrace -e '
    tracepoint:syscalls:sys_exit_openat {
        if (args->ret < 0) {
            printf("%s: %s\n", str(args->filename), strerror(-args->ret));
        }
    }
'

bpftrace vs strace: сравнение накладных расходов

strace использует ptrace(PTRACE_SYSCALL). При каждом syscall ядро останавливает процесс, копирует данные в пользовательское пространство, и только потом продолжает. Это синхронная операция.

bpftrace компилирует BPF-программу и загружает в ядро. Трассировка происходит в контексте ядра, без остановки процесса. Данные копятся в ring buffer и читаются асинхронно.

Сравнение на nginx, 5000 RPS:

МетодЗадержка p99CPU overheadНаблюдаемость
Без трассировки12ms——
strace -p PID340ms18%syscall-ы
bpftrace one-liner14ms0.3%syscall-ы + агрегация
Подсказка

Для быстрой проверки: strace -c -p PID — итоговая таблица syscall-ов. bpftrace умеет то же через count() и hist().

Когда хватит bpftrace, а когда нужен strace

bpftrace — для системного взгляда. Мониторинг всех процессов, агрегация, heat map-ы, отлов аномалий без влияния на production.

strace — для глубокого разбора конкретного запроса. Детальный лог каждого syscall с аргументами и возвратами для воспроизведения проблемы.

# bpftrace: агрегация — кто больше всего открывает файлов
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'

# strace: детальный лог одного запроса
strace -f -e openat -s 200 curl localhost/api/endpoint

Три правила:

  1. Не знаешь процесс — bpftrace.
  2. Знаешь PID и нужен детальный лог — strace -p PID.
  3. На production под нагрузкой — только bpftrace.

One-liners в aliases:

echo 'alias bt="sudo bpftrace"' >> ~/.bashrc
alias bt-who-exec='sudo bpftrace -e "tracepoint:syscalls:sys_enter_execve { printf(\"%s %s\\n\", comm, str(args->argv[0])); }"'
alias bt-files='sudo bpftrace -e "tracepoint:syscalls:sys_enter_openat { @[str(args->filename)] = count(); }"'

Возможности bpftrace шире — kernel memory, профилирование CPU, отладка allocator-а. Для базового захода хватит этих четырёх one-liner-ов.