Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Déboguer un programme C avec gdb

Après cette leçon: Je peux trouver avec un débogueur la ligne où un programme plante.

Leçon de 60 min7 commandesLab de compilation et d’architecture : gcc, flex, bison et RISC-V dans une VM

gdb trouve la ligne qui fait planter

gdb est le débogueur du projet GNU : un programme qui lance votre programme et l’arrête pour vous montrer où il en est. Quand un programme C s’interrompt avec le message « Segmentation fault », gdb donne en quelques secondes la ligne fautive.

Une application de gestion de stock s’arrête sans autre explication chaque fois qu’on ajoute un article. Relire mille lignes prendrait la journée, alors que gdb indique la bonne ligne tout de suite.

À la fin de cette leçon

Remarque

Je peux trouver avec un débogueur la ligne où un programme plante.

Mots-clés

MotCe que ça veut direExemple
Débogueurun programme qui lance un autre programme et l’observegdb
Signalun message que le système envoie à un processusSIGSEGV
Erreur de segmentationl’arrêt d’un programme qui touche une mémoire interdite
Trace de la pilela liste des appels de fonction en cours
Point d’arrêtune ligne où le débogueur arrête le programme

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

L’option -g garde les noms et les lignes

gcc supprime les noms de variables et de fonctions du programme final. Avec l’option -g, il y garde une table qui relie chaque instruction à sa ligne de C.

Ajoutez aussi -O0 pour ne pas optimiser : les lignes suivent alors l’ordre du fichier. Le programme ci-dessous a une fonction ranger qui écrit à une adresse reçue. main lui donne NULL.

Un programme qui écrit à l’adresse NULL

Code: c
1  #include <stdio.h>
2  void ranger(int *cible, int valeur) {
3      *cible = valeur;
4  }
5  int main(void) {
6      int *p = NULL;
7      ranger(p, 6);
8      printf("fini\n");
9      return 0;
10 }

Aller dans le dossier de travail

Commande
mkdir -p ~/c && cd ~/c

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le programme dans un fichier

Commande
printf '#include <stdio.h>\nvoid ranger(int *cible, int valeur) {\n    *cible = valeur;\n}\nint main(void) {\n    int *p = NULL;\n    ranger(p, 6);\n    printf("fini\\n");\n    return 0;\n}\n' > plante.c

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Compiler avec les informations de débogage

Commande
gcc -g -O0 -o plante plante.c

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Compter les signaux reçus par le programme

Commande
gdb -batch -ex run ./plante 2>&1 | grep -c SIGSEGV

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

La trace montre qui a appelé qui

La commande bt (backtrace) liste les appels en cours, du plus récent au plus ancien. Le cadre numéro 0 est la fonction où le programme s’est arrêté. Le cadre suivant est l’appelant, et ainsi de suite jusqu’à main.

Chaque ligne donne la fonction, ses arguments et la ligne du fichier. Le Run affiche seulement la fin de chaque ligne, car le début contient une adresse qui change.

Lister les lignes de la trace

Commande
gdb -batch -ex run -ex bt ./plante 2>&1 | grep "^#" | grep -o "at plante.c:[0-9]*"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Exemple travaillé : lire la trace

CadreFonctionLigneCe que cela dit
0ranger3Le plantage se produit en écrivant *cible
1main7main a appelé ranger à cette ligne

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

print montre la valeur d’une variable

Une fois le programme arrêté, la commande print affiche une variable du cadre courant. Ici print cible donne 0x0, l’adresse zéro, et print valeur donne 6.

On a trouvé la cause : ranger écrit à l’adresse zéro. La ligne 7 de main lui a passé NULL.

Afficher les deux arguments au moment du plantage

Commande
gdb -batch -ex run -ex "print cible" -ex "print valeur" ./plante 2>&1 | grep '^\$'

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Un point d’arrêt arrête avant le plantage

On n’attend pas toujours le plantage. La commande break ranger pose un point d’arrêt à l’entrée de la fonction ranger. Le programme s’arrête là, avant d’écrire.

info args affiche alors les arguments reçus. On voit que cible est déjà 0x0 avant la ligne fautive.

Arrêter dans ranger et lire les arguments

Commande
gdb -batch -ex "break ranger" -ex run -ex "info args" ./plante 2>&1 | grep -E "^(cible|valeur)"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Exemple travaillé : reconnaître un plantage

SignalStatut du shellCause la plus courante
SIGSEGV139Une adresse invalide, NULL ou une pile pleine
SIGFPE136Une division entière par zéro
SIGABRT134Le programme se tue lui-même après une erreur détectée

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Erreur fréquente

Attention

Beaucoup de gens pensent que la ligne du plantage contient la faute. Elle montre seulement où le mal éclate. Dans notre exemple, la faute est à la ligne 7, où main passe NULL. Remontez la trace jusqu’à la ligne qui a donné la mauvaise valeur.

À propos de ce labo et de sa source: Lab de compilation et d’architecture : gcc, flex, bison et RISC-V dans une VM

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.