--- title: "Análisis de rendimiento con strace y ltrace en Linux" description: "Usa strace y ltrace en Ubuntu 24.04 para contar llamadas al sistema y de biblioteca, medir cuánto tarda cada una y encontrar por qué un proceso va lento." category: "Optimización de Rendimiento" language: es source: https://cubepath.com/es/docs/optimización-de-rendimiento/analisis-de-rendimiento-con-strace-y-ltrace updated: 2026-09-25 --- # Análisis de rendimiento con strace y ltrace en Linux strace muestra las llamadas al sistema que hace un proceso (abrir ficheros, leer, escribir, conectar a la red) y ltrace muestra las llamadas a funciones de bibliotecas compartidas como `malloc` o `strlen`. Con ambas puedes averiguar por qué un programa tarda en arrancar, qué fichero está buscando sin encontrarlo o en qué operación se queda bloqueado, sin tener su código fuente. En este tutorial usarás strace y ltrace en Ubuntu 24.04 con ejemplos que puedes reproducir y aprenderás a aplicarlos a un proceso en producción. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Conocimientos básicos de la línea de comandos. > Advertencia: strace y ltrace detienen el proceso en cada llamada que rastrean, lo que puede ralentizarlo mucho (a veces más de 10 veces). Úsalos en producción durante periodos cortos y con filtros, como se explica en el paso 4. ## Paso 1: Instalar strace, ltrace y gcc strace y ltrace están en los repositorios de Ubuntu. Instala también `gcc`, que usarás para compilar un programa de ejemplo en el paso 5: ```bash sudo apt update sudo apt install strace ltrace gcc ``` Comprueba que strace funciona: ```bash strace -V ``` ```output strace -- version 6.8 ... ``` ## Paso 2: Obtener un resumen de llamadas al sistema La opción más útil para empezar es `-c`, que en lugar de imprimir cada llamada cuenta cuántas veces se ha hecho cada una, cuántas han fallado y cuánto tiempo han ocupado. Compara dos formas de copiar 1 MB con `dd`: en bloques de 1 byte y en un solo bloque de 1 MB. ```bash strace -c dd if=/dev/zero of=/tmp/test.bin bs=1 count=1000000 ``` ```output % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 50.84 1.402113 1 1000000 write 49.14 1.355290 1 1000003 read 0.01 0.000245 13 18 mmap ... ------ ----------- ----------- --------- --------- ---------------- 100.00 2.758012 1 2000079 5 total ``` Ahora con un bloque grande: ```bash strace -c dd if=/dev/zero of=/tmp/test.bin bs=1M count=1 ``` ```output % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 72.40 0.000921 921 1 write ... ------ ----------- ----------- --------- --------- ---------------- 100.00 0.001272 16 79 5 total ``` El mismo trabajo pasa de dos millones de llamadas al sistema a menos de cien. Este es el patrón más habitual que revela strace: una aplicación que lee o escribe en trozos diminutos, sin buffer, y pasa su tiempo cambiando entre modo usuario y modo kernel. Por defecto `-c` mide el tiempo de CPU del sistema dentro de cada llamada. Si lo que te interesa es cuánto tiempo real ha estado esperando el proceso (por ejemplo, a la red o al disco), añade `-w` para medir tiempo de reloj: ```bash strace -c -w curl -s -o /dev/null https://www.cubepath.com ``` Ordena el resumen por número de llamadas con `-S calls` o por tiempo con `-S time` (el valor por defecto). ## Paso 3: Ver cada llamada con su duración Cuando el resumen apunta a un tipo de llamada, pasa a la traza detallada. Estas opciones son las que más se usan para rendimiento: - `-f`: sigue también a los procesos hijos y a los hilos. - `-tt`: marca de tiempo con microsegundos al inicio de cada línea. - `-T`: tiempo pasado dentro de cada llamada, entre `< >` al final de la línea. - `-e trace=...`: limita la traza a ciertas llamadas o grupos (`%file`, `%network`, `%process`, `%memory`). - `-y`: muestra la ruta del fichero o el socket en lugar del número de descriptor. - `-o fichero`: escribe la traza en un fichero en lugar de en la terminal. Por ejemplo, para ver solo la actividad de red de `curl` con tiempos: ```bash strace -f -tt -T -y -e trace=%network curl -s -o /dev/null https://www.cubepath.com ``` ```output 10:42:17.311204 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 5 <0.000031> 10:42:17.311592 connect(5, {sa_family=AF_INET, sin_port=htons(443), ...}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000089> ... ``` ### Encontrar las llamadas más lentas Para un programa que tarda en arrancar, guarda la traza completa en un fichero: ```bash strace -f -tt -T -o /tmp/trace.txt python3 -c "import json" ``` Después ordena las líneas por la duración que aparece al final. Este comando extrae el valor entre `< >` y muestra las diez llamadas más lentas: ```bash awk -F'<' '/<[0-9.]+>$/ {print $NF "\t" $0}' /tmp/trace.txt | sort -rn | head -n 10 ``` Las llamadas que tardan milisegundos, y no microsegundos, son las candidatas: un `connect` a un servidor lento, un `read` sobre un disco de red, un `futex` donde un hilo espera a otro o un `poll` que espera respuesta. ### Encontrar ficheros que no existen Un arranque lento a menudo se debe a que el programa busca un fichero o módulo en decenas de rutas antes de encontrarlo. La opción `-Z` muestra solo las llamadas que fallan: ```bash strace -f -Z -e trace=%file python3 -c "import json" 2>&1 | head -n 20 ``` ```output openat(AT_FDCWD, "/usr/lib/python312.zip", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/usr/lib/python3.12/json/__pycache__/...", ...) = -1 ENOENT (No such file or directory) ... ``` Cientos de `ENOENT` seguidos indican rutas de búsqueda (`PYTHONPATH`, `LD_LIBRARY_PATH`, `include_path` de PHP) demasiado largas o mal ordenadas. ## Paso 4: Rastrear un proceso que ya está en marcha Para diagnosticar un servicio en producción no necesitas reiniciarlo: strace puede engancharse a un proceso existente con `-p`. En Ubuntu, la política de seguridad Yama solo permite rastrear procesos ajenos con `sudo`. Obtén el PID del proceso, por ejemplo de un worker de PHP-FPM o del proceso principal de tu aplicación: ```bash pgrep -a php-fpm ``` Recoge un resumen durante 30 segundos. `timeout` envía una señal a strace al terminar el tiempo, strace se desengancha limpiamente y el proceso sigue funcionando: ```bash sudo timeout 30 strace -c -f -p your_pid ``` Si el resumen muestra mucho tiempo en llamadas de red o de ficheros, mira el detalle solo de esas llamadas, guardándolo en un fichero: ```bash sudo timeout 10 strace -f -tt -T -y -e trace=%network,%file -p your_pid -o /tmp/app-trace.txt ``` Para un proceso que parece colgado, basta con engancharse sin filtros y mirar la última línea: si se queda en `read(7`, está esperando datos de una conexión; si es `futex(...`, espera un bloqueo de otro hilo. ```bash sudo strace -p your_pid ``` Pulsa `Ctrl+C` para desengancharte. El proceso no se detiene. ## Paso 5: Analizar llamadas a bibliotecas con ltrace ltrace funciona como strace pero intercepta las llamadas a funciones de bibliotecas compartidas. Tiene una limitación importante en Ubuntu: la versión incluida (0.7.3) no reconoce la tabla de enlaces que genera gcc cuando compila con protección de flujo de control (`-fcf-protection`) y enlace inmediato, que es la configuración por defecto de los paquetes de Ubuntu. Por eso, con muchos binarios del sistema ltrace no muestra ninguna llamada. Donde mejor funciona es con programas que compilas tú. Crea un programa de ejemplo que reserva memoria en un bucle: ```bash nano demo.c ``` ```c #include #include #include int main(void) { size_t total = 0; for (int i = 0; i < 10000; i++) { char *buf = malloc(64); snprintf(buf, 64, "item-%d", i); total += strlen(buf); free(buf); } printf("%zu\n", total); return 0; } ``` Compílalo desactivando esa protección y con enlace perezoso para que ltrace pueda interceptar las llamadas: ```bash gcc -O0 -fcf-protection=none -Wl,-z,lazy -o demo demo.c ``` Obtén un resumen con `-c`: ```bash ltrace -c ./demo ``` ```output 88890 % time seconds usecs/call calls function ------ ----------- ----------- --------- -------------------- 27.31 0.412780 41 10000 snprintf 24.66 0.372712 37 10000 malloc 24.12 0.364547 36 10000 free 23.90 0.361204 36 10000 strlen 0.01 0.000121 121 1 printf ------ ----------- ----------- --------- -------------------- 100.00 1.511364 40001 total ``` El resumen confirma 10 000 reservas y liberaciones de memoria. En una aplicación real, este patrón señala que conviene reutilizar un buffer en lugar de pedir memoria en cada iteración. Los tiempos absolutos están inflados por el propio rastreo; fíjate en las proporciones y en el número de llamadas. Para seguir solo unas funciones concretas, con su duración, usa `-e` y `-T`: ```bash ltrace -T -e malloc+free ./demo 2>&1 | head -n 6 ``` ```output demo->malloc(64) = 0x5a1c3f2e22a0 <0.000196> demo->free(0x5a1c3f2e22a0) = <0.000125> demo->malloc(64) = 0x5a1c3f2e22a0 <0.000118> ... ``` Con `-S`, ltrace muestra además las llamadas al sistema intercaladas con las de biblioteca, lo que ayuda a ver qué función de biblioteca provoca cada llamada al kernel: ```bash ltrace -S ./demo 2>&1 | tail -n 5 ``` Si ltrace no muestra nada con un binario del sistema, usa `perf` o `uftrace`, que no dependen de la tabla de enlaces. ## Paso 6: Un método de trabajo Estas herramientas rinden más si sigues siempre el mismo orden: 1. **Resumen primero.** `strace -c` (o `-c -w`) sobre el proceso o comando te dice qué tipo de llamada domina. 2. **Detalle filtrado después.** Rastrea solo ese grupo (`-e trace=%file`, `%network`...) con `-T` y `-tt`, guardando en fichero con `-o`. 3. **Ordena por duración** y busca repeticiones: miles de llamadas idénticas o unas pocas que tardan milisegundos. 4. **Confirma con otra herramienta.** Si el tiempo se va en CPU dentro de la aplicación y no en llamadas al sistema, strace no lo verá; ese es trabajo de `perf`. Como alternativa de bajo coste a strace en producción, `perf trace` rastrea llamadas al sistema sin detener el proceso en cada una. Está incluido en los paquetes `linux-tools` del kernel. Este comando lo ejecuta durante 10 segundos y, al recibir la señal de interrupción, imprime un resumen por llamada: ```bash sudo apt install linux-tools-common linux-tools-$(uname -r) sudo timeout -s INT 10 perf trace -s -p your_pid ``` ## Solución de problemas **`strace: attach: ptrace(PTRACE_SEIZE, ...): Operation not permitted`.** Estás intentando rastrear un proceso que no es hijo de tu shell sin privilegios. Usa `sudo`. Dentro de un contenedor Docker, además, el contenedor necesita la capacidad `SYS_PTRACE`. **La traza es enorme e ilegible.** Filtra con `-e trace=` por grupo o por llamadas concretas (`-e trace=openat,read`), usa `-o` para enviar la salida a un fichero y limita la duración con `timeout`. **El servicio va muy lento mientras lo rastreas.** Es el coste del rastreo con ptrace. Reduce el tiempo de captura, filtra las llamadas o usa `perf trace`. **ltrace no muestra llamadas.** El binario está compilado con `-fcf-protection` y enlace inmediato, como la mayoría de paquetes de Ubuntu. Recompila tu programa como en el paso 5 o usa otra herramienta. ## Conclusión Has usado strace para resumir y cronometrar llamadas al sistema, localizar las más lentas, detectar ficheros que no existen y engancharte a procesos en marcha, y ltrace para contar llamadas a bibliotecas en un programa propio. Como siguientes pasos, puedes perfilar el uso de CPU de la aplicación con `perf`, generar carga controlada con wrk mientras rastreas un servicio web o revisar los límites de E/S del disco si las lecturas y escrituras dominan el resumen.