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.

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:

TipoEjemploQué instrumenta
tracepointtracepoint:syscalls:sys_enter_openatPuntos estáticos del kernel. Estables entre versiones: úsalos primero.
kprobe / kretprobekprobe:vfs_readEntrada y retorno de cualquier función del kernel. Pueden cambiar entre versiones.
uprobe / uretprobeuretprobe:/bin/bash:readlineEntrada y retorno de funciones de un binario o librería.
profileprofile:hz:99Muestreo en todas las CPU a una frecuencia fija.
intervalinterval:s:5Temporizador en una sola CPU, útil para imprimir resultados.
BEGIN / ENDENDAl 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); }'

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.