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.