← На главную

Замеряем оверхед eBPF-хуков при открытии файла: perf record+flamegraph

28.07.2026 15:55 · hackernews

Если вы запускаете eBPF-нагрузки или пишете eBPF-код, важно уметь измерять его влияние на производительность. Автор показывает, как это сделать на примере одного из самых критичных действий ОС — открытия файла. Цель — замерить оверхед, который добавляет eBPF-хук на операции с файлами.

Для замеров написан простой тест на C. Он много раз открывает один и тот же файл /etc/hostname в условиях тёплого кэша, чтобы минимизировать флуктуации от файловой системы и дискового ввода-вывода. Код вызывает syscall(SYS_openat, AT_FDCWD, path, O_RDONLY) напрямую, минуя обёртку libc. Перед основным циклом выделяется память через mmap с MAP_POPULATE, вызывается mlock, и весь массив обнуляется – так исключаются page faults во время измерений. Функция now_ns() использует CLOCK_MONOTONIC через VDSO, не делая системного вызова. Первые 10% итераций отбрасываются как прогрев, а для каждой итерации засекается длительность открытия файла, которая записывается в массив и выводится после цикла. Такой подход даёт распределение, из которого можно извлечь p50 и p99 до и после подключения eBPF-хука.

Для профилировки нужно, чтобы perf видел символы eBPF-программ. Включаем JIT и экспорт символов командами:

sudo sysctl -w net.core.bpf_jit_enable=1
sudo sysctl -w net.core.bpf_jit_kallsyms=1

Затем проверяем, что символы появились, через bpftool prog show и поиск в /proc/kallsyms с помощью rg; автор использует LSM-хуки, поэтому ищет строки с lsm и соответствующие bpf_prog_....

Сам процесс измерения двухэтапный. Сначала прогоняем бенчмарк без eBPF, направляя вывод в файл для последующих расчётов p50/p99. Запуск привязывается к конкретному ядру и получает высочайший приоритет реального времени: taskset -c 3 chrt -f 99 ./bench /etc/hostname 100000 > /tmp/samples.txt. Потом то же самое — с уже работающим eBPF-кодом — прогоняем под perf record. Ключи -g и --call-graph fp включают запись стеков вызовов с размоткой через frame pointer, -e cycles:k сэмплирует только ядерные циклы, «на лету» захватывая syscall, VFS, LSM и сам eBPF, но не код бенчмарка в userspace. Частота сэмплирования выставлена в -F 997, чтобы избежать периодического совпадения.

Полученный perf.data обрабатывается командой perf report --stdio --sort comm,dso,symbol, а flamegraph можно построить, например, в Inferno. Пример из статьи показывает, что подавляющее время уходит на bpf_lsm_file_open внутри do_dentry_open (89,78%), а дальше по стеку следуют tail-вызовы и конкретные программы BPF: bpf_prog_..._tail_call_security_check, enforce_access_policy и path_check_callback, где значительная доля приходится на bpf_probe_read_kernel и copy_from_kernel_nofault.

Автор подчёркивает: этот пост — о методе профилирования, а не о конкретных цифрах, потому что реальный оверхед сильно зависит от того, что делает ваш хук. Такой подход позволяет чётко измерить влияние eBPF и точно указать, где требуется оптимизация — от простого кэширования до пересмотра алгоритма в горячем пути.

Читать оригинал →