Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Débogage d'un programme

Introduction

Déboguer un programme signifie rechercher des erreurs afin de les corriger. Plusieurs types d’erreurs existent. Les bogues de conception sont des bogues résultant d’erreurs dans l’élaboration d’un algorithme. Ce sont les plus faciles à identifier car le programme n’est pas capable d’effectuer ce que nous lui demandons en l’état. Nous pouvons même les identifier pendant la programmation car nous ne disposons pas des données à traiter. Les bogues de conception sont les plus longs à résoudre. Il ne suffit pas de modifier quelques lignes de code. Tout l’algorithme est à repenser et l’implémentation à refaire. Les bogues venant d’une maladresse du programmeur, comme une faute de frappe (telle que if(a=b) au lieu de if(a==b)), l’oubli d’initialiser une variable ou même l’utilisation d’une fonction au lieu d’une autre (strcpy() au lieu de strncpy() en oubliant que le contrôle de la longueur de la chaîne de caractères est nécessaire par exemple), sont bien plus difficiles à repérer, mais une fois identifiés, rapides à corriger. Le chapitre "Gestion des erreurs" propose d’ailleurs quelques recettes pour éviter de commettre certaines de ces erreurs.

Le débogage consiste donc à détecter les anomalies...

Déboguer proprement avec printf()

Problème

Votre programme s’arrête intempestivement et vous ne souhaitez pas mettre en œuvre des outils de débogage car vous pensez que déboguer avec printf() ira plus vite et mieux.

Solution

Profitez des macros prédéfinies __FILE__ et __LINE__ dans vos lignes avec printf() et procédez de manière dichotomique pour trouver la ligne où votre programme renvoie un résultat imprévu.

Discussion

Si déboguer avec printf() est généralement efficace, la vitesse et l’efficacité dépendront de chacun. Certains préfèrent utiliser le débogueur gdb, qui nécessite un apprentissage préalable. D’autres trouvent plus pratique la simplicité d’un printf(). D’autres outils existent par ailleurs, et la seule chose que nous pouvons dire est de connaître plusieurs moyens de déboguer pour passer à l’autre quand l’un montre ses faiblesses.

La ligne suivante affiche le nom du fichier ainsi que le numéro de la ligne sur laquelle se trouve l’appel à printf() :


printf("%s :%d\n", __FILE__, __LINE__);
 

Si vous envisagez d’écrire les données dans un fichier au lieu de les afficher, utilisez fprintf(fd, ... au lieu de print(... Vous pouvez d’ailleurs utiliser aussi dès le début fprintf(stdout...

Déterminer où le programme stoppe

Problème

Un programme s’arrête à un endroit inattendu et vous souhaitez savoir précisément, à quelle instruction C, le programme a pris fin.

Solution

Comme dans la recette précédente, utilisez printf() ou un débogueur. Avec un débogueur, suivez le code pas à pas, au fur et à mesure de son exécution, ou effectuez une analyse post mortem de son exécution.

Discussion

Si l’arrêt du programme est dû à un débordement de tampon, il faut savoir que le programme ne stoppe pas à l’endroit où se trouve le bogue, mais plus loin dans la plupart des cas. En effet, le principe du débordement de tampon est de corrompre la mémoire, modifiant de manière aléatoire d’autres données, ce qui amène le programme à prendre fin à un endroit où les données ne sont plus valides. Quand il s’agit d’un bogue de débordement de tampon, autant il n’est pas évident de trouver l’endroit où la mémoire est corrompue, autant il est assez facile d’identifier la nature du bogue. En général, chaque méthode de débogage amènera le programme à s’arrêter à un endroit différent, même si vous lui donnez des données similaires à traiter de la même manière.

Pour trouver l’endroit où la mémoire est corrompue, une bonne méthode consiste à utiliser printf() comme vu dans la recette précédente, mais en tirant profit de la bibliothèque Electric Fence. Vous pourriez utiliser gdb et Electric Fence en même temps, mais ce dernier agit aussi sur le débogueur et le ralentirait de manière significative. Sans Electric Fence, la recherche de l’origine de la corruption de la mémoire est ardue même avec gdb comme nous allons le voir.

Pour comprendre le fonctionnement, revenons sur quelques notions de système. Lorsque le programme demande au système d’allouer de la mémoire, ce dernier la lui attribue dans un certain nombre de pages mémoire de taille fixe (4 ko en général sur Unix). Par conséquent, lorsque le programme déborde...

Afficher le contenu d’une variable

Problème

Vous voulez afficher le contenu d’une variable quel que soit son type.

Solution

Le typage des variables en C ne permet pas de détecter le type d’une variable. Nous ne présenterons donc ici que quelques principes pour afficher des variables de type connu.

Discussion

Le premier principe d’affichage du contenu d’une variable est la simplicité. Cela doit se faire en une ligne, la plus courte possible. Ainsi, s’il s’agit d’un entier ou d’une chaîne de caractères, utilisez printf() sans hésiter. S’il s’agit d’une structure complexe, créez une fonction d’affichage ; appeler cette fonction se fera en une ligne.

Afficher le contenu d’une variable se fait pendant quelques temps, le temps du développement du code nécessitant cette variable, avant d’être supprimé ou utilisé uniquement en mode de débogage. Le code d’affichage d’une variable doit donc être court à taper, et ne pas gêner la lecture du code.

À moins de vouloir afficher une variable pour effectuer un test unique, il est préférable de programmer proprement, même pour effectuer du débogage, et ici, d’afficher non seulement le contenu de la variable, mais aussi son nom. Vous pouvez le faire de deux manières différentes...

Retarder l’arrêt intempestif d’un programme pour sauvegarder des données

Problème

Votre programme s’arrête, ce qui vous fait perdre toutes vos données.

Solution

Utilisez setjmp() et longjmp() pour revenir à une situation stable afin de sauvegarder vos données. Ne supprimez jamais l’ancienne sauvegarde avant d’en avoir faite une nouvelle.

Discussion

Avant de chercher à anticiper un arrêt intempestif du programme, il est possible de programmer une sauvegarde automatique toutes les N minutes, N étant défini par l’utilisateur. Pour cela, le mieux est d’utiliser les signaux, en particulier le signal SIGALRM et la fonction alarm() comme décrit dans la recette "Envoyer un signal" (chapitre "Signaux"). Cependant, il est préférable de nommer différemment cette sauvegarde passive régulière pour ne pas risquer de perdre les données sauvegardées activement par l’utilisateur.

Les fonctions setjmp() et longjmp(), qui sont bien standard (POSIX), sont à utiliser avec parcimonie. Elles consistent à mettre une sorte de point de contrôle afin de pouvoir y revenir (comme avec l’instruction goto) mais en effectuant un véritable retour en arrière, comme si la suite du programme ne s’était jamais exécutée. Pour cette raison, le débogage peut être rendu très difficile car en cas de manifestation d’un bogue, nous pouvons nous replacer dans une situation antérieure, masquant ainsi les effets du bogue. Cette manière de faire doit donc être rendue optionnelle pour le programmeur, activée lors des phases de test avant livraison aux utilisateurs et désactivée lors des phases de programmation et de débogage.

Le principe est le suivant. Il faut disposer...