Представь типичную боль. На сервере раз в минуту скачет нагрузка, top показывает чистый CPU, а что-то всё равно жрёт ресурсы. Или приложение тормозит на старте, а ты не понимаешь, какие файлы оно дёргает. Классический ответ - strace. Но strace тяжёлый: он перехватывает каждый системный вызов через ptrace и на каждом дважды перекидывает процесс из user space в ядро и обратно. На горячем коде это легко раздувает latency в 10-100 раз. На нагруженном сервисе запустить strace - почти всегда плохая идея, а strace -f на форк-бомбе вообще может его придушить.
И тут на сцену выходит eBPF. Если коротко: eBPF - это способ запускать маленькие безопасные программы прямо внутри ядра Linux, не патча его и не перезагружаясь. Перед загрузкой каждую такую программу проверяет верификатор ядра (никаких бесконечных циклов, никакого выхода за границы памяти), поэтому уронить ядро ей нельзя. Ядро само считает события (запуски процессов, открытия файлов, дисковые операции) и сразу агрегирует их - тебе наверх отдаётся уже готовая сводка или гистограмма, а не поток сырых строк. Накладные расходы крошечные, поэтому eBPF можно крутить на живом проде. Это и есть прод-безопасная замена strace.
Хорошая новость: тебе не надо писать eBPF-программы руками. Уже есть наборы готовых инструментов, которые решают типовые задачи буквально в одну команду - BCC tools и bpftrace. Их и разбираем. По состоянию на 2026 год это базовый инструментарий любого Linux perf/SRE-инженера, и оба набора давно в штатных репозиториях дистрибутивов.

BCC tools: диагностика в одну команду
BCC (BPF Compiler Collection) - это пакет с десятками готовых утилит. В Debian/Ubuntu он ставится так:
Код: Выделить всё
sudo apt install bpfcc-tools linux-headers-$(uname -r)Что важно знать в 2026. Классические BCC-утилиты написаны на Python и компилируют eBPF-программу под твоё ядро прямо в момент запуска через LLVM/Clang - отсюда требование linux-headers и заметное потребление памяти (десятки-сотни МБ на запуск). Сейчас сообщество переходит на libbpf-tools: это те же execsnoop/opensnoop/biolatency, но переписанные на C с механизмом CO-RE (Compile Once - Run Everywhere) поверх BTF. Они компилируются один раз в маленький статический бинарник, не тянут на прод ни Python, ни Clang, ни заголовки ядра, стартуют мгновенно и едят килобайты вместо мегабайт. В Debian/Ubuntu это пакет libbpf-tools, в свежих RHEL/Fedora многие из них уже идут именно в таком виде. Требование одно: ядро должно быть собрано с BTF (CONFIG_DEBUG_INFO_BTF=y, по умолчанию во всех актуальных ядрах). Если видишь оба варианта - на проде предпочитай libbpf-tools. Поведение и колонки у них совпадают с тем, что разберём ниже.
Пройдёмся по самым полезным.
execsnoop - показывает каждый новый запуск процесса (перехватывает execve/execveat). Это ровно то, что не ловит top: короткоживущие процессы, которые родились и умерли между его опросами. Идеально для "кто-то форкает что-то из крона" или "что за процесс мелькает в нагрузке".
Код: Выделить всё
$ sudo execsnoop
PCOMM PID PPID RET ARGS
sh 18923 1 0 /bin/sh -c /usr/bin/backup.sh
backup.sh 18924 18923 0 /usr/bin/backup.sh
gzip 18925 18924 0 /bin/gzip -9opensnoop - кто какие файлы открывает (перехватывает open/openat). Незаменимо, когда надо понять, где приложение ищет конфиг или почему ломится не туда.
Код: Выделить всё
$ sudo opensnoop
PID COMM FD ERR PATH
1456 nginx 12 0 /etc/nginx/nginx.conf
1456 nginx -1 2 /etc/nginx/sites/secret.conf
2201 php-fpm 8 0 /var/www/app/.envbiolatency - задержки дисковых операций (block I/O) в виде гистограммы. Когда жалуются "диск тормозит", это первый инструмент. Запусти с интервалом и числом выводов - тут раз в 10 секунд, один вывод:
Код: Выделить всё
$ sudo biolatency 10 1
usecs : count distribution
128 -> 255 : 12 |*** |
256 -> 511 : 145 |****************************************|
512 -> 1023: 60 |**************** |
1024 -> 2047: 4 |* |Ещё несколько утилит, которые стоит знать на 2026:
- tcpconnect - исходящие TCP-соединения (кто куда коннектится), tcpconnlat - задержка установки соединения, tcplife - завершившиеся соединения с длительностью и переданными байтами (удобно ловить долгие или болтливые сессии), tcpretrans - повторные передачи (первый признак потерь в сети).
- runqlat - гистограмма задержки планировщика (сколько поток ждал в очереди готовых, прежде чем получил CPU). Растёт - значит ядер не хватает или есть троттлинг. Рядом runqlen (длина очереди по CPU).
- profile - семплит стеки по всем CPU на основе perf-событий, из его вывода строят флеймграф (тема отдельного урока).
- cachestat - попадания/промахи page cache, cachetop - то же по процессам.
- funccount, argdist, trace - универсальные конструкторы, когда готовой утилиты нет, но не хочется писать целый bpftrace-скрипт.
BCC хорош, когда есть готовая утилита под твою задачу. А если нет? Тогда берёшь bpftrace - это маленький язык, на котором свой инструмент пишется одной строкой. По духу он как awk, только событиями для него служат не строки файла, а события ядра. Ставится пакетом bpftrace. На 2026 актуальная ветка - 0.23/0.24; нужно ядро хотя бы 4.9, а практически всё интересное живёт на 5.x и новее. Свежие версии умеют булевы литералы (true/false), range-based циклы for, встроенный ncpus, syscall_name() для перевода номера вызова в имя - но для разбора ниже хватит и базы.
Синтаксис простой: проба /фильтр/ { действие }.
- Проба - что ловим. Например tracepoint:syscalls:sys_enter_openat - вход в системный вызов открытия файла. Сокращённо t:. Бывают пробы kprobe (любая функция ядра), uprobe (функция в пользовательской программе), tracepoint (стабильные точки ядра, лучший выбор), profile/interval (по таймеру).
- Фильтр (необязательный) - условие в /слешах/, действие сработает только если оно истинно. Например /comm == "nginx"/.
- Действие - что делать. Чаще всего складывать в @ - это карта (map), которая копит данные прямо в ядре, не выгружая каждое событие в user space.
Несколько bpftrace примеров, которые реально пригодятся.
Посчитать, какие процессы делают больше всего системных вызовов (готовый аналог - утилита syscount):
Код: Выделить всё
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'Свой execsnoop в одну строку - кто запускает процессы:
Код: Выделить всё
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%-16s -> %s\n", comm, str(args->filename)); }'Гистограмма размеров чтений - сколько байт за один read() утаскивают процессы:
Код: Выделить всё
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @bytes = hist(args->ret); }'Измерить задержку конкретного syscall - вход и выход связываем через временную метку в карте:
Код: Выделить всё
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @t[tid] = nsecs; }
tracepoint:syscalls:sys_exit_openat /@t[tid]/ { @ns = hist(nsecs - @t[tid]); delete(@t[tid]); }'Когда bpftrace и BCC бьют strace и perf? Когда нужна агрегация в ядре (миллион событий схлопывается в одну гистограмму, а не сыплется построчно), низкий overhead (можно на проде) и широкий охват (видно сразу все процессы, а не один прицепленный). strace по-прежнему хорош для глубокого ручного разбора одного процесса с расшифровкой структур аргументов в тестовой среде, а perf - для точного профилирования по событиям PMU. Но как массовый прод-инструмент наблюдаемости eBPF их обходит.
Грабли и заблуждения
- "Команда не найдена". В Debian/Ubuntu имена с суффиксом -bpfcc (execsnoop-bpfcc), в Fedora/RED OS - без. Смотри список в /usr/share/bcc/tools/. Если есть libbpf-tools - там имена без суффикса и отдельный каталог.
- Запуск без sudo. eBPF-трейсинг грузит программу в ядро: нужен root или связка CAP_BPF + CAP_PERFMON (обе появились в ядре 5.8). Без прав получишь ошибку загрузки, а не результат. В контейнере под Kubernetes для bcc/bpftrace часто требуется privileged или явная выдача этих capability плюс доступ к /sys/kernel/debug.
- Нет заголовков ядра (только для Python-BCC). Классический BCC компилирует программу под твоё ядро на лету, поэтому нужен пакет linux-headers под текущий uname -r. Забыл - получишь длинную ошибку компиляции. У libbpf-tools этой проблемы нет вообще: им нужен только BTF в ядре.
- Ждать вывод сразу. biolatency, runqlat, syscount, любые карты в bpftrace копят данные и печатают итог по Ctrl-C или по таймеру. Пустой экран первые секунды - это норма, а не зависание. execsnoop/opensnoop, наоборот, печатают построчно в реальном времени.
- Старое ядро. На ядрах до 4.x eBPF-трейсинг практически не работает. По-настоящему он живёт с 4.9+, а большинство современных утилит и CO-RE - с 5.x. На 2026 это уже не проблема для актуальных дистрибутивов, но всплывает на legacy-серверах.
- "eBPF видит всё в зашифрованном трафике/в TLS". Нет. tcplife и подобные видят метаданные соединений и объёмы, а не расшифрованный payload. Для прикладного содержимого нужны uprobe на функции библиотек.
- Путаница probe-фильтр. Фильтр в /слешах/ ставится между пробой и блоком { }, а не внутри блока. Условие внутри блока пишется через if, но для отбора событий правильнее фильтр - он отсекает раньше и дешевле.
Открой два терминала.
- В первом запусти sudo execsnoop. Во втором выполни ls, потом sleep 1, потом date - и смотри, как они появляются в execsnoop с правильными PID и PPID. Найди свой шелл в колонке PPID.
- Запусти sudo opensnoop -x и в другом терминале сделай cat /no/such/file - найди свою строку с ERR равным 2 (ENOENT).
- Запусти sudo biolatency 5 1 и параллельно создай нагрузку: dd if=/dev/zero of=/tmp/test bs=1M count=200 oflag=direct - посмотри, как наполнилась гистограмма (после убери rm /tmp/test).
- Финал: sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }', подожди 10 секунд, Ctrl-C - найди самый болтливый процесс (он внизу таблицы).
- Почему execsnoop ловит процессы, которые не видит top?
- В выводе opensnoop строка с FD равным -1 и ERR равным 2 - что это значит и куда смотреть дальше?
- Из чего состоит однострочник bpftrace (три части) и зачем нужен символ @?
- Почему biolatency и bpftrace безопаснее запускать на проде, чем strace? Назови хотя бы две причины.
- Чем libbpf-tools лучше классических Python-BCC на проде и какое единственное требование к ядру у них есть?
eBPF считает события внутри ядра и отдаёт готовую сводку - это дёшево и безопасно для прода, в отличие от strace на ptrace. BCC tools (пакет bpfcc-tools, каталог /usr/share/bcc/tools) - готовые утилиты на каждый день: execsnoop ловит короткоживущие процессы, opensnoop показывает обращения к файлам и их ошибки, biolatency и runqlat рисуют гистограммы задержек диска и планировщика, tcpconnect/tcplife/tcpretrans следят за сетью. В 2026 на прод тяни libbpf-tools (CO-RE поверх BTF) - они не требуют заголовков ядра и почти ничего не весят. Когда готового инструмента нет - bpftrace пишет его одной строкой по схеме проба-фильтр-действие, агрегируя данные через @ и count()/hist() прямо в ядре. И главное правило: не вываливай команду ради команды - читай колонки, ищи аномалии (ненулевой ERR, длинный хвост в гистограмме, болтливый процесс) и по ним решай, куда копать дальше.