Kprobes — dynamiczne przechwytywanie funkcji jądra Linux
Ftrace pozwala śledzić wywołania funkcji jądra. Kprobes idą dalej — pozwalają wstrzyknąć własny kod w dowolne miejsce w kernelu, bez rekompilacji i bez restartu.
Czym są kprobes?
Kprobes (Kernel Probes) to mechanizm jądra Linux, który pozwala dynamicznie podczepić handler pod praktycznie dowolną instrukcję w kernelu. Kiedy procesor trafia na zastawiony probe, wykonanie jest chwilowo przekierowane do Twojego kodu, po czym wraca do normalnego toku.
Trzy warianty:
- kprobe — odpala się przed wykonaniem wskazanej instrukcji (zazwyczaj na wejściu do funkcji)
- kretprobe — odpala się przy wyjściu z funkcji (return), daje dostęp do wartości zwracanej
- jprobe — przestarzały (usunięty od kernela 4.15), zastąpiony przez kprobe z
pre_handler
Wymagania
Kernel musi mieć:
CONFIG_KPROBES=y
CONFIG_KPROBE_EVENTS=y
Większość dystrybucji ma to włączone domyślnie. Sprawdzisz tak:
grep CONFIG_KPROBES /boot/config-$(uname -r)
Potrzebujesz też debugfs zamontowanego:
mount | grep debugfs
# Jeśli nie zamontowany:
sudo mount -t debugfs none /sys/kernel/debug
Kprobes przez debugfs
Najszybszy sposób na zastawienie probe’a — bez pisania modułu, bez kompilacji.
Przechwycenie wejścia do funkcji
Chcemy wiedzieć, kiedy kernel otwiera pliki — podczepiamy się pod do_sys_openat2:
# Zastawienie probe
echo 'p:myprobe do_sys_openat2 filename=+0(%si):string' > /sys/kernel/debug/tracing/kprobe_events
# Włączenie
echo 1 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable
# Podgląd
cat /sys/kernel/debug/tracing/trace_pipe
Teraz każde otwarcie pliku w systemie pojawi się w trace_pipe z nazwą pliku. +0(%si) to drugi argument funkcji (register si na x86_64), rzutowany na string.
Wyłączenie:
echo 0 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable
echo '-:myprobe' >> /sys/kernel/debug/tracing/kprobe_events
Przechwycenie returna (kretprobe)
Chcemy wiedzieć, ile trwa vfs_read i co zwraca:
echo 'r:myretprobe vfs_read ret=$retval' > /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/myretprobe/enable
cat /sys/kernel/debug/tracing/trace_pipe
$retval to wartość zwracana przez funkcję — w przypadku vfs_read to ilość przeczytanych bajtów (lub błąd).
Kprobes przez perf
perf potrafi zastawiać kprobes bez ręcznego grzebania w debugfs:
# Dodanie probe'a
sudo perf probe --add 'do_sys_openat2 filename=+0(%si):string'
# Nagrywanie zdarzeń
sudo perf record -e probe:do_sys_openat2 -a -- sleep 5
# Podgląd
sudo perf script
Usunięcie:
sudo perf probe --del do_sys_openat2
Probe na dowolnej linii w funkcji
perf probe pozwala podczepić się nie tylko na wejściu, ale też w środku funkcji:
# Lista dostępnych punktów w tcp_sendmsg
sudo perf probe -L tcp_sendmsg
# Probe na konkretnej linii
sudo perf probe 'tcp_sendmsg:15 size'
Wymaga symboli debugowania (kernel-debuginfo / linux-image-*-dbg).
Kprobes przez bpftrace
Jeśli masz bpftrace, to najwygodniejszy sposób:
# Kto otwiera jakie pliki?
sudo bpftrace -e 'kprobe:do_sys_openat2 { printf("%s: %s\n", comm, str(arg1)); }'
# Ile trwa vfs_read?
sudo bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
@us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
# Top 10 najczęściej wywoływanych funkcji kernela
sudo bpftrace -e 'kprobe:* { @[probe] = count(); } interval:s:5 { print(@, 10); clear(@); }'
Ten ostatni przykład potrafi mocno obciążyć system — uważaj na produkcji.
Praktyczne zastosowania
Kto zabija procesy?
sudo bpftrace -e 'kprobe:do_send_sig_info {
printf("%s (pid=%d) -> signal %d to pid %d\n",
comm, pid, arg0, ((struct task_struct *)arg2)->pid);
}'
Śledzenie alokacji pamięci >1MB
sudo bpftrace -e 'kprobe:__alloc_pages /arg1 > 8/ {
printf("%s: alloc order %d (%d KB)\n", comm, arg1, (1 << arg1) * 4);
}'
Opóźnienia DNS (udp_sendmsg na port 53)
sudo bpftrace -e 'kprobe:udp_sendmsg {
$sk = (struct sock *)arg0;
$dport = ($sk->__sk_common.skc_dport >> 8) | (($sk->__sk_common.skc_dport & 0xff) << 8);
if ($dport == 53) {
printf("%s -> DNS query\n", comm);
}
}'
Ograniczenia
- Nie można podczepić się pod funkcje inline — kompilator je wstawia w miejsce wywołania, nie mają adresu
- Nie można podczepić się pod kprobes — rekurencja
- Blacklista — niektóre krytyczne funkcje są zablokowane (
/sys/kernel/debug/kprobes/blacklist) - Narzut — każdy probe to przerwanie, przy tysiącach probe’ów na gorących ścieżkach odczujesz spowolnienie
- Niestabilny ABI — nazwy funkcji kernela zmieniają się między wersjami, probe’y nie są przenośne
Kprobes vs ftrace vs eBPF
| Kprobes | Ftrace | eBPF | |
|---|---|---|---|
| Cel | dowolna instrukcja | funkcje | funkcje + zdarzenia |
| Narzut | średni | niski | niski |
| Elastyczność | wysoka | średnia | bardzo wysoka |
| Łatwość użycia | średnia | łatwa | wymaga narzędzi |
| Bezpieczeństwo | moduł jądra | bezpieczny | weryfikator BPF |
Kprobes to fundament — ftrace i eBPF pod spodem często z nich korzystają. Jeśli chcesz szybko coś sprawdzić na produkcji, bpftrace z kprobes to najlepsza kombinacja.