¿Cómo depuro un programa MPI?


129

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 realmente seguro de si eso es posible o cómo hacerlo. Una alternativa sería hacer 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?

Respuestas:


62

Como alguien más dijo, TotalView es el estándar para esto. Pero te costará un brazo y una pierna.

El sitio OpenMPI tiene excelentes preguntas frecuentes sobre depuración de MPI . El ítem # 6 en las preguntas frecuentes describe cómo adjuntar GDB a los procesos MPI. Lea todo esto, hay algunos buenos consejos.

Sin embargo, si descubre que tiene demasiados procesos para realizar un seguimiento, consulte la Herramienta de análisis de seguimiento de pila (STAT) . Usamos esto en Livermore para recopilar rastros de pila de potencialmente cientos de miles de procesos en ejecución y para representarlos de manera inteligente ante los usuarios. No es un depurador con todas las funciones (un depurador con todas las funciones nunca se escalará 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.


14
A partir de 2010 Allinea DDT es un depurador con todas las funciones que se escala a más de 208k núcleos
Mark

1
Así que seguiré adelante y votaré la respuesta de @ Mark aquí. El DDT es bueno. Pruébalo también. TotalView también se integra con STAT ahora, por lo que si su sitio tiene una instalación de TotalView, también podría intentarlo. LLNL mantiene TotalView y DDT, y es bueno que TotalView finalmente tenga una dura competencia.
Todd Gamblin

Me gustaría secundar el enlace a Preguntas frecuentes sobre depuración de MPI ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Específicamente, la viñeta 6 es una manera buena, rápida y fácil (¡suficiente para mí!) De entender al menos una forma de depurar un proceso individual.
Jeff

Los pasos en el n. ° 6 de la página de preguntas frecuentes funcionaron excelentemente para mí y me ayudaron a resolver mi problema. Muchas gracias por esto.
Jon Deaton el

86

He encontrado gdb bastante útil. Lo uso como

mpirun -np <NP> xterm -e gdb ./program 

Esto lanza las ventanas xterm en las que puedo hacer

run <arg1> <arg2> ... <argN>

por lo general funciona bien

También puede empaquetar estos comandos juntos usando:

mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]

¿Cómo puedo enviar la misma entrada a todos los NP gdb xterms? Por ejemplo, quiero agregar dos puntos de interrupción a cada proceso, y hay 16 procesos. ¿Hay alguna alternativa a xterm para hacer esto? ¿Podemos conectar sesiones en una sola instancia de pantalla, tmux o Terminator de Chris Jones?
osgx

@osgx Puede hacer esto guardando los comandos ("break xxx", "break yyy", "run") <file>y pasando -x <file>a gdb.
eush77

pero me encuentro con un error, el mensaje de error es "error execvp en el archivo xterm (No existe tal archivo o directorio)"
hitwlh

cuando intento esto con jdb y OpenMPI no funciona, es decir, cada instancia de jdb ve num_ranks de 1 en lugar de lo que se le da al argumento -np. alguna idea de por qué?
Michel Müller el

26

Muchas de las publicaciones aquí son sobre GDB, pero no mencionan cómo adjuntar 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á que rebotar para iniciar todos sus procesos. Si solo desea depurar un (o un pequeño número de) procesos MPI, puede agregarlo como un ejecutable separado en la línea de comando utilizando el :operador:

mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out

Ahora solo uno de sus procesos obtendrá GDB.


Puedo usar "mpiexec -n X gdb ./a.out", pero ¿hay alguna manera de usar el modo gdb -tui?
hitwlh

16

Como otros han mencionado, si solo está trabajando con un puñado de procesos MPI, puede intentar usar múltiples sesiones de gdb , el valgrind reducible o rodar su propia solución printf / logging.

Si está utilizando más procesos que eso, realmente comienza a necesitar un depurador adecuado. Las preguntas frecuentes de OpenMPI recomiendan Allinea DDT y TotalView .

Yo trabajo en Allinea DDT . Es un depurador de código fuente gráfico con todas las funciones, así que sí, puede:

  • Depurar o adjuntar a (más de 200k) procesos MPI
  • Paso y pausarlos en grupos o individualmente
  • Agregue puntos de interrupción, relojes y puntos de rastreo
  • Captura errores de memoria y fugas

...y así. Si ha usado Eclipse o Visual Studio, estará en casa.

Agregamos algunas características interesantes específicamente para depurar código paralelo (ya sea MPI, multiproceso o CUDA):

  • Las variables escalares se comparan automáticamente en todos los procesos: (fuente: allinea.com )Sparklines que muestran valores en todos los procesos

  • También puede rastrear y filtrar los valores de variables y expresiones sobre procesos y tiempo: Valores de registro de puntos de seguimiento a lo largo del tiempo

Es ampliamente utilizado entre los 500 principales sitios de 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.



7

Si eres tmuxusuario, te sentirás muy cómodo usando el script de Benedikt Morbach :tmpi

Fuente original: https://github.com/moben/scripts/blob/master/tmpi

Fork: https://github.com/Azrael3000/tmpi

Con él, tiene varios 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 en comparación con el xterm -eenfoque). Además, puede conocer los valores de las variables en el proceso que desea hacer printsin tener que pasar a otro panel, esto imprimirá en cada panel los valores de la variable para cada proceso.

Si no eres tmuxusuario, te recomiendo que lo pruebes y lo veas.


2
Dado que tmpi es realmente fantástico y exactamente lo que estaba buscando, lo bifurqué en mi cuenta de github : github.com/Azrael3000/tmpi desde que el autor original lo eliminó
Azrael3000

6

http://github.com/jimktrains/pgdb/tree/master es una utilidad que escribí para hacer esto mismo. Hay algunos documentos y no dude en enviarme 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.


¡Gracias! Definitivamente veré esto la próxima vez que esté trabajando en MPI.
Jay Conrod

5

El uso screenjunto con la gdbdepuración de aplicaciones MPI funciona bien, especialmente si xtermno está disponible o si se trata de más de unos pocos procesadores. Hubo muchos escollos en el camino con las búsquedas de stackoverflow que lo acompañan, por lo que reproduciré mi solución en su totalidad.

Primero, agregue código después de MPI_Init para imprimir el PID y detenga el programa para esperar a que lo adjunte. La solución estándar parece ser un bucle infinito; Finalmente me decidí raise(SIGSTOP);, lo que requiere una llamada adicional continuepara escapar 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. Luego puede grepusar el archivo stderr para alguna palabra clave (aquí PID literal) 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 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 -minicia la pantalla en modo separado, le -S "P$RANK"permite asignarle un nombre a la pantalla para acceder fácilmente más tarde, y la -lopción de bash la inicia en modo interactivo y evita que gdb salga de inmediato.

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 ingresar la secuencia de comandos en las pantallas (para que no tenga que ingresar a todas las pantallas y escribir lo mismo) usando el -X stuffcomando de pantalla . Se requiere una nueva línea al final del comando. Aquí se accede a las pantallas -S "P$i"utilizando los nombres dados previamente. La -p 0opción es crítica, de lo contrario, el comando falla intermitentemente (en función de si se ha conectado o no 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 adjuntarlo a cualquier pantalla usando screen -rS "P$i"y desconectar usando Ctrl+A+D. Se pueden enviar comandos a todas las sesiones de gdb en analogía con la sección de código anterior.


3

También está mi herramienta de código abierto, padb, que tiene como objetivo ayudar con la programación paralela. Lo llamo una "Herramienta de inspección de trabajos", ya que funciona no solo como un depurador, sino que también puede funcionar, por ejemplo, como un programa paralelo superior. Ejecutar en modo "Informe completo" le mostrará los rastros de la pila de cada proceso dentro de su aplicación junto con variables locales para cada función en cada rango (suponiendo que compiló con -g). También le mostrará las "colas de mensajes MPI", que es la lista de envíos y recibos pendientes para cada rango dentro del trabajo.

Además de mostrar el informe completo, también es posible decirle a Padb que amplíe los bits individuales de información dentro del trabajo, hay una gran cantidad de opciones y elementos de configuración para controlar la información que se muestra, consulte la página web para obtener más detalles.

Padb


3

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.


2

Utilizo este pequeño método casero para adjuntar el depurador a los 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 adjuntarles el depurador y agregar puntos de interrupción. Cuando haya terminado, proporcione una entrada de un solo 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, desearía compilar esta función solo para compilaciones de depuración.


MPI ha requerido la mayoría de las declaraciones de depuración que he escrito incluso para un código simple. (risas) Esto puede ser muy útil.
Troggy el

3
Esta solución es similar a la viñeta 6 aquí ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Puede mejorar un poco su código agregando gethostname(hostname, sizeof(hostname)); printf("PID %d on host %s ready for attach\n", getpid(), hostname);. Luego, se adjunta al proceso escribiendo rsh <hostname_from_print_statement>y finalmente gdb --pid=<PID_from_print_statement>.
Jeff

2

El comando para adjuntar gdb a un proceso mpi está incompleto, debería ser

mpirun -np <NP> xterm -e gdb ./program 

Una breve discusión de mpi y gdb se puede encontrar aquí


2

Una manera bastante simple de depurar un programa MPI.

En la función main () agregue sleep (some_seconds)

Ejecute el programa como siempre

$ mpirun -np <num_of_proc> <prog> <prog_args>

El programa comenzará y se dormirá.

Entonces tendrá unos segundos para encontrar sus procesos por ps, ejecute gdb y adjúntelos.

Si usa algún editor como QtCreator, puede usar

Depuración-> Iniciar depuración-> Adjuntar a la aplicación en ejecución

y encontrar sus procesos allí.


1

Realizo algunas depuraciones relacionadas con MPI con trazas de registro, pero también puede ejecutar gdb si está 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 iniciar desde un depurador.


Cambió a otro enlace que no está roto, agregó algunos comentarios.
Jim Hunziker


0

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 MPI se convertirá en hilos del mismo proceso UNIX. Luego puede usar fácilmente gdb para subir los rangos de MPI.

SMPI propone otras ventajas para el estudio de aplicaciones MPI: clarividencia (puede observar todas las partes del sistema), reproducibilidad (varias ejecuciones conducen al mismo comportamiento exacto a menos que lo especifique), ausencia de errores detectados (ya que la plataforma simulada se mantiene diferente del anfitrión), etc.

Para obtener más información, consulte esta presentación o la respuesta relacionada .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.