Comment déboguer un programme MPI?

129

J'ai un programme MPI qui se compile et s'exécute, mais j'aimerais le parcourir pour m'assurer que rien de bizarre ne se passe. Idéalement, j'aimerais un moyen simple d'attacher GDB à un processus particulier, mais je ne sais pas vraiment si c'est possible ou comment le faire. Une alternative serait que chaque processus écrive la sortie de débogage dans un fichier journal séparé, mais cela ne donne pas vraiment la même liberté qu'un débogueur.

Existe-t-il de meilleures approches? Comment déboguez-vous les programmes MPI?

Jay Conrod
la source

Réponses:

62

Comme quelqu'un l'a dit, TotalView est la norme pour cela. Mais cela vous coûtera un bras et une jambe.

Le site OpenMPI a une excellente FAQ sur le débogage MPI . L'élément n ° 6 de la FAQ décrit comment attacher GDB aux processus MPI. Lisez le tout, il y a quelques bons conseils.

Si vous constatez que vous avez beaucoup trop de processus à suivre, consultez l' outil d'analyse de trace de pile (STAT) . Nous l'utilisons chez Livermore pour collecter les traces de pile de potentiellement des centaines de milliers de processus en cours d'exécution et pour les représenter intelligemment aux utilisateurs. Ce n'est pas un débogueur complet (un débogueur complet ne serait jamais mis à l'échelle jusqu'à 208k cœurs), mais il vous indiquera quels groupes de processus font la même chose. Vous pouvez ensuite parcourir un représentant de chaque groupe dans un débogueur standard.

Todd Gamblin
la source
14
À partir de 2010, Allinea DDT est un débogueur complet qui évolue à plus de 208k cœurs
Mark
1
Je vais donc aller de l'avant et voter pour la réponse de @ Mark ici. Le DDT est sympa. Essayez-le aussi. TotalView s'intègre également à STAT maintenant, donc si votre site dispose d'une installation TotalView, vous pouvez également l'essayer. LLNL conserve TotalView et DDT, et c'est bien que TotalView ait enfin une concurrence féroce.
Todd Gamblin
Je voudrais seconder le lien vers la FAQ sur le débogage MPI ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Plus précisément, la puce 6 est un bon moyen, rapide et facile (assez pour moi même!) À comprendre pour au moins déboguer un processus individuel.
Jeff
Les étapes du n ° 6 de la page FAQ ont parfaitement fonctionné pour moi et m'ont aidé à résoudre mon problème. Merci beaucoup pour cela.
Jon Deaton
86

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] [...]
messenjah
la source
Comment puis-je envoyer la même entrée à tous les xterms NP gdb? Par exemple, je veux ajouter deux points d'arrêt à chaque processus, et il y a 16 processus. Existe-t-il une alternative à xterm pour faire cela? Pouvons-nous connecter des sessions en une seule instance de screen, tmux ou Terminator de Chris Jones?
osgx
@osgx Vous pouvez le faire en sauvegardant les commandes ("break xxx", "break yyy", "run") <file>et en les passant -x <file>à gdb.
eush77
mais je rencontre une erreur, le message d'erreur est "erreur execvp sur le fichier xterm (aucun fichier ou répertoire de ce type)"
hitwlh
quand j'essaye cela avec jdb et OpenMPI cela ne fonctionne pas, c'est-à-dire que chaque instance jdb voit num_ranks de 1 au lieu de ce qui est donné à l'argument -np. une idée pourquoi?
Michel Müller
26

De nombreux articles ici concernent GDB, mais ne mentionnez pas comment s'attacher à un processus depuis le démarrage. Évidemment, vous pouvez vous attacher à tous les processus:

mpiexec -n X gdb ./a.out

Mais c'est extrêmement inefficace car vous devrez rebondir pour démarrer tous vos processus. Si vous souhaitez simplement déboguer un (ou un petit nombre de) processus MPI, vous pouvez l'ajouter en tant qu'exécutable séparé sur la ligne de commande à l'aide de l' :opérateur:

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

Désormais, un seul de vos processus obtiendra GDB.

Wesley Bland
la source
Je peux utiliser "mpiexec -n X gdb ./a.out", mais y a-t-il un moyen d'utiliser le mode gdb -tui?
hitwlh
16

Comme d'autres l'ont mentionné, si vous ne travaillez qu'avec une poignée de processus MPI, vous pouvez essayer d'utiliser plusieurs sessions gdb , le redoutable valgrind ou lancer votre propre solution printf / logging.

Si vous utilisez plus de processus que cela, vous commencez vraiment à avoir besoin d'un débogueur approprié. La FAQ OpenMPI recommande Allinea DDT et TotalView .

Je travaille sur Allinea DDT . C'est un débogueur de code source graphique complet, donc oui, vous pouvez:

  • Déboguer ou attacher à (plus de 200 000) processus MPI
  • Étape et mettez-les en pause en groupes ou individuellement
  • Ajouter des points d'arrêt, des montres et des points de trace
  • Attraper les erreurs de mémoire et les fuites

...etc. Si vous avez utilisé Eclipse ou Visual Studio, vous serez chez vous.

Nous avons ajouté quelques fonctionnalités intéressantes spécifiquement pour le débogage de code parallèle (que ce soit MPI, multi-thread ou CUDA):

  • Les variables scalaires sont automatiquement comparées à travers tous les processus: (source: allinea.com )Sparklines montrant les valeurs à travers les processus

  • Vous pouvez également suivre et filtrer les valeurs des variables et des expressions au fil des processus et du temps: Les valeurs du journal des points de trace dans le temps

Il est largement utilisé parmi les 500 meilleurs sites HPC, tels que ORNL , NCSA , LLNL , Jülich et. Al.

L'interface est assez vive; nous avons chronométré l'étape et la fusion des piles et des variables de 220 000 processus à 0,1 s 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 , tout comme plusieurs autres projets open source populaires.

marque
la source
7

Si vous êtes un tmuxutilisateur, vous vous sentirez très à l'aise avec le script de Benedikt Morbach :tmpi

Source primaire: https://github.com/moben/scripts/blob/master/tmpi

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

Avec lui, 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 permet de gagner beaucoup de temps par rapport à l' xterm -eapproche). De plus, vous pouvez connaître les valeurs des variables dans le processus que vous voulez simplement faire printsans avoir à passer à un autre panneau, cela imprimera sur chaque panneau les valeurs de la variable pour chaque processus.

Si vous n'êtes pas un tmuxutilisateur, je vous recommande fortement de l'essayer et de le voir.

GG1991
la source
2
Puisque tmpi est vraiment fantastique et exactement ce que je cherchais, je l'ai fourché sur mon compte github: github.com/Azrael3000/tmpi depuis que l'auteur original l'a supprimé
Azrael3000
6

http://github.com/jimktrains/pgdb/tree/master est un utilitaire que j'ai écrit pour faire exactement cela. Il y a quelques documents et n'hésitez pas à m'envoyer un message pour des questions.

Vous appelez essentiellement un programme perl qui encapsule GDB et achemine ses E / S vers un serveur central. Cela permet à GDB de fonctionner sur chaque hôte et pour vous d'y accéder sur chaque hôte du terminal.

Jim Keener
la source
Merci! Je vais certainement vérifier cela la prochaine fois que je travaillerai dans MPI.
Jay Conrod
5

L'utilisation screenavec gdbpour déboguer les applications MPI fonctionne bien, surtout si elle xtermn'est pas disponible ou si vous avez affaire à plus de quelques processeurs. Il y avait de nombreux pièges en cours de route avec les recherches d'accompagnement de stackoverflow, je vais donc reproduire ma solution dans son intégralité.

Tout d'abord, ajoutez du code après MPI_Init pour imprimer le PID et arrêtez le programme pour attendre que vous l'attachiez. La solution standard semble être une boucle infinie; J'ai finalement opté pour raise(SIGSTOP);, ce qui nécessite un appel supplémentaire continuepour 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 l'exécutable en arrière-plan et récupérez le fichier stderr. Vous pouvez ensuite greple fichier stderr pour un mot-clé (ici 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. Le faire dans une session d'écran permet un accès facile à n'importe quelle session gdb. -d -mdé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' -loption de bash le démarre en mode interactif et empêche gdb de quitter 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 écrire une entrée dans les écrans (afin de ne pas avoir à entrer dans chaque écran et à taper la même chose) en utilisant la -X stuffcommande screen . Une nouvelle ligne est requise à la fin de la commande. Ici, les écrans sont accessibles en -S "P$i"utilisant les noms donnés précédemment. L' -p 0option est critique, sinon la commande échoue par intermittence (selon que vous vous êtes déjà connecté ou non à 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 attacher à 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 gdb par analogie avec la section de code précédente.

user3788566
la source
3

Il y a aussi mon outil open-source, padb, qui vise à aider à la programmation parallèle. Je l'appelle un "Job Inspection Tool" car il fonctionne non seulement comme un débogueur peut également fonctionner, par exemple, comme un programme de type top parallèle. Exécutez en mode "Rapport complet", il vous montrera les traces de la pile de chaque processus au sein de votre application ainsi que les variables locales pour chaque fonction sur chaque rang (en supposant que vous avez compilé avec -g). Il vous montrera également les «files d'attente de messages MPI», c'est-à-dire la liste des envois et des réceptions en attente pour chaque rang dans le travail.

En plus d'afficher le rapport complet, il est également possible de dire à padb de zoomer sur des bits d'information individuels dans le travail, il existe une myriade d'options et d'éléments de configuration pour contrôler les informations affichées, voir la page Web pour plus de détails.

Padb


la source
3

La manière «standard» de déboguer les programmes MPI consiste à utiliser un débogueur qui prend en charge ce modèle d'exécution.

Sous UNIX, on dit que TotalView prend en charge MPI.

Communauté
la source
2

J'utilise cette petite méthode homebrewn pour attacher le débogueur aux processus 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 de leur attacher le débogueur et d'ajouter des points d'arrêt. Lorsque vous avez terminé, fournissez une entrée de caractère unique et vous êtes prêt à partir.

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 voudrez compiler cette fonction uniquement pour les versions de débogage.


la source
MPI a exigé la plupart des déclarations de débogage que j'ai jamais écrites pour du code même simple. (lol) Cela peut être très utile.
Troggy
3
Cette solution est similaire à la puce 6 ici ( open-mpi.org/faq/?category=debugging#serial-debuggers ). Vous pouvez améliorer un peu votre code en ajoutant gethostname(hostname, sizeof(hostname)); printf("PID %d on host %s ready for attach\n", getpid(), hostname);. Ensuite, vous vous attachez au processus en tapant rsh <hostname_from_print_statement>, et enfin gdb --pid=<PID_from_print_statement>.
Jeff
2

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

Akintayo
la source
2

Un moyen assez simple de déboguer un programme MPI.

Dans la fonction main (), ajoutez sleep (some_seconds)

Exécutez le programme comme d'habitude

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

Le programme démarre et s'endort.

Vous aurez donc quelques secondes pour trouver vos processus par ps, exécuter gdb et vous y attacher.

Si vous utilisez un éditeur comme QtCreator, vous pouvez utiliser

Déboguer-> Démarrer le débogage-> Attacher à l'application en cours d'exécution

et trouvez vos processus là-bas.

étranger
la source
1

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 avez affaire à un processus difficile à lancer à partir d'un débogueur.

Jim Hunziker
la source
Changé en un autre lien qui n'est pas cassé, a ajouté un commentaire.
Jim Hunziker
0

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 remonter les rangs MPI.

SMPI propose d'autres avantages à l'étude des applications MPI: clairevoyance (vous pouvez observer toutes les parties du système), reproductibilité (plusieurs exécutions conduisent exactement au même comportement sauf si vous le spécifiez), absence de heisenbugs (car la plateforme simulée est maintenue différente de l'hôte), etc.

Pour plus d'informations, consultez cette présentation ou cette réponse connexe .

Martin Quinson
la source