Pour contrôler l'exécution d'un programme, on utilise des points d'arrêt. On donne à GDB une liste d'endroits où il doit faire une pause dans l'exécution. Cela permet par exemple de vérifier l'état d'une variable ou le contenu d'un registre. Comme nous l'avons vu plus tôt, il est possible de placer automatiquement un point d'arrêt au point d'entrée du programme, avec la commande "start". Cependant, si le programme comporte plusieurs milliers de lignes, nous n'allons pas faire tourner tout le programme au "pas à pas" (une instruction à la fois, nous verrons plus tard comment faire), nous allons plutôt placer des points d'arrêt aux endroits qui nous intéressent.
Il y a trois façons de préciser un point d'arrêt :
- indiquer un endroit précis (fonction, ligne, fichier) ;
- lors de la modification d'une variable (ou à sa lecture) ;
- lorsqu'un certain événement se produit (création d'un nouveau processus, chargement d'une librairie, throw ou catch d'une exception, etc.).
Respectivement, ces trois types de points d'arrêt se nomment
breakpoints,
watchpoints (points de surveillance), et
catchpoints (points d'interception).
Lorsque GDB interrompt le programme, il affiche la raison de l'arrêt, la ligne de l'instruction suivante (la prochaine qui sera exécutée) et l'invite de commande :
Code : Console | (gdb) r
Starting program: main
Breakpoint 3, main () at sample1.cpp:9
9 a = a / b;
(gdb) |
(gdb) info break [breaknum]
Affiche la liste des points d'arrêt. Si vous précisez un numéro (et non une position), vous ne verrez que les infos qui concernent ce point d'arrêt.
Les breakpoints
GDB s'arrête juste avant l'instruction qui suit le breakpoint. Cela signifie par exemple que si vous demandez à GDB de s'arrêter à la ligne 17, il va s'arrêter juste au début de la ligne, sans l'exécuter.
(gdb) break [position]
Si on ne donne pas de position, GDB s'arrêtera à la prochaine instruction qui sera exécutée. Sinon on peut spécifier l'endroit où le programme sera arrêté :
- en donnant un numéro de ligne (break 123) du fichier courant ;
- en donnant un décalage en lignes par rapport à la prochaine instruction (break +5 #s'arrêter 5 lignes après la prochaine instruction) ;
- en donnant un nom de fichier et un numéro de ligne (break monfichier.cpp:123) ;
- en donnant un nom de fonction (break ma_fonction) ;
- en donnant un nom de fichier et de fonction (break monfichier.cpp:ma_fonction) ;
- en donnant une adresse (break *adresse #adresse peut être un nombre, une variable, une expression...).
Exemple : "(gdb) break *(0x80F523 + maVariable).
Remarque : en précisant une fonction, plusieurs breakpoints peuvent êtres placés, par exemple avec un programme écrit en C++ où on aurait surchargé la fonction.
(gdb) break [position] if condition
On peut aussi préciser une condition d'arrêt. GDB va alors évaluer (calculer) cette condition juste avant de s'arrêter, pour savoir s'il doit ou non continuer. Exemple :
Code : Console | (gdb) break 123 if a+b < 0 |
GDB arrêtera alors l'exécution du programme à la ligne 123 seulement si a+b est négatif. La condition est écrite avec la syntaxe du langage utilisé pour le programme.
Parfois, vous ne souhaitez vous arrêter qu'une fois (par exemple, seulement lors du premier appel à une fonction). Il suffit alors d'utiliser "tbreak" à la place de "break", en l'utilisant exactement de la même manière.
(gdb) clear [position]
Supprime le breakpoint correspondant à la position indiquée. Comme précédemment, ne pas indiquer de position suppose qu'il s'agit de l'instruction suivante.
Ajouter un watchpoint
(gdb) watch variable
Indique à GDB de s'arrêter dès que la variable est modifiée. L'instruction qu'il vous affiche correspond donc à celle qui sera exécutée juste après (et non pas à celle qui a modifié la variable, puisqu'elle a déjà été exécutée).
Vous pouvez également utiliser une expression, par exemple "a*b", pour indiquer à GDB de ne s'arrêter que lorsque (a*b) aura été modifié. Mais il faut bien noter que pour surveiller une expression, GDB est obligé de surveiller chaque variable de cette expression et d'évaluer le résultat.
(gdb) rwatch variable
Indique à GDB de s'arrêter dès que la variable est accédée par le programme (r pour "read").
(gdb) awatch variable
Indique à GDB de s'arrêter aussi bien lorsque la variable est modifiée que lorsqu'elle est lue.
Ajouter un catchpoint
Le plus simple est que je vous donne l'aide traduite. Si vous ne savez pas à quoi correspond un événement, c'est que vous n'en avez pas besoin pour l'instant.
Code : Console | Intercepter un signal :
catch signal - Tous les signaux
catch signal <signame> - Seulement le signal <signame>
Intercepter une exception :
catch throw - Toutes les exceptions lorsqu'elles sont lancées (throw...)
catch throw <exceptname> - Seulement l'exception <exceptname> lorsqu'elle est lancée (throw...)
catch catch - Toutes les exceptions lorsqu'elles sont catchées
catch catch <exceptname> - Seulement l'exception <exceptname> lorsqu'elle est catchée
Intercepter les événements de threads :
catch thread_start - Lors de la création d'un nouveau thread
catch thread_exit - Lors de la fermeture d'un thread
catch thread_join - Lors d'un join de threads
Intercepter les événements de processus :
catch start - Lors de la création d'un nouveau procesus
catch exit - Lors de la fermeture d'un processus
catch fork - Lors d'un appel à fork()
catch vfork - Lors d'un appel à vfork()
catch exec - Lors d'un appel à exec()
Intercepter les chargements/déchargements de librairies dynamiques :
catch load - Lors du chargement de n'importe quelle librairie
catch load <libname> - Lors du chargement de la librairie <libname>
catch unload - Lors du déchargement de n'importe quelle librairie
catch unload <libname> - Lors du déchargement de la librairie <libname> |
Tout comme pour break, on peut vouloir placer un catchpoint qui n'aura effet qu'une seule fois. Il suffit d'utiliser "tcatch" au lieu de "catch".
Supprimer un point d'arrêt
Dans les commandes suivantes, si [breaknum] n'est pas spécifié, cela signifie "tous les points d'arrêt".
(gdb) delete [breaknum]
Supprime le point d'arrêt numéro "breaknum".
(gdb) disable [breaknum]
Désactive un point d'arrêt, mais ne le supprime pas (GDB ne s'arrêtera pas, mais vous pourrez toujours le réactiver par la suite).
(gdb) enable [breaknum]
Active un point d'arrêt.
(gdb) enable [breaknum] once
Active un point d'arrêt pour un seul passage. Dès qu'il est atteint une fois, il est désactivé automatiquement.
(gdb) enable [breaknum] delete
Active un point d'arrêt pour un seul passage. Dès qu'il est atteint une fois, il est automatiquement supprimé.
Continuer l'exécution
Lorsque le programme est interrompu, GDB vous passe l'invite de commande. Vous pouvez y entrer toutes les commandes que vous souhaitez. Une fois que vous souhaitez continuer l'exécution du programme, il suffit d'entrer la commande "continue" (abr. "c").
Vous pouvez également utiliser le mode "pas à pas" (step-by-step en anglais). Il y a plusieurs façons de faire, dont les plus utiles sont :
(gdb) step [count]
Exécute les [count] prochaines lignes de code. Si count n'est pas spécifié, il vaut 1. Si l'instruction est un appel de fonction, step va passer à l'intérieur de cette fonction. Attention cependant, step ne peut aller que dans les fichiers qui ont des informations de débogage (il a besoin des numéros de ligne).
(gdb) next [count]
Comme "step", exécute les [count] prochaines lignes de code. La différence étant que si l'instruction est un appel de fonction, next ne s'arrêtera pas à l'intérieur de celle-ci, mais bien à la ligne suivant l'appel de la fonction.
(gdb) until
Exécute toutes les instructions jusqu'à ce que le programme arrive à une ligne qui suit la ligne actuelle. C'est utile dans le cas de boucles. Par exemple, la boucle suivante en C++ :
Code : C++1
2 | for (int i=0;i<1000;++i)
cout << i;
|
Avec step ou next, GDB va s'arrêter 1000 fois sur ces deux lignes (en fait, 1001 fois pour la première, la 1001
e fois la seconde n'est plus exécutée car i est alors égal à 1000 , et 1000 /< 1000

).
Avec until, GDB ne va s'y arrêter que la première fois, puis il s'arrêtera seulement à la prochaine ligne.