1. Déboguer le code#
Le débogueur (debugger) est le principal outil et aussi le plus puissant dont dispose un développeur. Il permet de suivre en temps réel l’exécution d’un programme avec navigation interactive dans ses sources: on a ainsi la possibilité de faire avancer le code ligne à ligne, voire instruction par instruction, d’inspecter le contenu des variables et bien plus encore…
Pour déboguer, on utilise en général un exécutable compilé avec les symboles de débogage (ou de debugging accessibles par l’option -g sur la plupart des compilateurs). C’est l’exécutable produit par la commande waf install_debug. C’est aussi celui qui est utilisé lorsque l’on choisit debug dans ASTK.
L’ajout de ces symboles (et principalement la suppression des optimisations, équivalente au niveau -O0) produit en général un exécutable différent de celui de production, avec parfois une précision différente dans les calculs flottants.
Plusieurs modes d’utilisation existent:
Post-mortem: quand il y a plantage, il est possible après une exécution, de remonter à l’endroit où celui-ci se produit grâce au “core file”
Interactif: on lance l’exécutable “sous” le débogueur
Couplage a posteriori : on connecte le débogueur à un programme en cours d’exécution
Le premier mode est utile lorsque l’on a besoin de connaître l’endroit exact d’un plantage mais que l’on ne veut pas ralentir outre-mesure l’exécution.
Le second mode est plus adapté lorsque l’on a déjà une idée du problème, puisque l’on va pouvoir aller examiner le contenu des objets et faire des vérifications élémentaires (on notera d’ailleurs que ce second cas est aussi utile lorsque l’on observe seulement un comportement anormal puisque l’on va pouvoir suivre l’état de certaines variables autrement que par des impressions).
Ce mode est celui de choix en général.
Enfin le dernier mode est utile lorsque que l’on se heurte à un problème de performances sur un calcul de taille conséquente, ou même lorsque qu’un calcul semble boucler indéfiniment. Il est alors difficile de réaliser un profiling [D1.06.01]: en connectant un débogueur à un programme en cours d’exécution, on peut examiner dans quelle routine il se trouve.
1.1. Post-mortem#
En cas de plantage d’un calcul Aster , un débogueur est automatiquement exécuté en mode post-mortem pour donner des indications sur la localisation du plantage dans le source.
Le premier réflexe en cas de plantage est donc de relancer son calcul en mode « debug » pour obtenir une localisation précise (numéro de ligne dans le source de l’instruction illicite). Il faut cependant veiller à ce que les erreurs fatales provoquent un abandon du calcul (c’est-à-dire avoir renseigné le mot-clé ERREUR=_F(ERREUR_F=”ABORT”) dans DEBUT). C’est aussi le cas pour les cas-tests, la présence du mot-clé CODE active ce comportement en cas d’erreur.
Remarque
Sur les plate-formesutilisant le compilateur Intel, on dispose directement du numéro de ligne en version optimisée(nodebug).
Si on veut aller plus loin, sans pour autant lancer son calcul sous le débogueur, on suivra les indications ci-dessous pour réaliser un débogage post-mortem .
Pour utiliser ce mode de débogage, il faut lancer son étude depuis ASTK en sélectionnant le mode «interactif» et en cliquant sur lancer “pre” par opposition à lancer “run”.
ASTK prépare alors dans /tmp l’arborescence nécessaire au lancement d’ Aster et indique dans le fichier d” output la ligne de commande à utiliser pour démarrer l’exécution après s’être placé au bon endroit.
On obtient le même fonctionnement en utilisant:
waf test_debug –name=zzzz000a –exectool=env
Comme avec ASTK, l’output indique les commandes à utiliser par la suite.
Exemple de l’output:
OK Environnement de Code_Aster préparé dans /tmp/interactif.16468-dsp0764418
<INFO> Pour lancer l'exécution, copiez/collez les lignes suivantes dans un shell bash/ksh :
cd /tmp/interactif.16468-dsp0764418
. /xxx/public/v13/tools/Code_aster_frontend-salomemeca/etc/codeaster/profile.sh
. /xxx/dev/codeaster/install/std/share/aster/profile.sh
. profile_tmp.sh
<INFO> Ligne de commande 1 :
cp fort.1.1 fort.1
/xxx/dev/codeaster/install/std/bin/asterd /xxx/dev/codeaster/install/std/lib/aster/Execution/E_SUPERV.py -commandes fort.1 --num_job=16468-dsp0764418 --mode=interactif --rep_outils=/xxx/public/v13/tools/Code_aster_frontend-salomemeca/outils --rep_mat=/xxx/dev/codeaster/install/std/share/aster/materiau --rep_dex=/xxx/dev/codeaster/install/std/share/aster/datg --numthreads=1 --tpmax=60 --memjeveux=75.75
Pour lancer l'exécution dans le debugger Python, vous pouvez utiliser :
cp fort.1.1 fort.1
/xxx/dev/codeaster/install/std/bin/asterd /usr/lib/python2.7/pdb.py /xxx/dev/codeaster/install/std/lib/aster/Execution/E_SUPERV.py -commandes fort.1 --num_job=16468-dsp0764418 --mode=interactif --rep_outils=/xxx/public/v13/tools/Code_aster_frontend-salomemeca/outils --rep_mat=/xxx/dev/codeaster/install/std/share/aster/materiau --rep_dex=/xxx/dev/codeaster/install/std/share/aster/datg --numthreads=1 --tpmax=60 --memjeveux=75.75
Pour le débogage “post-mortem” 3 étapes sont nécessaires:
Positionner l’environnement d’exécution (recopier depuis l’output les lignes ad-hoc, il y a plus ou moins de lignes selon l’environnement à positionner):
cd /tmp/interactif.16468-dsp0764418
. /xxx/public/v13/tools/Code_aster_frontend-salomemeca/etc/codeaster/profile.sh
. /xxx/dev/codeaster/install/std/share/aster/profile.sh
. profile_tmp.sh
Exécuter le code en interactif (recopier depuis l’output les lignes ad-hoc):
ulimit -c unlimited
cp fort.1.1 fort.1
/xxx/dev/codeaster/install/std/bin/asterd /xxx/dev/codeaster/install/std/lib/aster/Execution/E_SUPERV.py -commandes fort.1 --num_job=16468-dsp0764418 --mode=interactif --rep_outils=/xxx/public/v13/tools/Code_aster_frontend-salomemeca/outils --rep_mat=/xxx/dev/codeaster/install/std/share/aster/materiau --rep_dex=/xxx/dev/codeaster/install/std/share/aster/datg --numthreads=1 --tpmax=60 --memjeveux=75.75
La première commande permet de s’assurer que le corefile pourra être écrit sans limite de taille, sinon il est possible qu’il ne soit pas produit du tout.
Lancer le débogueur “ post-mortem ”:
Lorsque le calcul a planté, le système a produit un fichier que l’on appelle core file . Ce fichier qui contient l’état de la mémoire au moment du plantage permet l’analyse post-mortem . Attention: si le programme utilisait une grande quantité de mémoire au moment du plantage, ce fichier peut être volumineux.
Ce fichier est nommé core ou parfois core.NNN où NNN est un numéro.
L’analyse post-mortem est réalisée en lançant:
gdb /xxx/dev/codeaster/install/std/bin/asterd core
La première instruction exécutée dans le débogueur est en général wherepour savoir où s’est arrêté le programme!
Pour ce qui concerne la navigation une fois le débogueur lancé, on se reportera à la section suivante.
1.2. Débogage interactif#
1.2.1. Fonctionnement général#
On peut également déboguer le code de façon plus interactive en lançant l’exécution de Code_Aster sous le contrôle d’un débogueur. Un débogueur est un outil permettant l’avancée d’un programme ligne à ligne et l’examen de toutes les variables rencontrées dans le source.
Un tel outil rend de nombreux services et peut s’avérer extrêmement puissant. Les débogueurs couramment utilisés sont gdb (GNU, en mode texte, fonctionne partout)ou idb (Intel). En général, on utilisera une interface graphique qui est plus conviviale que ces simples outils en ligne de commande: on peut citer DDD, nemiver (interfaces à gdb ) ou bien IDB (interface Eclipse à idb ).
Concrètement, pour lancer l’exécution de Code_Aster sous le contrôle du débogueur, il faut utiliser le bouton “Lancer / dbg” de ASTK. La version exécutée est alors automatiquement la version debug .
L’interface graphique se lance, elle positionne immédiatement un premier point d’arrêt qui a pour effet de stopper le programme dans le main du programme python.c (le point d’entrée de Code_Aster ).
L’équivalent avec wafest obtenu en exécutant:
waf test_debug –name=zzzz000a –exectool=debugger
Si le débogueur ne se lance pas ou pour changer de débogueur, voir le paragraphe 1.5 Configurer le débogueur .
Avant de poursuivre l’exécution, on peut positionner d’autres points d’arrêts. Une fois ceci terminé, on continue l’exécution en appuyant sur « cont » ou « run » selon l’interface graphique utilisée.
Quelques commandes de gdb (syntaxe très proche pour idb)
Aide en ligne : «man gdb» ou bien dans gdb taper help, ou help subject
Touche « ENTER » reproduit l’action précédente
Où suis-je ? : where, up, down (permet de se déplacer dans la pile d’appels)
Spécifier un point d’arrêt dans une routine ou à une ligne donnée de la routine courante:
break nom_routine break num_ligne exemple : break op0199 ou break 87 ou b op0199exemple 2 : break 87 if (i .eq. 3) (on s’arrête à ligne 87 du fichier courant si la variable locale i vaut 3)Dans certains débogueurs, break est remplacé par stop in/stop at.
Continuer l’exécution jusqu’au point d’arrêt suivant : cont ou c
Lister les points d’arrêt : info breakpoints oustatus
Détruire un point d’arrêt : delete id
Désactiver un point d’arrêt: disable id
Avancer d’une instruction en restant dans la routine en cours : next ou n
Avancer d’une instruction en plongeant dans les routines appelées : step ou s
Afficher le contenu d’une variable : print nom_var ou p nom_var
Affiche une expression à chaque arrêt: display nom_var ou display expression
Remplir une variable: set nom_var=valeur
Lister le programme : list ou list num_ligne
Tuer le programme courant: kill , pour le redémarrer: run
Sauvegarder les points d’arrêts pour réutilisation: save breakpoints filename
Toutes ces commandes ont en général un équivalent graphique (bouton) ou bien un raccourci (par exemple dans idb ce sont les touches fonctions).
1.2.2. Débogage d’un programme parallèle avec Totalview#
Lorsque le calcul à déboguer est parallèle (MPI par exemple), il est possible d’utiliser un débogueur dédié comme Totalview qui se chargera de lancer le calcul parallèle et donnera accès à la position de chaque processus MPI.
La démarche pour lancer un calcul Aster sous Totalview diffère quelque peu du mode interactif séquentiel.
Les étapes à suivre sont les suivantes:
préparation d’un répertoire temporaire d’exécution avec le mode « pre »
positionnement de l’environnement et lancement de Totalview
paramétrage de Totalview
Pour la première étape, on se reportera au § 1.1 . On prendra garde à sélectionner la version parallèle et un seul processeur .
Dans la seconde étape, le positionnement de l’environnement et le lancement de Totalview se font à l’aide du fichier d” output produit par la première étape:
cd /tmp/interactif.16468-dsp0764418
. /xxx/public/v13/tools/Code_aster_frontend-salomemeca/etc/codeaster/profile.sh
. /xxx/dev/codeaster/install/std/share/aster/profile.sh
. profile_tmp.sh
cp fort.1.1 fort.1
totalview /xxx/dev/codeaster/install/std/bin/asterd
Le paramétrage de Totalview se fait de façon similaire aux deux images ci-dessous. On notera l’argument «-totalview» rajouté à la suite des arguments donnés dans le fichier d’output.
Le pilote MPI à utiliser peut différer selon les plateformes. Le menu «Tasks» permet de préciser le nombre de processus à lancer.
1.3. Débogage d’un programme en cours d’exécution#
1.3.1. Introduction#
Parce qu’il n’est parfois pas possible de réaliser un profiling , on souhaite interrompre un programme pour savoir où il passe le plus clair de son temps, ou tout simplement où il semble boucler. Il est bien évidemment possible de surcharger le code pour y placer des impressions mais cela demande de connaître a priori l’endroit du blocage ou bien de travailler par dichotomie ce qui peut devenir long (si le calcul en question est une étude).
On propose ici une technique très simple utilisant un débogueur (gdb).
1.3.2. Mise en application sur un exemple#
Considérons le calcul suivant:
[:ref:`desoza@aster3 ~ <desoza@aster3 ~>`]$ date && bjobs
Tue Jun 30 09:39:35 CEST 2009
JOBID USER STAT QUEUE FROM_HOST EXEC_HOST JOB_NAME SUBMIT_TIME
721238 desoza RUN q16G_24h aster2 aster10 *_gros_cas Jun 29 12:59
Il tourne depuis 20h40min. Si on regarde dans son répertoire d’exécution :
[:ref:`desoza@aster3 ~ <desoza@aster3 ~>`]$ ssh aster10
Last login: Tue Jun 30 08:51:25 2009 from aster3
cd[desoza@aster10 ~]$ cd /tmp/721238
[:ref:`desoza@aster10 721238 <desoza@aster10 721238>`]$ ls -ltrh
total 1.5G
-rwxrwxr-x 1 desoza astergrp 81M Jun 24 00:42 asteru
-rw-r--r-- 1 desoza astergrp 2 Jun 29 12:59 msg_job
-rw-r--r-- 1 desoza astergrp 12 Jun 29 12:59 FTMPDIR
-rw-r--r-- 1 desoza astergrp 0 Jun 29 12:59 fort.0
drwxr-xr-x 2 desoza astergrp 6 Jun 29 13:00 RESU_ENSIGHT
drwxr-xr-x 2 desoza astergrp 6 Jun 29 13:00 REPE_OUT
drwxr-xr-x 2 desoza astergrp 35 Jun 29 13:00 rep_coco
-rw-r--r-- 1 desoza astergrp 852 Jun 29 13:00 721238.export
-rwxr-xr-x 1 desoza astergrp 8.1M Jun 29 13:00 fort.20
-rw-r--r-- 1 desoza astergrp 0 Jun 29 13:00 err_cp
-rwxr-xr-x 1 desoza astergrp 7.9K Jun 29 13:00 fort.1
-rw-r--r-- 1 desoza astergrp 0 Jun 29 13:00 err
drwxr-xr-x 17 desoza astergrp 4.0K Jun 29 13:00 Eficas
-rw-r--r-- 1 desoza astergrp 6.5K Jun 29 13:00 config.txt
-rw-r--r-- 1 desoza astergrp 595 Jun 29 13:01 fort.9
-rwxr-xr-x 1 desoza astergrp 15M Jun 29 13:01 elem.1
-rw-r--r-- 1 desoza astergrp 8.3K Jun 29 13:03 fort.8
-rw-r--r-- 1 desoza astergrp 61K Jun 29 13:03 fort.6
-rw-r--r-- 1 desoza astergrp 245M Jun 29 13:04 glob.1
-rw-r--r-- 1 desoza astergrp 778K Jun 29 13:04 fort.15
-rw-r--r-- 1 desoza astergrp 1.1G Jun 29 13:04 vola.1
On constate que le calcul n’a rien écrit sur disque depuis 20h35min. De fait il n’a même pas fini une itération de Newton:
…
CARA_ELEM=springs,
MODELE=tipo,
CHAM_MATER=fisica,
);
—NOMBRE TOTAL DE NOEUDS : 80435 DONT :
25030 NOEUDS « LAGRANGE »
—NOMBRE TOTAL D’EQUATIONS : 191245
— TAILLE DU PROFIL MORSE DE LA TRIANGULAIRE SUPERIEURE (FORMAT SCR): 3740478
—DONC LA TAILLE DE LA MATRICE EST:
— EN SYMETRIQUE NNZ= 3740478
—EN NON SYMETRIQUE NNZ= 7289711
— NOMBRE TOTAL DE NOEUDS ESCLAVES : 5369
INSTANT DE CALCUL : 2.000000000E-02
CONTACT | ITERATIONS | RESIDU | RESIDU | OPTION | CONTACT | REAC GEOM || NEWTON | RELATIF | ABSOLU | ASSEMBLAGE | DISCRET | MAXIMUM |ITER. GEOM. | | RESI_GLOB_RELA | RESI_GLOB_MAXI | | ITERATIONS | |
Nous allons utiliser les fonctionnalités de gdb (ou tout autre debugger ) qui permettent d’interrompre un programme après s’y être attaché. Il va pour cela falloir connaître le PID de l’exécutable Aster qui tourne en boucle. On peut par exemple utiliser la commande suivante (ne fonctionne que si l’exécutable en question s’appelle “asteru” et qu’il n’y a qu’un seul job au nom de l’utilisateur sur le nœud de calcul):
[:ref:`desoza@aster10 721238 <desoza@aster10 721238>`]$ pgrep -u $USER asteru
2595
Lorsque le job que l’on veut ausculter est en parallèle, il est délicat de trouver le PID du processeur i. Une possibilité est d’utiliser l’outil “top”, d’afficher les colonnes PID et PPID (Parent PID) et de remonter le numéro du processus “asteru” à partir du numéro du répertoire temporaire de la forme “proc_pid” (ici pid est le PID du script shell de lancement du calcul Aster ). L’idée est que l’on cherche dans la colonne PPID, le numéro pid, on trouve alors dans la colonne PID un nouveau numéro que l’on cherche à nouveau dans la colonne PPID, et ainsi de suite jusqu’à arriver au processus “./asteru …”.
Nous exécutons ensuite la ligne suivante après s’être placé dans le répertoire temporaire d’exécution:
[:ref:`desoza@aster10 721238 <desoza@aster10 721238>`]$ gdb ./asteru 2595
Cela donne la chose suivante:
GNU gdb Bull Linux (6.3.0.0-1.132.EL4.b.2.Bull)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "ia64-redhat-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
Attaching to program: /tmp/721238/asteru, process 2595
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xa000000000000000
`shared object read from target memory' has disappeared; keeping its symbols.
Reading symbols from /opt/intel/cmkl/9.1.023/lib/64/libmkl.so...done.
Loaded symbols for /opt/intel/cmkl/9.1.023/lib/64/libmkl.so
…
Loaded symbols for /aster/local/Python-2.4.5/lib/python2.4/lib-dynload/_random.so
Reading symbols from /aster/local/Python-2.4.5/lib/python2.4/lib-dynload/md5.so...done.
Loaded symbols for /aster/local/Python-2.4.5/lib/python2.4/lib-dynload/md5.so
Reading symbols from /opt/intel/cmkl/9.1.023/lib/64/libmkl_i2p.so...done.
Loaded symbols for /opt/intel/cmkl/9.1.023/lib/64/libmkl_i2p.so
0x4000000000358a20 in tldlr8_??unw ()
Nous avons ainsi interrompu le programme (il n’est plus dans l’état ‘running’ R mais dans l’état ‘stopped’ T) comme le montre l’outil top .
PID PPID USER VIRT SWAP RES CODE DATA P S %CPU %MEM TIME+ nFLT COMMAND
2595 2546 desoza 7351m 127m 7.1g 64m 7.0g 2 T 0.0 5.5 1245:46 6 ./asteru
On peut désormais faire comme dans un debugger , et demander où on est pour savoir ce qui se passe:
(gdb) where
#0 0x4000000000358a20 in tldlr8_??unw ()
#1 0x4000000000355ee0 in tldlg3_??unw ()
#2 0x4000000000357860 in tldlgg_??unw ()
#3 0x400000000256b2b0 in algoco_??unw ()
#4 0x40000000023e2f10 in cfalgo_??unw ()
#5 0x40000000022575c0 in nmcofr_??unw ()
#6 0x4000000001c48210 in nmcoun_??unw ()
#7 0x4000000001046480 in nmdepl_??unw ()
#8 0x40000000004044e0 in op0070_??unw ()
....
Comme on utilise une version “nodebug” on n’a pas accès au source (et aux numéros de ligne), il faudrait pour cela une version compilée avec l’option “-g”. Néanmoins on peut déterminer ce qui se passe. Ici il s’agit d’un calcul de contact qui mouline dans tldlr8 la routine qui factorise le complément de Schur du contact. Comme il y a plus de 4000 nœuds de contacts actifs, cette factorisation est très longue (mais c’est normal).
Lorsque l’on a fini dans gdb, on peut se détacher du programme puis quitter, l’exécution reprend alors son cours.
(gdb) detach
Detaching from program: /tmp/721238/asteru, process 2595
(gdb) q
1.4. Déboguer le source Python#
Lorsque le bug concerne les sources Python, il faut utiliser le débogueur Python. Pour cela, on utilise encore ASTK / Lancer “pre”. Après avoir fait quelque chose comme:
cd /tmp/interactif.12219
export ASTER_VERSION=NEW9
. /opt/aster/ASTK/ASTK_SERV/conf/aster_profile.sh
. /opt/aster/NEW9/profile.sh
On peut lancer le code sous le contrôle du débogueur Python:
To start execution in the Python debugger you could type :
./asterd /usr/lib/python2.7/pdb.py Python/Execution/E_SUPERV.py -eficas_path \
./Python -commandes fort.1.1 -rep none -num_job 12219 -mode interactif \
-rep_outils /opt/aster/outils -rep_mat /opt/aster/NEW9/materiau \
-rep_dex /opt/aster/NEW9/datg -tpmax 120 -memjeveux 16.0
Pour plus de détails, voir par exemple: http://docs.python.org/library/pdb.html
1.5. Configurer le débogueur#
La ligne de commande utilisée pour exécuter le débogueur (en mode interactif ou post-mortem ) est définie dans les fichiers de configuration de ASTK.
Pour voir la ligne de commande utilisée en interactif, faire:
as_run –showme param cmd_dbg
Pour voir la ligne de commande utilisée en post-mortem , faire:
as_run –showme param cmd_post
En général, ces commandes sont définies dans le fichier de configuration du serveur.
Vous pouvez modifier cette valeur en écrivant la ligne de commande de votre choix dans $HOME/.astkrc/prefs.
Attention
Le fichier àmodifier est $HOME/.astkrc_salomemeca_VERSION/prefssi ASTKest issu de Salome-Meca.
Exemple pour gdb , copier/coller la ligne:
echo “cmd_dbg : xterm -e gdb –command=@D @E @C” >> ~/.astkrc/prefs
Exemple pour nemiver , copier/coller la ligne:
echo “cmd_dbg : nemiver@E @a” >> ~/.astkrc/prefs
Exemple pour idb , copier/coller la ligne:
echo “cmd_dbg : /opt/intel/Compiler/11.1/064/bin/ia32/idb -gui -gdb -command @D -exec @E” >> ~/.astkrc/prefs
Les codes @E, @C, … sont remplacés par ASTK au moment du lancement:
@E: nom de l’exécutable Code_Aster,
@a: les arguments passés à l’exécutable Code_Aster,
@C: nom du corefile,
@D: nom du fichier de commandes pour le débogueur (qui contient where + quit),
@d: le texte correspondant aux commandes passées au débogueur,