Le dévermineur fourni avec FreeBSD est appelé
gdb
(GNU
debugger). Vous pouvez le démarrer en tapant
%
gdb nomprog
bien que la plupart des gens préfèrent le démarrer au sein d'Emacs. Vous pouvez faire cela avec:
M-x gdb RET nomprog RET
Utiliser un dévermineur vous permet d'exécuter le programme
dans des circonstances plus contrôlées. Typiquement, vous pouvez
exécuter le programme ligne à ligne, inspecter la valeur des
variables, changer cette dernière, dire au dévermineur d'exécuter jusqu'à
un certain point puis de s'arrêter etc... Vous pouvez même vous brancher
sur un programme en fonctionnement, ou charger un fichier core
pour enquêter sur le plantage du programme. Il est même possible de
déverminer le noyau, quoique ce soit un peu plus rusé que de
déverminer des applications utilisateur dont nous discuterons
dans cette section.
gdb
dispose d'une assez bonne aide en ligne
comme d'un ensemble de pages d'info, aussi cette section va se concentrer sur
quelques commandes basiques.
Finalement, si vous trouvez son interface texte non fonctionnelle, il y a une interface graphique pour celui-ci, xxgdb, dans la collection des logiciels portés.
Cette section a pour but d'être une introduction
à l'utilisation de gdb
et ne couvre pas les sujets
très spécialisés comme le déverminage du noyau.
Vous devrez avoir compilé le programme avec l'option -g
pour avoir la meilleure utilisation de gdb
. Il fonctionnera sans
mais vous ne verrez que le nom de la fonction dans laquelle vous vous trouvez plutôt
que son code source. Si vous voyez une ligne comme:
… (no debugging symbols found) …
quand gdb
démarre, vous saurez que le programme
n'a pas été compilé avec l'option -g
.
A l'invite de gdb
, tapez
break main
. Cela dira au dévermineur
de passer le code préliminaire d'initialisation du programme
et de démarrer au début de votre code. Maintenant tapez
run
pour démarrer le programme—cela va
démarrer au début du code d'initialisation et ensuite s'arrêtera
lors de l'appel à main()
.
(Si vous vous êtes toujours demandé où main()
était appelé, maintenant vous le savez !).
Vous pouvez maintenant vous déplacer dans le programme ligne par ligne en
pressant n
. Si vous arrivez à l'appel d'une fonction,
vous pouvez entrer dans celle-ci en appuyant sur s
. Une fois
que vous êtes dans l'appel de la fonction, vous pouvez retourner dans le code
appelant en appuyant sur f
. Vous pouvez aussi utiliser
up
et down
pour avoir une vue rapide de l'appelant.
Voici un exemple simple de comment détecter une erreur dans un programme avec
gdb
. Voici notre programme (avec une erreur délibérée):
#include <stdio.h> int bazz(int anint); main() { int i; printf("C'est mon programme\n"); bazz(i); return 0; } int bazz(int anint) { printf("Vous m'avez fourni %d\n", anint); return anint; }
Le programme met i à 5
et le passe à une
fonction bazz()
qui imprime le nombre que nous lui avons donné.
Puis nous compilons et exécutons le programme obtenu
%
cc -g -o temp temp.c
%
./temp
C'est mon programme Vous m'avez fourni 4231
Ce n'était pas ce que nous attendions ! Il est temps de voir ce qui se passe !
%
gdb temp
GDB is free software and you are welcome to 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. GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc. (gdb)break main
passe le code d'initialisation Breakpoint 1 at 0x160f: file temp.c, line 9. gdb met un point d'arrêt sur main() (gdb)run
Exécute jusqu'à main() Starting program: /home/james/tmp/temp Le programme démarre Breakpoint 1, main () at temp.c:9 gdb s'arrête à main() (gdb)n
Va à la ligne suivante C'est mon programme Le programme écrit (gdb)s
entre dans bazz() bazz (anint=4231) at temp.c:17 gdb montre la pile (gdb)
Arrêtons-nous une minute! Comment anint a eu la valeur
4231
? Ne l'avons-nous pas mis à 5
dans main()
? Remontons dans main()
et regardons.
(gdb)up
Remonte la pile des appels #1 0x1625 in main () at temp.c:11 gdb montre la pile (gdb)p i
Montre la valeur de i $1 = 4231 gdb montre 4231
Oh ! En regardant dans le code, nous avons oublié d'initialiser i. Nous aurions dû mettre
… main() { int i; i = 5; printf("C'est mon programme\n"); …
mais nous n'avions pas mis la ligne i=5;
. Comme
nous n'avons pas initialisé i, il a pris le nombre se trouvant
dans la zone de mémoire quand le programme a démarré,
ce qui dans ce cas était 4231
.
gdb
montre la pile chaque fois que nous entrons ou sortons
d'une fonction, même si nous avons utilisé up
et
down
pour nous déplacer dans la pile des appels.
Cela montre le nom de la fonction et les valeurs de ses arguments, ce qui nous aide
à garder une trace d'où nous sommes et de ce qui se passe.
(La pile est une zone de stockage où le programme stocke les informations
sur les arguments passés aux fonctions et où il doit aller
quand il revient d'une fonction).
Un fichier core
est basiquement un fichier qui contient
l'état complet du processus quand il s'est planté.
Dans « le bon vieux temps », les programmeurs devait imprimer des
listings en hexadécimal de fichiers core
et transpirer
sur leur manuels de code machine, mais la vie est maintenant un peu plus facile.
Par chance, sous FreeBSD et les autres systèmes 4.4BSD, un fichier core
est appelé nomprog.core
plutôt que juste core
, pour mieux savoir à quel
programme appartient un fichier core
.
Pour examiner un fichier core
, démarrez gdb
de façon habituel. Plutôt que de taper break
ou
run
, tapez
(gdb) core nomprog.core
Si vous n'êtes pas dans le même répertoire que le fichier
core
, vous devrez faire dir /path/to/core/file
d'abord.
Vous devriez voir quelque chose comme cela:
%
gdb a.out
GDB is free software and you are welcome to 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. GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc. (gdb)core a.out.core
Core was generated by `a.out'. Program terminated with signal 11, Segmentation fault. Cannot access memory at address 0x7020796d. #0 0x164a in bazz (anint=0x5) at temp.c:17 (gdb)
Dans ce cas, le programme a été appelé a.out
,
aussi le fichier core
s'appelle a.out.core
.
Nous pouvons voir que le programme s'est planté car il a essayé
d'accèder à une zone dans la mémoire qui n'était pas
disponible dans la fonction appelée bazz
.
Quelquefois il est utile de pouvoir voir comment une fonction a été
appelée car le problème peut avoir eu lieu bien avant
dans la pile des appels dans un programme complexe. La commande bt
demande à gdb
d'afficher une trace inverse de la pile
des appels:
(gdb) bt
#0 0x164a in bazz (anint=0x5) at temp.c:17
#1 0xefbfd888 in end ()
#2 0x162c in main () at temp.c:11
(gdb)
La fonction end()
est appelée lorsque le programme se plante;
dans ce cas, la fonction bazz()
a été appelée
main()
.
Une des plus belles caractéristiques de gdb
est qu'il peut se brancher sur un programme qui s'exécute déjà.
Bien sûr, cela suppose que vous ayez les privilèges suffisants pour
le faire. Un problème habituel est quand vous vous déplacez dans
un programme qui se dédouble et que vous voulez tracer le programme fils
cependant le dévermineur ne vous laissera seulement tracer le père.
Ce que vous devez faire est de démarrer un autre gdb
,
utiliser ps
pour trouver l'ID du processus fils et faire
(gdb) attach identifiant_processus
dans gdb
, et déverminer ensuite comme d'habitude.
« C'est tout simple, » pensez-vous certainement,«
mais pendant le temps que je faisais ça, le processus fils
sera déjà parti loin ». Ne vous en faites pas,
noble lecteur, voici comment faire (avec l'appui des pages d'info
de gdb
):
… if ((pid = fork()) < 0) /* _Toujours_ verifier cela */ error(); else if (pid == 0) { /* le fils */ int PauseMode = 1; while (PauseMode) sleep(10); /* Attendre jusqu'a ce que quelqu'un se brache sur nous */ … } else { /* le pere */ …
Maintenant tout ce que nous avons à faire est de nous brancher sur le processus fils,
de mettre PauseMode à 0
et d'attendre
que l'appel à la fonction sleep()
retourne !
Ce document, ainsi que d'autres peut être téléchargé sur ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/
Pour toutes questions à propos de FreeBSD, lisez la
documentation avant de contacter
<questions@FreeBSD.org>.
Pour les questions sur cette documentation, contactez
<doc@FreeBSD.org>.