bpftrace: cómo diagnosticar Linux cuando top, strace y perf ya no son suficientes

Cuando un servidor Linux empieza a comportarse de forma extraña, las primeras herramientas suelen ser conocidas: top para localizar procesos que consumen CPU, vmstat para observar memoria y planificación, iostat para estudiar almacenamiento y strace para seguir llamadas al sistema. El problema aparece cuando todas ellas indican que algo va mal, pero ninguna explica por qué. bpftrace permite bajar un nivel más y observar en tiempo real lo que ocurre dentro del kernel y en las aplicaciones sin recompilar programas ni reiniciar el servidor.

Las claves de bpftrace en 30 segundos

  • bpftrace utiliza eBPF para ejecutar programas de observabilidad dentro del kernel Linux de forma controlada.
  • Puede rastrear llamadas al sistema, funciones del kernel, procesos, red, memoria, almacenamiento y funciones de aplicaciones.
  • Su sintaxis recuerda a awk y muchas investigaciones pueden resolverse con una sola línea.
  • Tracepoints, kprobes, uprobes, mapas e histogramas permiten pasar de observar síntomas a localizar causas.
  • En producción conviene filtrar eventos, evitar printf() masivos y preferir interfaces estables como los tracepoints.

eBPF se ha convertido durante los últimos años en una de las piezas más interesantes del ecosistema Linux. Herramientas como Cilium, Falco, BCC o Pixie se apoyan en esta tecnología para observar o controlar sistemas sin introducir modificaciones permanentes en el kernel.

bpftrace acerca esas capacidades al administrador de sistemas. En lugar de escribir programas eBPF en C, compilarlos y gestionar manualmente su carga, permite expresar buena parte de las investigaciones con pequeños scripts.

Un ejemplo mínimo deja clara la filosofía:

sudo bpftrace -e 'BEGIN { printf("Hello from bpftrace!\n"); }'Lenguaje del código: JavaScript (javascript)

No parece gran cosa, pero detrás de esa línea bpftrace genera el programa correspondiente, lo carga en el kernel y prepara su ejecución.

Qué aporta eBPF y por qué cambia el troubleshooting de Linux

BPF nació originalmente para filtrar paquetes de red. Su evolución hacia eBPF, extended Berkeley Packet Filter, amplió radicalmente sus posibilidades hasta convertirlo en un motor de ejecución orientado a eventos dentro del kernel.

Un programa eBPF puede activarse, entre otros muchos casos, cuando:

  • se ejecuta una llamada al sistema;
  • entra o sale una función del kernel;
  • se abre un archivo;
  • aparece una conexión de red;
  • se crea o termina un proceso;
  • se produce una asignación de memoria;
  • se dispara un contador hardware.

Esto permite responder preguntas que un sistema tradicional de monitorización difícilmente puede resolver.

Por ejemplo:

¿Qué proceso está borrando un archivo?

¿Qué aplicación está abriendo miles de ficheros?

¿Qué función del kernel está consumiendo CPU?

¿Cuánto tarda realmente una llamada read()?

¿Qué procesos generan más conexiones?

¿Qué ruta de ejecución está provocando una latencia inesperada?

eBPF incorpora además un verificador que analiza los programas antes de permitir su ejecución. Una vez aceptados, pueden compilarse mediante JIT a código nativo y ejecutarse únicamente cuando se produce el evento al que están asociados.

Esto no significa que cualquier script pueda utilizarse alegremente en producción. Un trazado demasiado amplio o un evento que se ejecuta millones de veces por segundo también puede generar carga. La diferencia es que existe una infraestructura específicamente diseñada para realizar este tipo de observación de manera mucho más eficiente que las técnicas tradicionales.

La estructura de bpftrace es sorprendentemente sencilla

Los programas siguen normalmente esta forma:

probe /predicate/
{
    action
}

El probe indica qué evento observar.

El predicate permite filtrar cuándo interesa actuar.

La action especifica qué hacer cuando se produce.

Un ejemplo:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat
/pid == 1234/
{
    printf("Process %s opened a file\n", comm);
}'Lenguaje del código: PHP (php)

Aquí se observa openat(), pero únicamente cuando la llamada procede del proceso con PID 1234.

Esta capacidad de filtrar antes de recopilar información resulta especialmente importante en servidores con mucha actividad.

Tracepoints frente a kprobes

Una de las primeras decisiones al trabajar con bpftrace consiste en elegir dónde enganchar la observación.

Los tracepoints son eventos definidos deliberadamente dentro del kernel. Su principal ventaja es la estabilidad.

Por ejemplo:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat
{
    printf("%s %s\n", comm, str(args.filename));
}'Lenguaje del código: PHP (php)

Permite observar los archivos que están intentando abrir los procesos.

Los kprobes, por el contrario, permiten engancharse directamente a funciones internas del kernel:

sudo bpftrace -e '
kprobe:do_sys_openat2
{
    printf("%s PID=%d\n", comm, pid);
}'Lenguaje del código: PHP (php)

Ofrecen una profundidad extraordinaria, pero tienen una desventaja: los nombres y argumentos de las funciones internas pueden cambiar entre versiones del kernel.

Por eso, cuando existe un tracepoint equivalente, normalmente resulta preferible utilizarlo.

La disponibilidad puede consultarse directamente:

sudo bpftrace -l 'tracepoint:*'Lenguaje del código: JavaScript (javascript)

o:

sudo bpftrace -l 'kprobe:*open*'Lenguaje del código: JavaScript (javascript)

También puede observar aplicaciones sin modificar su código

bpftrace no termina en el kernel.

Los uprobes permiten engancharse a funciones de aplicaciones o bibliotecas de espacio de usuario.

Por ejemplo, para observar llamadas a malloc():

sudo bpftrace -e '
uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc
{
    printf("%s PID=%d called malloc()\n", comm, pid);
}'Lenguaje del código: PHP (php)

El equivalente de retorno, uretprobe, permite estudiar qué devuelve la función.

Combinando ambos pueden medirse latencias:

sudo bpftrace -e '
uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc
{
    @start[tid] = nsecs;
}

uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc
{
    @latency = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}'Lenguaje del código: PHP (php)

Esta capacidad resulta útil para estudiar aplicaciones de producción cuando recompilar con instrumentación específica no es una opción.

También existen los USDT (User Statically Defined Tracing), puntos de observación introducidos deliberadamente por los desarrolladores dentro de aplicaciones y runtimes.

Los mapas convierten millones de eventos en información útil

Imprimir cada evento suele ser una mala idea.

Si un servidor ejecuta miles de llamadas por segundo, la consola se convierte rápidamente en ruido y el propio printf() puede añadir una carga innecesaria.

Aquí entran los mapas y las agregaciones.

Para contar qué programas ejecutan más openat():

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat
{
    @[comm] = count();
}'Lenguaje del código: PHP (php)

El resultado puede mostrar algo similar:

@["bash"]: 42
@["vim"]: 18
@["nginx"]: 156Lenguaje del código: JavaScript (javascript)

También pueden utilizarse:

FunciónUso
count()Cuenta eventos
sum()Suma valores
avg()Calcula medias
min()Obtiene el mínimo
max()Obtiene el máximo
hist()Genera histogramas logarítmicos
lhist()Genera histogramas lineales
stats()Combina recuento, total y media

Los histogramas son especialmente interesantes para analizar latencias porque evitan reducir un problema complejo a un simple promedio.

Doce investigaciones útiles para un administrador Linux

Una de las mejores formas de aprender bpftrace es utilizarlo sobre problemas concretos.

Para observar aperturas de archivos:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat
{
    printf("%-16s %s\n", comm, str(args.filename));
}'Lenguaje del código: PHP (php)

Para saber qué procesos generan más llamadas al sistema:

sudo bpftrace -e '
tracepoint:raw_syscalls:sys_enter
{
    @[comm] = count();
}'Lenguaje del código: PHP (php)

Para observar procesos nuevos:

sudo bpftrace -e '
tracepoint:sched:sched_process_fork
{
    printf("%s -> %s\n",
           str(args.parent_comm),
           str(args.child_comm));
}'Lenguaje del código: PHP (php)

Para detectar quién elimina archivos:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_unlinkat
{
    printf("%s PID=%d\n", comm, pid);
}'Lenguaje del código: PHP (php)

Para medir cuánto tarda read():

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_read
{
    @start[tid] = nsecs;
}

tracepoint:syscalls:sys_exit_read
/@start[tid]/
{
    printf("%s took %llu ns\n",
           comm,
           nsecs - @start[tid]);
    delete(@start[tid]);
}'Lenguaje del código: PHP (php)

Para comprobar qué aplicaciones abren más conexiones:

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_connect
{
    @[comm] = count();
}'Lenguaje del código: PHP (php)

Para obtener una aproximación de los principales consumidores de CPU:

sudo bpftrace -e '
profile:hz:99
{
    @[comm] = count();
}'Lenguaje del código: PHP (php)

Y para descubrir las rutas del kernel que aparecen con mayor frecuencia:

sudo bpftrace -e '
profile:hz:99
{
    @[kstack] = count();
}'Lenguaje del código: PHP (php)

Este último enfoque puede resultar especialmente útil como punto de partida para generar perfiles y estudiar dónde se está consumiendo realmente el tiempo de CPU.

De los one-liners a scripts reutilizables

Cuando una investigación empieza a crecer, escribirlo todo dentro de -e deja de tener sentido.

Los programas pueden almacenarse como archivos .bt.

Por ejemplo:

tracepoint:syscalls:sys_enter_openat
{
    @[comm] = count();
}

interval:s:5
{
    printf("=== File Opens ===\n");
    print(@);
    clear(@);
}Lenguaje del código: PHP (php)

Después:

sudo bpftrace opens.btLenguaje del código: CSS (css)

Esto permite mantener una pequeña biblioteca interna de herramientas de diagnóstico, versionarla en Git y compartirla entre administradores.

Una organización puede terminar disponiendo de scripts específicos para PostgreSQL, Nginx, Kubernetes, almacenamiento, red o determinadas aplicaciones internas.

bpftrace no sustituye a top, strace o perf

Sería un error interpretar bpftrace como una herramienta que hace innecesarias las anteriores.

Cada una responde mejor a preguntas diferentes.

top sigue siendo excelente para localizar rápidamente procesos con consumo elevado.

vmstat ofrece una fotografía inmediata de CPU, memoria, procesos y espera.

iostat resulta muy útil para almacenamiento.

strace continúa siendo extraordinariamente práctico para estudiar las llamadas al sistema de un proceso determinado.

perf proporciona capacidades avanzadas de profiling.

bpftrace entra especialmente en juego cuando la investigación requiere construir una pregunta específica sobre el comportamiento del sistema que las métricas tradicionales no responden directamente.

Ahí está su verdadero valor.

Cómo utilizarlo sin convertir el diagnóstico en otro problema

La potencia de bpftrace exige cierta disciplina.

En producción conviene seguir unas reglas sencillas:

  • Preferir tracepoints cuando exista una alternativa estable.
  • Aplicar predicates cuanto antes para reducir los eventos procesados.
  • Utilizar mapas y agregaciones en lugar de imprimir cada evento.
  • Conocer la frecuencia potencial del probe antes de activarlo.
  • Borrar entradas temporales de los mapas cuando dejen de utilizarse.
  • Aprovechar BTF cuando esté disponible para trabajar con tipos del kernel.
  • Validar scripts nuevos en desarrollo o staging.
  • Limitar cada investigación únicamente a los probes necesarios.

También conviene recordar que la instrumentación dinámica no es completamente gratuita. Un script mal diseñado que se enganche a un evento extremadamente frecuente puede afectar al sistema que se intenta diagnosticar.

Ese detalle resulta todavía más importante en servidores críticos.

bpftrace representa, en cierta manera, un cambio de mentalidad para el administrador Linux. En lugar de limitarse a consultar métricas predefinidas, puede formular sus propias preguntas al sistema mientras éste continúa funcionando.

Cuando top muestra el síntoma, iostat confirma que algo ocurre y strace todavía no explica suficientemente el origen, eBPF y bpftrace permiten abrir una ventana mucho más profunda hacia lo que está haciendo realmente Linux.

Preguntas frecuentes

¿Qué diferencia existe entre eBPF y bpftrace?

eBPF es la tecnología del kernel Linux que permite ejecutar programas asociados a eventos. bpftrace proporciona un lenguaje de alto nivel que simplifica la creación de programas de tracing sobre eBPF.

¿Es seguro utilizar bpftrace en producción?

Puede utilizarse en producción, pero los scripts deben diseñarse cuidadosamente. Es recomendable filtrar eventos, evitar salidas masivas y validar primero las trazas más complejas en un entorno de pruebas.

¿Es mejor utilizar tracepoints o kprobes?

Cuando existe un tracepoint adecuado suele ser preferible porque ofrece una interfaz más estable. Los kprobes permiten llegar a funciones internas del kernel, pero pueden variar entre versiones.

¿Puede bpftrace analizar aplicaciones además del kernel?

Sí. Los uprobes y uretprobes permiten observar funciones de aplicaciones y bibliotecas de espacio de usuario, mientras que USDT permite utilizar puntos de instrumentación definidos específicamente por algunas aplicaciones.

Suscríbete al boletín SysAdmin

Este es tu recurso para las últimas noticias y consejos sobre administración de sistemas, Linux, Windows, cloud computing, seguridad de la nube, etc. Lo enviamos 2 días a la semana.

¡Apúntate a nuestro newsletter!


– patrocinadores –

Noticias destacadas

– patrocinadores –

¡SUSCRÍBETE AL BOLETÍN
DE LOS SYSADMINS!

Scroll al inicio
×