bpftrace es un lenguaje de trazado de alto nivel para Linux que compila tus programas a eBPF y los carga en el kernel, de modo que puedes observar llamadas al sistema, funciones del kernel y funciones de cualquier programa en ejecución sin reiniciar nada ni modificar código. Su sintaxis recuerda a awk: una sonda, un filtro opcional y una acción. En este tutorial instalarás bpftrace en Ubuntu 24.04, aprenderás a encontrar sondas, ejecutarás one-liners útiles para diagnóstico y escribirás un script reutilizable.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. bpftrace necesita privilegios de root para cargar programas en el kernel. - Familiaridad básica con conceptos como llamada al sistema, PID y proceso.
Notabpftrace se ejecuta en el kernel de producción. Las sondas de alta frecuencia (por ejemplo, en cada llamada al sistema de todo el servidor) añaden sobrecarga. Filtra siempre por proceso o evento cuando trabajes en un servidor con carga.
Paso 1: Instalar bpftrace
Instala el paquete desde los repositorios de Ubuntu:
sudo apt update
sudo apt install bpftrace
Comprueba la versión:
bpftrace --version
bpftrace v0.20.2
Los kernels de Ubuntu incluyen información de tipos BTF, que permite a bpftrace conocer las estructuras del kernel sin instalar las cabeceras. Comprueba que está disponible:
ls -l /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 5846213 Sep 25 09:01 /sys/kernel/btf/vmlinux
El paquete trae también decenas de herramientas listas para usar, escritas en bpftrace, como execsnoop.bt, opensnoop.bt, biolatency.bt o tcpconnect.bt. Localízalas:
dpkg -L bpftrace | grep '\.bt$' | head -n 5
Para probar que todo funciona, ejecuta un programa que imprime un mensaje y termina:
sudo bpftrace -e 'BEGIN { printf("bpftrace funciona\n"); exit(); }'
Attaching 1 probe...
bpftrace funciona
Paso 2: Entender la sintaxis y los tipos de sonda
Un programa de bpftrace es una lista de bloques con esta forma:
sonda /filtro/ { acciones }
Cuando ocurre el evento de la sonda y se cumple el filtro, se ejecutan las acciones. Los tipos de sonda más usados son:
| Tipo | Ejemplo | Qué instrumenta |
|---|---|---|
tracepoint | tracepoint:syscalls:sys_enter_openat | Puntos estáticos del kernel. Estables entre versiones: úsalos primero. |
kprobe / kretprobe | kprobe:vfs_read | Entrada y retorno de cualquier función del kernel. Pueden cambiar entre versiones. |
uprobe / uretprobe | uretprobe:/bin/bash:readline | Entrada y retorno de funciones de un binario o librería. |
profile | profile:hz:99 | Muestreo en todas las CPU a una frecuencia fija. |
interval | interval:s:5 | Temporizador en una sola CPU, útil para imprimir resultados. |
BEGIN / END | END | Al empezar y al terminar el programa (Ctrl+C). |
Dentro de las acciones tienes variables integradas como pid, tid, comm (nombre del proceso), uid, nsecs (marca de tiempo en nanosegundos), retval (valor de retorno) y args (argumentos de un tracepoint). Las variables que empiezan por @ son mapas: se agregan en el kernel y se imprimen al terminar.
Para listar las sondas disponibles, usa -l con un patrón:
sudo bpftrace -l 'tracepoint:syscalls:sys_enter_open*'
tracepoint:syscalls:sys_enter_open
tracepoint:syscalls:sys_enter_open_by_handle_at
tracepoint:syscalls:sys_enter_open_tree
tracepoint:syscalls:sys_enter_openat
tracepoint:syscalls:sys_enter_openat2
Añade -v para ver los argumentos que ofrece un tracepoint:
sudo bpftrace -lv 'tracepoint:syscalls:sys_enter_openat'
tracepoint:syscalls:sys_enter_openat
int __syscall_nr
int dfd
const char * filename
int flags
umode_t mode
Paso 3: Ejecutar one-liners de diagnóstico
Los siguientes programas responden preguntas habituales en un servidor. Todos se detienen con Ctrl+C, momento en el que bpftrace imprime los mapas.
Qué ficheros abre cada proceso:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%-16s %s\n", comm, str(args->filename)); }'
Attaching 1 probe...
systemd-journal /proc/1022/cmdline
cron /etc/crontab
sshd /etc/ssh/sshd_config
Qué comandos se ejecutan en el sistema, útil para descubrir scripts o cron jobs que lanzan procesos de corta duración que top no llega a ver:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%-6d %-16s %s\n", pid, comm, str(args->filename)); }'
Qué procesos hacen más llamadas al sistema:
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
^C
@[sshd]: 214
@[bpftrace]: 1320
@[mysqld]: 58721
Cuántos bytes lee cada proceso con read(), en un histograma por tamaño:
sudo bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @bytes[comm] = hist(args->ret); }'
Qué procesos abren conexiones TCP salientes:
sudo bpftrace -e 'kprobe:tcp_connect { printf("%-6d %s\n", pid, comm); }'
Quién accede a un fichero concreto, por ejemplo /etc/shadow:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat /str(args->filename) == "/etc/shadow"/ { printf("%s (PID %d, UID %d)\n", comm, pid, uid); }'
Notadesde bpftrace 0.21, la forma recomendada de acceder a los argumentos es
args.filename. La formaargs->filenamede esta guía funciona en la versión de Ubuntu 24.04 y sigue aceptándose en las posteriores.
Paso 4: Medir latencias con histogramas
Contar eventos ayuda, pero muchos problemas de rendimiento son de latencia. El patrón consiste en guardar una marca de tiempo al entrar, calcular la diferencia al salir y acumularla en un histograma.
Latencia de E/S de disco por petición, usando los tracepoints de la capa de bloques. La petición se identifica por el dispositivo y el sector:
sudo bpftrace -e '
tracepoint:block:block_rq_issue { @inicio[args->dev, args->sector] = nsecs; }
tracepoint:block:block_rq_complete /@inicio[args->dev, args->sector]/ {
@latencia_us = hist((nsecs - @inicio[args->dev, args->sector]) / 1000);
delete(@inicio[args->dev, args->sector]);
}
END { clear(@inicio); }'
Genera algo de E/S en otra terminal (por ejemplo, sudo find / -xdev -type f > /dev/null) y pulsa Ctrl+C:
@latencia_us:
[64, 128) 112 |@@@@@@@@@ |
[128, 256) 603 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[256, 512) 341 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[512, 1K) 58 |@@@@@ |
[1K, 2K) 9 | |
La mayoría de peticiones tarda entre 128 y 512 microsegundos. Si aparece una cola larga en los milisegundos, el disco o el almacenamiento subyacente es el cuello de botella. El script incluido biolatency.bt hace lo mismo con más detalle.
Latencia de vfs_read en el kernel, con un kprobe y su kretprobe, usando el identificador de hilo como clave:
sudo bpftrace -e '
kprobe:vfs_read { @inicio[tid] = nsecs; }
kretprobe:vfs_read /@inicio[tid]/ {
@ns[comm] = hist(nsecs - @inicio[tid]);
delete(@inicio[tid]);
}
END { clear(@inicio); }'
El bloque END con clear(@inicio) evita que bpftrace imprima al final el mapa auxiliar de marcas de tiempo.
Paso 5: Trazar programas en espacio de usuario
Las uprobes instrumentan funciones de un binario o de una librería compartida. El ejemplo clásico lee cada línea que se escribe en cualquier bash del sistema al retornar la función readline:
sudo bpftrace -e 'uretprobe:/bin/bash:readline { printf("PID %d: %s\n", pid, str(retval)); }'
Escribe comandos en otra sesión y los verás aparecer:
Attaching 1 probe...
PID 1901: ls -la
PID 1901: systemctl status nginx
Para saber qué funciones puedes instrumentar en un binario, lista sus sondas con un patrón:
sudo bpftrace -l 'uprobe:/bin/bash:read*'
Los binarios sin símbolos (stripped) solo exponen las funciones exportadas. Para trazar funciones internas necesitas los símbolos de depuración del paquete.
Paso 6: Perfilar el uso de CPU
La sonda profile toma una muestra de la pila en cada CPU a la frecuencia indicada. Contar pilas durante unos segundos muestra dónde se gasta el tiempo de CPU. Este programa muestrea a 99 Hz durante 10 segundos las pilas de usuario de un proceso concreto; sustituye your_pid por su PID:
sudo bpftrace -e 'profile:hz:99 /pid == your_pid/ { @[ustack] = count(); } interval:s:10 { exit(); }'
La salida muestra cada pila con el número de muestras; las que más se repiten son las funciones que consumen CPU. Se usa 99 Hz en lugar de 100 para no sincronizarse con temporizadores periódicos del sistema.
Para leer muchas pilas es más cómodo un gráfico de llama. Descarga las herramientas de Brendan Gregg y genera el SVG a partir de pilas de todo el sistema:
git clone https://github.com/brendangregg/FlameGraph ~/FlameGraph
sudo bpftrace -e 'profile:hz:99 { @[kstack, ustack, comm] = count(); } interval:s:30 { exit(); }' > pilas.txt
~/FlameGraph/stackcollapse-bpftrace.pl pilas.txt | ~/FlameGraph/flamegraph.pl > cpu.svg
Abre cpu.svg en un navegador: el ancho de cada bloque es proporcional al tiempo de CPU de esa función.
Paso 7: Escribir un script reutilizable
Cuando un programa crece, guárdalo en un fichero .bt. Este script mide la latencia de las llamadas al sistema de los procesos con un nombre concreto, que recibe como parámetro. Crea el directorio y el fichero:
sudo mkdir -p /usr/local/share/bpftrace
sudo nano /usr/local/share/bpftrace/syscall-latencia.bt
#!/usr/bin/env bpftrace
/*
* Latencia de llamadas al sistema de un proceso, por syscall.
* Uso: sudo bpftrace /usr/local/share/bpftrace/syscall-latencia.bt <nombre>
*/
BEGIN
{
printf("Trazando syscalls de '%s'. Ctrl+C para terminar.\n", str($1));
}
tracepoint:raw_syscalls:sys_enter /comm == str($1)/
{
@inicio[tid] = nsecs;
}
tracepoint:raw_syscalls:sys_exit /@inicio[tid]/
{
@latencia_us[args->id] = hist((nsecs - @inicio[tid]) / 1000);
delete(@inicio[tid]);
}
END
{
clear(@inicio);
}
$1 es el primer parámetro posicional y str($1) lo trata como texto. La clave del histograma es el número de la llamada al sistema. Ejecútalo contra un proceso que exista, por ejemplo sshd:
sudo bpftrace /usr/local/share/bpftrace/syscall-latencia.bt sshd
Attaching 4 probes...
Trazando syscalls de 'sshd'. Ctrl+C para terminar.
^C
@latencia_us[0]:
[0] 41 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[1] 17 |@@@@@@@@@@@@@@@@@@@@@ |
[2, 4) 3 |@@@ |
Para traducir los números a nombres, usa ausyscall del paquete auditd: en x86_64, ausyscall 0 devuelve read. Ten en cuenta que las llamadas que esperan actividad, como poll o epoll_wait, muestran latencias altas aunque el proceso no esté lento: simplemente está esperando datos.
Solución de problemas
ERROR: ... Operation not permitted. bpftrace necesita root. Ejecútalo con sudo. Si ya lo haces y el sistema tiene Secure Boot activado, el modo lockdown del kernel puede bloquear algunas funciones, como leer memoria arbitraria del kernel; consulta el modo con cat /sys/kernel/security/lockdown.
ERROR: Could not resolve symbol o la sonda no existe. Las funciones del kernel cambian entre versiones. Comprueba que existe con sudo bpftrace -l 'kprobe:nombre*' y, si hay un tracepoint equivalente, úsalo en su lugar porque es estable.
Una uprobe no muestra nada. Comprueba la ruta real del binario con readlink -f $(command -v programa) y que la función aparece en sudo bpftrace -l 'uprobe:/ruta/binario:*'. Si el binario está stripped, la función interna no será visible.
Aparece Lost N events. Estás imprimiendo con printf más eventos de los que caben en el búfer. Añade un filtro más estricto o sustituye printf por un mapa agregado (count(), hist()), que se resume en el kernel y apenas genera salida.
Conclusión
Has instalado bpftrace en Ubuntu 24.04, has aprendido a buscar sondas y sus argumentos, has medido latencias de disco y del kernel con histogramas, has trazado un programa de usuario, has perfilado CPU y has escrito un script con parámetros. Como siguientes pasos, prueba las herramientas incluidas en el paquete (execsnoop.bt, biolatency.bt, tcpretrans.bt), consulta la guía de referencia en man bpftrace y crea tu propia colección de scripts para los problemas que más se repiten en tus servidores.
