Aller au contenu

Commandes bpftrace

bpftrace est un langage de traçage de haut niveau pour eBPF sous Linux. Il fournit une syntaxe concise pour écrire des scripts de traçage utilisant des sondes du noyau et de l’espace utilisateur, rendant l’instrumentation dynamique accessible pour l’analyse de performance et le débogage.

Installation

Linux/Ubuntu

# Ubuntu 22.04+
sudo apt install bpftrace

# Fedora
sudo dnf install bpftrace

# Build from source
git clone https://github.com/bpftrace/bpftrace.git
cd bpftrace && mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc) && sudo make install

Vérifier l’installation

# Vérifier la version et les sondes supportées
bpftrace --version
bpftrace -l 'tracepoint:syscalls:*' | head -20

Types de sondes

Type de sondeSyntaxeDescription
kprobekprobe:func_nameEntrée de fonction du noyau
kretprobekretprobe:func_nameRetour de fonction du noyau
uprobeuprobe:/path:funcEntrée de fonction espace utilisateur
uretprobeuretprobe:/path:funcRetour de fonction espace utilisateur
tracepointtracepoint:category:nameTracepoint statique du noyau
usdtusdt:/path:probeSonde statique espace utilisateur
profileprofile:hz:99Échantillonnage temporel
intervalinterval:s:1Sortie périodique
softwaresoftware:faults:100Événements logiciels
hardwarehardware:cache-misses:1000000Événements PMC matériels
BEGINBEGINDémarrage du script
ENDENDFin du script

Variables intégrées

VariableDescription
pidID du processus
tidID du thread
uidID de l’utilisateur
gidID du groupe
commNom du processus
nsecsHorodatage en nanosecondes
elapsedNanosecondes depuis le démarrage de bpftrace
cpuID du CPU actuel
curtaskPointeur task_struct actuel
retvalValeur de retour (kretprobe/uretprobe)
arg0-argNArguments de la sonde
argsStructure d’arguments du tracepoint
cgroupID de cgroup du processus actuel
kstackTrace de pile du noyau
ustackTrace de pile espace utilisateur

One-liners essentiels

# Tracer les nouveaux processus avec arguments
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s %s\n", comm, str(args.filename)); }'

# Compter les appels système par nom de processus
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'

# Distribution des tailles de read()
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_read { @bytes = hist(args.count); }'

# Tracer les ouvertures de fichiers par processus
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); }'

# Mesurer la latence de lecture VFS
sudo bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @us = hist((nsecs - @start[tid]) / 1000); delete(@start[tid]); }'

# Compter les appels de fonctions du noyau
sudo bpftrace -e 'kprobe:tcp_sendmsg { @[comm] = count(); }'

# Profiler les piles du noyau à 99 Hz
sudo bpftrace -e 'profile:hz:99 { @[kstack] = count(); }'

# Traçage des connexions TCP
sudo bpftrace -e 'kprobe:tcp_v4_connect { printf("%s PID:%d -> connecting\n", comm, pid); }'

# Histogramme de latence d'E/S par blocs
sudo bpftrace -e 'tracepoint:block:block_rq_issue { @start[args.dev, args.sector] = nsecs; } tracepoint:block:block_rq_complete /@start[args.dev, args.sector]/ { @usecs = hist((nsecs - @start[args.dev, args.sector]) / 1000); delete(@start[args.dev, args.sector]); }'

Maps et agrégations

# Map de comptage
sudo bpftrace -e 'kprobe:vfs_read { @reads[comm] = count(); }'

# Map de somme
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @bytes[comm] = sum(args.ret); }'

# Moyenne
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @avg_bytes[comm] = avg(args.ret); }'

# Min/Max
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @max_read[comm] = max(args.ret); }'

# Histogramme linéaire (0-100 par pas de 10)
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @size = lhist(args.ret, 0, 10000, 1000); }'

# Histogramme log2
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @size = hist(args.ret); }'

# Statistiques (comptage, moyenne, total)
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args.ret > 0/ { @bytes[comm] = stats(args.ret); }'

Filtres et formatage

# Filtrer par PID
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_write /pid == 1234/ { @bytes = hist(args.count); }'

# Filtrer par nom de processus
sudo bpftrace -e 'kprobe:vfs_read /comm == "nginx"/ { @[kstack] = count(); }'

# Opérateur ternaire
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read { @result[args.ret >= 0 ? "success" : "error"] = count(); }'

# Formatage d'horodatage
sudo bpftrace -e 'kprobe:do_nanosleep { printf("%s PID:%d sleeping\n", strftime("%H:%M:%S", nsecs), pid); }'

# Comparaison de chaînes avec jokers
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat /str(args.filename) == "/etc/passwd"/ { printf("%s opened /etc/passwd\n", comm); }'

Scripts (multiligne)

# Sauvegarder sous latency.bt et exécuter avec : sudo bpftrace latency.bt
# Mesurer la latence de lecture par processus
sudo bpftrace -e '
kprobe:vfs_read {
    @start[tid] = nsecs;
}

kretprobe:vfs_read /@start[tid]/ {
    $duration_us = (nsecs - @start[tid]) / 1000;
    @latency_us[comm] = hist($duration_us);
    if ($duration_us > 10000) {
        printf("SLOW READ: %s pid=%d %d us\n", comm, pid, $duration_us);
    }
    delete(@start[tid]);
}

interval:s:5 {
    print(@latency_us);
    clear(@latency_us);
}

END {
    clear(@start);
}
'

Lister et explorer les sondes

# Lister tous les tracepoints
sudo bpftrace -l 'tracepoint:*'

# Lister les sondes correspondant à un motif
sudo bpftrace -l 'kprobe:tcp_*'

# Lister les arguments du tracepoint
sudo bpftrace -lv 'tracepoint:syscalls:sys_enter_openat'

# Lister les sondes USDT dans un binaire
sudo bpftrace -l 'usdt:/usr/sbin/mysqld:*'

# Compter les types de sondes disponibles
sudo bpftrace -l | awk -F: '{print $1}' | sort | uniq -c | sort -rn

Traçage de l’espace utilisateur

# Tracer les appels malloc dans un processus en cours d'exécution
sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc { @size = hist(arg0); }' -p 1234

# Tracer l'entrée/retour de fonction dans un binaire
sudo bpftrace -e 'uprobe:/usr/local/bin/myapp:process_request { @start[tid] = nsecs; } uretprobe:/usr/local/bin/myapp:process_request /@start[tid]/ { @latency = hist((nsecs - @start[tid]) / 1000); delete(@start[tid]); }'

# Tracer les appels de fonctions Python via USDT
sudo bpftrace -e 'usdt:/usr/bin/python3:function__entry { printf("%s:%s\n", str(arg0), str(arg1)); }'

Patterns utiles

# Processus principaux par nombre d'appels système (avec sortie par intervalles)
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); } interval:s:5 { print(@, 10); clear(@); }'

# Tracer les recherches DNS
sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo { printf("%s is resolving: %s\n", comm, str(arg0)); }'

# Suivi des défauts de page
sudo bpftrace -e 'software:page-faults:1 { @[comm, kstack] = count(); }'

# Traçage du planificateur CPU
sudo bpftrace -e 'tracepoint:sched:sched_switch { @[args.prev_comm] = count(); }'