J'ai un programme MPI) qui compile et exécute, mais j'aimerais le parcourir pour m'assurer que rien de bizarre ne se produit. Idéalement, j'aimerais un moyen simple de lier GDB à un processus particulier. , mais je ne sais pas vraiment si cela est possible ni comment le faire. Une alternative serait que chaque processus écrive la sortie du débogage dans un fichier journal séparé, mais cela ne donne pas vraiment la même liberté qu’un débogueur.
Y a-t-il de meilleures approches? Comment déboguer les programmes MPI?
Comme quelqu'un l'a dit, TotalView est la norme à cet égard. Mais cela vous coûtera un bras et une jambe.
Le site OpenMPI a un grand FAQ sur le débogage MPI =. L'élément n ° 6 de la FAQ décrit comment associer GDB à des processus MPI. Lire le tout, il y a quelques bons conseils.
Si vous constatez que vous avez trop de processus à suivre, vérifiez Outil d'analyse de trace de pile (STAT) . Chez Livermore, nous l’utilisons pour collecter des traces de pile provenant potentiellement de centaines de milliers de processus en cours et pour les représenter intelligemment aux utilisateurs. Ce n'est pas un débogueur complet (un débogueur complet ne peut jamais évoluer vers des cœurs de 208 Ko), mais il vous indiquera quels groupes de processus font la même chose. Vous pouvez ensuite consulter un représentant de chaque groupe dans un débogueur standard.
J'ai trouvé gdb très utile. Je l'utilise comme
mpirun -np <NP> xterm -e gdb ./program
Ceci lance les fenêtres xterm dans lesquelles je peux faire
run <arg1> <arg2> ... <argN>
fonctionne généralement bien
Vous pouvez également regrouper ces commandes en utilisant:
mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]
La plupart des messages ici concernent GDB, mais ne mentionnez pas comment attacher à un processus depuis le démarrage. Évidemment, vous pouvez attacher à tous les processus:
mpiexec -n X gdb ./a.out
Mais cela est extrêmement inefficace car vous devrez vous lancer pour lancer tous vos processus. Si vous voulez simplement déboguer un (ou un petit nombre de) MPI processus, vous pouvez l'ajouter en tant qu'exécutable séparé sur la ligne de commande en utilisant le :
opérateur:
mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out
Désormais, un seul de vos processus obtiendra la base de données GDB.
Comme d'autres l'ont déjà mentionné, si vous travaillez uniquement avec une poignée de MPI processus, vous pouvez essayer d'utiliser - plusieurs sessions gdb , le redoutable valgrind ou lancez votre propre solution printf/logging.
Si vous utilisez plus de processus que cela, vous commencez vraiment à avoir besoin d'un débogueur approprié. Le OpenMPI FAQ recommande à la fois Allinea DDT et TotalView .
Je travaille sur Allinea DDT . C'est un débogueur de code source graphique complet, donc oui, vous pouvez:
...etc. Si vous avez utilisé Eclipse ou Visual Studio, vous serez chez vous.
Nous avons ajouté des fonctionnalités intéressantes spécifiquement pour le débogage du code parallèle (que ce soit MPI, multi-thread ou CUDA):
Les variables scalaires sont automatiquement comparées entre tous les processus:
(source: allinea.com )
Vous pouvez également suivre et filtrer les valeurs des variables et des expressions sur les processus et la durée:
Il est largement utilisé parmi top5 sites HPC, tels que ORNL , NCSA , LLNL , Jülich et. Al.
L'interface est plutôt accrocheuse; nous avons chronométré et fusionné les piles et les variables de 220 000 processus à 0,1 seconde dans le cadre des tests d'acceptation sur le cluster Jaguar d'Oak Ridge.
@tgamblin a mentionné l'excellent STAT , qui s'intègre à Allinea DDT , ainsi que plusieurs autres projets open source populaires.
http://github.com/jimktrains/pgdb/tree/master est un utilitaire que j'ai écrit pour faire cela. Il y a des docs et n'hésitez pas à me poser des questions.
En gros, vous appelez un programme Perl qui encapsule GDB et les entonnoirs de conversion IO) sur un serveur central. Cela permet à GDB de s'exécuter sur chaque hôte et de vous y accéder sur chaque hôte du terminal.
Si vous êtes un utilisateur tmux
, vous serez très à l'aise avec le script de Benedikt Morbach : tmpi
Source primaire: https://github.com/moben/scripts/blob/master/tmpi
Fork: https://github.com/Azrael3000/tmpi
Avec celui-ci, vous avez plusieurs panneaux (nombre de processus) tous synchronisés (chaque commande est copiée sur tous les panneaux ou processus en même temps, ce qui vous fait gagner beaucoup de temps en comparant avec le xterm -e
_ approche). De plus, vous pouvez connaître les valeurs des variables dans le processus que vous voulez simplement faire print
sans avoir à passer à un autre panneau, cela affichera sur chaque panneau les valeurs de la variable pour chaque processus.
Si vous n'êtes pas un utilisateur tmux
, je vous recommande fortement de l'essayer et de le voir.
Utiliser screen
avec gdb
pour déboguer MPI les applications fonctionnent bien, surtout si xterm
n'est pas disponible ou si vous avez plus d'un Il y avait de nombreux pièges en cours de route avec les recherches de stackoverflow, je vais donc reproduire ma solution dans son intégralité.
Tout d'abord, ajoutez le code après MPI_Init pour imprimer le PID et arrêtez le programme pour attendre que vous vous connectiez. La solution standard semble être une boucle infinie; J'ai finalement opté pour raise(SIGSTOP);
, qui nécessite un appel supplémentaire de continue
pour s'échapper dans 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);
}
Après la compilation, exécutez le fichier exécutable en arrière-plan et récupérez le stderr. Vous pouvez alors grep
le fichier stderr pour un mot clé (ici le PID littéral) pour obtenir le PID et le rang de chaque processus.
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`)
Une session gdb peut être attachée à chaque processus avec gdb $MDRUN_EXE $PID
. Faire cela dans une session d'écran permet un accès facile à n'importe quelle session gdb. -d -m
Démarre l'écran en mode détaché, -S "P$RANK"
Vous permet de nommer l'écran pour un accès facile plus tard, et l'option -l
De bash le démarre en mode interactif et empêche gdb de sortir immédiatement.
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
Une fois que gdb a démarré dans les écrans, vous pouvez créer un script de saisie pour les écrans (afin de ne pas avoir à entrer tous les écrans et à taper la même chose) à l'aide de la commande -X stuff
De l'écran. Une nouvelle ligne est requise à la fin de la commande. Ici, les écrans sont accessibles par -S "P$i"
En utilisant les noms donnés précédemment. L'option -p 0
Est essentielle, sinon la commande échoue par intermittence (selon que vous vous êtes ou non déjà connecté à l'écran).
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
À ce stade, vous pouvez vous connecter à n’importe quel écran en utilisant screen -rS "P$i"
Et détacher en utilisant Ctrl+A+D
. Les commandes peuvent être envoyées à toutes les sessions de gdb en analogie avec la section de code précédente.
Le moyen "standard" de déboguer les programmes MPI) consiste à utiliser un débogueur qui prend en charge ce modèle d'exécution.
Sous UNIX, TotalView est supposé avoir un bon support pour MPI.
J'utilise cette petite méthode homebrewn pour attacher le débogueur à MPI - appelez la fonction suivante, DebugWait (), juste après MPI_Init () dans votre code. Maintenant, pendant que les processus attendent une entrée au clavier, vous avez tout le temps d’y attacher le débogueur et d’ajouter des points d’arrêt. Lorsque vous avez terminé, saisissez un seul caractère et vous êtes prêt à commencer.
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);
}
Bien sûr, vous voudriez compiler cette fonction uniquement pour les versions de débogage.
Il y a aussi mon outil open-source, padb, qui vise à aider à la programmation parallèle. Je l'appelle un "outil d'inspection des tâches" car il ne fonctionne pas uniquement comme un débogueur mais peut également fonctionner par exemple comme un programme parallèle semblable à un programme. Exécuté en mode "Rapport complet", il vous montrera les traces de chaque processus de votre application ainsi que les variables locales pour chaque fonction sur chaque rang (en supposant que vous ayez compilé avec -g). Il vous montrera également les "files de messages MPI", c'est-à-dire la liste des envois et des envois en attente pour chaque rang de la tâche.
En plus d'afficher le rapport complet, il est également possible d'indiquer à padb de zoomer sur des éléments d'information individuels du travail. Il existe une myriade d'options et d'éléments de configuration permettant de contrôler les informations affichées. Pour plus d'informations, reportez-vous à la page Web.
La commande pour attacher gdb à un processus mpi est incomplète, elle devrait être
mpirun -np <NP> xterm -e gdb ./program
Une brève discussion de mpi et gdb peut être trouvée ici
Je fais du débogage lié à MPI avec des traces de journal, mais vous pouvez également exécuter gdb si vous utilisez mpich2: MPICH2 et gdb . Cette technique est une bonne pratique en général lorsque vous traitez avec un processus difficile à lancer à partir d'un débogueur.
Un moyen assez simple de déboguer un programme MPI.
Dans la fonction main (), ajoutez un sommeil (quelques secondes)
Exécutez le programme comme d'habitude
$ mpirun -np <num_of_proc> <prog> <prog_args>
Le programme commencera et entrera dans le sommeil.
Vous aurez donc quelques secondes pour trouver vos processus par ps, exécuter gdb et les joindre.
Si vous utilisez un éditeur comme QtCreator, vous pouvez utiliser
Debug-> Démarrer le débogage-> Attacher à l'application en cours d'exécution
et vous trouvez des processus là-bas.
Une autre solution consiste à exécuter votre code dans SMPI, le MPI simulé. C'est un projet open source dans lequel je suis impliqué. Chaque rang MPI sera converti en threads du même processus UNIX. Vous pouvez ensuite facilement utiliser gdb pour passer au rang MPI.
SMPI propose d’autres avantages pour l’étude des MPI: clairevoyance (vous pouvez observer toutes les parties du système), reproductibilité (plusieurs exécutions entraînent exactement le même comportement, sauf indication contraire de votre part), absence de heisenbugs (la plate-forme simulée étant différente de celle de l'hôte), etc.
Pour plus d'informations, voir cette présentation , ou cela réponse associée .