Первый kprobe на eBPF: следим за открытием файлов
Задача простая: увидеть, какие процессы какие файлы открывают, не перезапуская их и не трогая strace. eBPF позволяет повесить обработчик на функцию ядра и получать события почти без накладных расходов.
Одной строкой
В современных ядрах все варианты open сходятся в do_sys_openat2. Её второй аргумент — указатель на имя файла в пространстве пользователя:
sudo bpftrace -e 'kprobe:do_sys_openat2 {
printf("%-16s %s\n", comm, str(arg1));
}'
arg0 здесь — дескриптор каталога, arg1 — путь, arg2 — указатель на struct open_how. Функция появилась в ядре 5.6; на более старых ядрах нужно цепляться к do_sys_open, где путь тоже лежит во втором аргументе.
Почему kprobe хрупкий
kprobe цепляется к внутренней функции ядра, а у неё нет стабильного интерфейса: её могут переименовать, заинлайнить или поменять порядок аргументов. Для долгоживущих инструментов надёжнее tracepoint:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat {
printf("%-16s %s\n", comm, str(args.filename));
}'
Tracepoint описан форматом в /sys/kernel/tracing/events/syscalls/sys_enter_openat/format, и поля там именованные — гадать, в каком регистре что лежит, не нужно.
Дальше
bpftrace хорош для разведки, но для постоянного сервиса я переписал то же самое на libbpf с CO-RE: один бинарник работает на разных ядрах без пересборки. Об этом — в следующей заметке, когда доберусь.