debugging - ¿Cómo depuro un programa MPI?
(15)
Como dijo otra persona, TotalView es el estándar para esto. Pero te costará un brazo y una pierna.
El sitio OpenMPI tiene una gran pregunta frecuente sobre la depuración MPI . El ítem # 6 en las preguntas frecuentes describe cómo adjuntar procesos de GDB a MPI. Lea todo, hay algunos buenos consejos.
Sin embargo, si descubres que tienes demasiados procesos para realizar un seguimiento, echa un vistazo a Stack Trace Analysis Tool (STAT) . Usamos esto en Livermore para recopilar trazas de pila de potencialmente cientos de miles de procesos en ejecución y para representarlos inteligentemente a los usuarios. No es un depurador con todas las funciones (un depurador con todas las funciones nunca se escalaría a 208k núcleos), pero le dirá qué grupos de procesos están haciendo lo mismo. Luego puede pasar por un representante de cada grupo en un depurador estándar.
Tengo un programa MPI que compila y ejecuta, pero me gustaría revisarlo para asegurarme de que no ocurra nada extraño. Idealmente, me gustaría una forma simple de adjuntar GDB a cualquier proceso en particular, pero no estoy muy seguro de si eso es posible o cómo hacerlo. Una alternativa sería que cada proceso escriba la salida de depuración en un archivo de registro separado, pero esto realmente no da la misma libertad que un depurador.
¿Hay mejores enfoques? ¿Cómo se depuran los programas MPI?
Como han mencionado otros, si solo está trabajando con un puñado de procesos de MPI, puede intentar utilizar varias sesiones de gdb , la valgrind temible o lanzar su propia solución de valgrind / registro.
Si usa más procesos que eso, realmente comienza a necesitar un depurador adecuado. Las preguntas frecuentes de OpenMPI recomiendan tanto Allinea DDT como TotalView .
Yo trabajo en Allinea DDT . Es un depurador de código fuente gráfico con todas las funciones, así que sí, puedes:
- Depurar o adjuntar a (más de 200k) procesos MPI
- Paso y pausa en grupos o individualmente
- Agregar puntos de interrupción, relojes y puntos de referencia
- Captura errores y fugas de memoria
...y así. Si has usado Eclipse o Visual Studio, entonces estarás en casa.
Agregamos algunas características interesantes específicamente para la depuración de código paralelo (ya sea MPI, multiproceso o CUDA):
Las variables escalares se comparan automáticamente en todos los procesos:
También puede rastrear y filtrar los valores de las variables y expresiones sobre los procesos y el tiempo:
Es ampliamente utilizado entre los sitios top500 HPC, como ORNL , NCSA , LLNL , Jülich et. Alabama.
La interfaz es bastante ágil; cronometramos el paso y la fusión de las pilas y variables de 220,000 procesos a 0.1s como parte de las pruebas de aceptación en el clúster Jaguar de Oak Ridge.
@tgamblin mencionó el excelente STAT , que se integra con Allinea DDT , al igual que otros proyectos populares de código abierto.
El comando para adjuntar gdb a un proceso mpi está incompleto, debe ser
mpirun -np <NP> xterm -e gdb ./program
Una breve discusión de mpi y gdb se puede encontrar here
Hago algunas depuraciones relacionadas con MPI con rastros de registro, pero también puedes ejecutar gdb si estás usando mpich2: MPICH2 y gdb . Esta técnica es una buena práctica en general cuando se trata de un proceso que es difícil de ejecutar desde un depurador.
He encontrado gdb bastante útil. Lo uso como
mpirun -np <NP> xterm -e gdb ./program
Esto inicia las ventanas xterm en las que puedo hacer
run <arg1> <arg2> ... <argN>
por lo general funciona bien
También puede empaquetar estos comandos usando:
mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]
La forma "estándar" de depurar programas MPI es mediante el uso de un depurador que admita ese modelo de ejecución.
En UNIX, se dice que TotalView tiene un buen soporte para MPI.
Muchos de los mensajes aquí son sobre GDB, pero no mencionan cómo adjuntarlos a un proceso desde el inicio. Obviamente, puede adjuntar a todos los procesos:
mpiexec -n X gdb ./a.out
Pero eso es tremendamente ineficaz ya que tendrás que moverte para comenzar todos tus procesos. Si solo desea depurar uno (o una pequeña cantidad de) proceso de MPI, puede agregarlo como un ejecutable independiente en la línea de comando usando el operador:
mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out
Ahora solo uno de sus procesos obtendrá GDB.
Otra solución es ejecutar su código dentro de SMPI, el MPI simulado. Ese es un proyecto de código abierto en el que estoy involucrado. Cada rango de MPI se convertirá en hilos del mismo proceso de UNIX. Luego puede usar gdb fácilmente para avanzar en los rangos de MPI.
SMPI propone otras ventajas para el estudio de aplicaciones MPI: clairevoyance (puede observar todas las partes del sistema), reproducibilidad (varias ejecuciones conducen a exactamente el mismo comportamiento a menos que así lo especifique), ausencia de heisenbugs (ya que la plataforma simulada se mantiene diferente del anfitrión), etc.
Para obtener más información, vea esta presentación o esa respuesta relacionada .
Si usted es usuario de tmux
, se sentirá muy cómodo utilizando el guión de Benedikt Morbach : tmpi
Fuente original: https://github.com/moben/scripts/blob/master/tmpi
Tenedor: https://github.com/Azrael3000/tmpi
Con él tiene múltiples paneles (número de procesos) todos sincronizados (cada comando se copia en todos los paneles o procesos al mismo tiempo, por lo que ahorra mucho tiempo comparando con el enfoque xterm -e
). Además puede conocer los valores de las variables en el proceso que desea simplemente haciendo una print
sin tener que moverse a otro panel, esto imprimirá en cada panel los valores de la variable para cada proceso.
Si no eres usuario de tmux
, recomiendo encarecidamente probarlo y ver.
También está mi herramienta de código abierto, padb, que tiene como objetivo ayudar con la programación paralela. Yo lo llamo una "Herramienta de Inspección de Trabajo" ya que funciona no solo como un depurador también puede funcionar, por ejemplo, como un programa paralelo similar. Ejecutar en el modo "Informe completo" le mostrará los rastros de pila de cada proceso dentro de su aplicación junto con las variables locales para cada función sobre cada rango (suponiendo que haya compilado con -g). También le mostrará las "colas de mensajes MPI", que es la lista de envíos pendientes y recibe para cada rango dentro del trabajo.
Además de mostrar el informe completo, también es posible indicar a padb que amplíe partes de información individuales dentro del trabajo; hay una gran cantidad de opciones y elementos de configuración para controlar qué información se muestra; consulte la página web para obtener más detalles.
Usar la screen
junto con gdb
para depurar aplicaciones MPI funciona muy bien, especialmente si xterm
no está disponible o si se trata de más de unos pocos procesadores. Hubo muchas trampas en el camino con las búsquedas simultáneas de , así que reproduciré mi solución por completo.
Primero, agregue el código después de MPI_Init para imprimir el PID y detenga el programa para esperar que se adjunte. La solución estándar parece ser un ciclo infinito; Finalmente me decidí por raise(SIGSTOP);
, que requiere una llamada adicional de continue
escapando dentro de gdb.
}
int i, id, nid;
MPI_Comm_rank(MPI_COMM_WORLD,&id);
MPI_Comm_size(MPI_COMM_WORLD,&nid);
for (i=0; i<nid; i++) {
MPI_Barrier(MPI_COMM_WORLD);
if (i==id) {
fprintf(stderr,"PID %d rank %d/n",getpid(),id);
}
MPI_Barrier(MPI_COMM_WORLD);
}
raise(SIGSTOP);
}
Después de compilar, ejecute el ejecutable en segundo plano y capture el stderr. A continuación, puede grep
el archivo stderr para algunas palabras clave (aquí literal PID) para obtener el PID y el rango de cada proceso.
MDRUN_EXE=../../Your/Path/To/bin/executable
MDRUN_ARG="-a arg1 -f file1 -e etc"
mpiexec -n 1 $MDRUN_EXE $MDRUN_ARG >> output 2>> error &
sleep 2
PIDFILE=pid.dat
grep PID error > $PIDFILE
PIDs=(`awk ''{print $2}'' $PIDFILE`)
RANKs=(`awk ''{print $4}'' $PIDFILE`)
Se puede adjuntar una sesión de gdb a cada proceso con gdb $MDRUN_EXE $PID
. Hacerlo dentro de una sesión de pantalla permite un fácil acceso a cualquier sesión de gdb. -d -m
inicia la pantalla en modo separado, -S "P$RANK"
permite nombrar la pantalla para acceder más fácilmente más tarde, y la opción -l
para bash la inicia en modo interactivo y evita que gdb salga inmediatamente.
for i in `awk ''BEGIN {for (i=0;i<''${#PIDs[@]}'';i++) {print i}}''`
do
PID=${PIDs[$i]}
RANK=${RANKs[$i]}
screen -d -m -S "P$RANK" bash -l -c "gdb $MDRUN_EXE $PID"
done
Una vez que gdb ha comenzado en las pantallas, puede guiar la entrada a las pantallas (para que no tenga que ingresar todas las pantallas y escribir lo mismo) usando el comando -X stuff
la pantalla. Se requiere una nueva línea al final del comando. Aquí las pantallas son accedidas por -S "P$i"
usando los nombres dados previamente. La opción -p 0
es crítica; de lo contrario, el comando falla intermitentemente (según haya o no conectado previamente a la pantalla).
for i in `awk ''BEGIN {for (i=0;i<''${#PIDs[@]}'';i++) {print i}}''`
do
screen -S "P$i" -p 0 -X stuff "set logging file debug.$i.log
"
screen -S "P$i" -p 0 -X stuff "set logging overwrite on
"
screen -S "P$i" -p 0 -X stuff "set logging on
"
screen -S "P$i" -p 0 -X stuff "source debug.init
"
done
En este punto, puede adjuntar a cualquier pantalla usando la screen -rS "P$i"
y separar usando Ctrl+A+D
Los comandos se pueden enviar a todas las sesiones de gdb en analogía con la sección de código anterior.
Utilizo este pequeño método homebrewn para adjuntar depurador a procesos MPI: llame a la siguiente función, DebugWait (), justo después de MPI_Init () en su código. Ahora, mientras los procesos esperan la entrada del teclado, tiene todo el tiempo para adjuntar el depurador y agregar puntos de interrupción. Cuando haya terminado, proporcione una sola entrada de carácter y estará listo para comenzar.
static void DebugWait(int rank) {
char a;
if(rank == 0) {
scanf("%c", &a);
printf("%d: Starting now/n", rank);
}
MPI_Bcast(&a, 1, MPI_BYTE, 0, MPI_COMM_WORLD);
printf("%d: Starting now/n", rank);
}
Por supuesto, le conviene compilar esta función solo para compilaciones de depuración.
mpirun -gdb
Gracias a http://www.ncsa.illinois.edu/UserInfo/Resources/Hardware/CommonDoc/mpich2_gdb.html
http://github.com/jimktrains/pgdb/tree/master es una utilidad que escribí para hacer esto mismo. Hay algunos documentos y no dude en llamarme para preguntas.
Básicamente llama a un programa perl que envuelve GDB y canaliza su IO a un servidor central. Esto permite que GDB se ejecute en cada host y que pueda acceder a él en cada host en la terminal.
http://valgrind.org/ nuf dijo
Enlace más específico: valgrind