Ir al contenido

Comandos de py-spy

py-spy es un perfilador por muestreo para programas Python escrito en Rust. Puede perfilar procesos Python en ejecución sin modificar código ni reiniciarlos, produciendo flame graphs y vistas top en tiempo real con mínima sobrecarga.

Instalación

Linux/Ubuntu

# Install via pip
pip install py-spy

# Install via cargo (Rust)
cargo install py-spy

# Install via package manager (Arch Linux)
pacman -S py-spy

# Verify installation
py-spy --version

# py-spy requires root to attach to processes you don't own
# Or set ptrace permissions:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

py-spy record — Generar perfiles

# Grabar un flame graph SVG desde un script
py-spy record -o profile.svg -- python my_script.py

# Grabar desde un proceso en ejecución por PID
sudo py-spy record -o profile.svg --pid 1234

# Grabar durante una duración específica (segundos)
sudo py-spy record -o profile.svg --pid 1234 --duration 60

# Establecer tasa de muestreo (predeterminado 100 Hz)
py-spy record -o profile.svg --rate 200 -- python my_script.py

# Grabar solo hilos inactivos (esperando/durmiendo)
py-spy record -o profile.svg --idle --pid 1234

# Grabar incluyendo marcos nativos de extensiones C/C++
py-spy record -o profile.svg --native -- python my_script.py

# Grabar subprocesos (seguir forks)
py-spy record -o profile.svg --subprocesses -- python my_script.py

# Grabar solo hilos que mantienen el GIL
py-spy record -o profile.svg --gil -- python my_script.py

# Grabar con números de línea de funciones
py-spy record -o profile.svg --function -- python my_script.py

# Modo no bloqueante (puede perder algunas muestras)
py-spy record -o profile.svg --nonblocking --pid 1234

Formatos de salida

# Flame graph SVG (predeterminado)
py-spy record -o profile.svg --format flamegraph -- python my_script.py

# Formato speedscope (ver en https://speedscope.app)
py-spy record -o profile.speedscope.json --format speedscope -- python my_script.py

# Salida de muestras sin procesar
py-spy record -o profile.raw --format raw -- python my_script.py

# Formato Chrome trace (ver en chrome://tracing)
py-spy record -o profile.json --format chrometrace -- python my_script.py

# Formato pprof (compatible con herramientas Go pprof)
py-spy record -o profile.pb.gz --format pprof -- python my_script.py

py-spy top — Perfilado en vivo

# Vista top en vivo de un proceso en ejecución
sudo py-spy top --pid 1234

# Vista en vivo de un script
py-spy top -- python my_script.py

# Incluir marcos nativos en vista top
sudo py-spy top --native --pid 1234

# Mostrar hilos inactivos
sudo py-spy top --idle --pid 1234

# Incluir subprocesos
py-spy top --subprocesses -- python my_script.py

# Tasa de muestreo personalizada
sudo py-spy top --rate 50 --pid 1234

py-spy dump — Volcado de hilos

# Volcar trazas de pila actuales de todos los hilos
sudo py-spy dump --pid 1234

# Volcar con variables locales
sudo py-spy dump --locals --pid 1234

# Volcar en formato JSON
sudo py-spy dump --json --pid 1234

# Volcar incluyendo marcos nativos
sudo py-spy dump --native --pid 1234

Perfilado de escenarios específicos

Aplicaciones web

# Perfilar una aplicación Django
py-spy record -o django_profile.svg -- python manage.py runserver

# Perfilar una aplicación Flask
py-spy record -o flask_profile.svg -- python app.py

# Perfilar un worker de Gunicorn (adjuntar al PID del worker específico)
sudo py-spy record -o gunicorn_profile.svg --pid $(pgrep -f 'gunicorn.*worker')

# Perfilar uvicorn (ASGI)
py-spy record -o uvicorn_profile.svg --subprocesses -- uvicorn main:app --workers 4

Procesamiento de datos

# Perfilar una carga de trabajo pandas/numpy con extensiones nativas
py-spy record -o data_profile.svg --native -- python etl_pipeline.py

# Perfilar con subprocesos (multiprocessing)
py-spy record -o multiproc_profile.svg --subprocesses -- python parallel_job.py

Servicios de larga ejecución

# Adjuntar a un demonio en ejecución durante 5 minutos
sudo py-spy record -o daemon_profile.svg --pid 1234 --duration 300

# Perfilado periódico vía cron (instantáneas de 30 segundos)
# Add to crontab: */5 * * * * sudo py-spy record -o /tmp/profile_$(date +\%s).svg --pid $(cat /var/run/myapp.pid) --duration 30

Docker y contenedores

# Perfilar un proceso Python en un contenedor Docker
# 1. Encontrar el PID del contenedor en el host
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' my_container)

# 2. Adjuntar py-spy al namespace PID del contenedor
sudo py-spy record -o container_profile.svg --pid $CONTAINER_PID

# O ejecutar py-spy dentro del contenedor
docker exec -it my_container py-spy top --pid 1

# Docker run con capacidad SYS_PTRACE para py-spy
docker run --cap-add SYS_PTRACE my_image

Kubernetes

# Perfilar un pod Python
# 1. Encontrar el nodo y PID
kubectl exec -it my-pod -- pgrep -f python

# 2. Usar kubectl exec con py-spy instalado en el contenedor
kubectl exec -it my-pod -- py-spy record -o /tmp/profile.svg --pid 1 --duration 30

# 3. Copiar el perfil fuera
kubectl cp my-pod:/tmp/profile.svg ./profile.svg

# Para contenedores de depuración efímeros
kubectl debug -it my-pod --image=py-spy-debug --target=my-container -- py-spy top --pid 1

Interpretación de la salida

Flame Graph (SVG)

# Lectura del flame graph:
# - Eje X: proporción del tiempo total de CPU (más ancho = más CPU)
# - Eje Y: profundidad de la pila (abajo = punto de entrada, arriba = función hoja)
# - Cada caja: una llamada a función
# - Clic para ampliar, Ctrl+F para buscar
# - El color es aleatorio (no significativo por defecto)

Columnas de la vista Top

# Explicación de columnas en py-spy top:
# %Own    — Tiempo de CPU solo en esta función (tiempo propio)
# %Total  — Tiempo de CPU en esta función + llamadas
# OwnTime — Tiempo propio absoluto
# TotalTime — Tiempo total absoluto
# Function — Módulo y nombre de función
# Line    — Archivo fuente y número de línea

Combinación con otras herramientas

# Generar pilas plegadas para las herramientas FlameGraph de Brendan Gregg
py-spy record -o profile.raw --format raw -- python my_script.py
# El formato raw ya es compatible con pilas plegadas

# Convertir a speedscope para análisis interactivo
py-spy record -o profile.speedscope.json --format speedscope -- python my_script.py
# Abrir en https://speedscope.app

# Convertir salida pprof para uso con go tool pprof
py-spy record -o profile.pb.gz --format pprof -- python my_script.py
go tool pprof -http=:8080 profile.pb.gz

Solución de problemas

# Permiso denegado — se necesita root o acceso ptrace
sudo py-spy top --pid 1234
# Or: echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

# "Error: Failed to find python version" — especificar ruta de Python
sudo py-spy record --pid 1234 --python-program /usr/bin/python3

# Símbolos faltantes en modo nativo — instalar paquetes de depuración
sudo apt install python3-dbg

# Proceso no encontrado en contenedor — usar PID del host
docker inspect --format '{{.State.Pid}}' my_container

Referencia rápida

ComandoPropósito
py-spy recordGenerar flame graph o archivo de perfil
py-spy topVista de perfilado de CPU en vivo tipo top
py-spy dumpVolcado único de pilas de hilos
--pid PIDAdjuntar a proceso en ejecución
--nativeIncluir marcos de extensiones C/C++
--subprocessesSeguir procesos hijos
--gilSolo perfilar hilos que mantienen el GIL
--rate NEstablecer frecuencia de muestreo (Hz)
--formatFormato de salida (flamegraph, speedscope, raw, chrometrace, pprof)