Skip to topic | Skip to bottom
Home
Linfo
Linfo.GLEnvtProg0708TP6r1.4 - 15 Mar 2008 - 20:45 - NicolasNobelistopic end

Start of topic | Skip to actions

TP 6 - Debugging de programmes avec DDD

Licence 3 Informatique - 2007-2008

PhilippeCollet, NicolasNobelis

URL de cette page : http://deptinfo.unice.fr/twiki/bin/view/Linfo/GLEnvtProg0708TP6
URL de l'enseignement : http://deptinfo.unice.fr/twiki/bin/view/Linfo/GLEnvtProg0708

Avant propos

  • Créez votre répertoire de séance pour le TP et placez-vous dedans.
  • Récupérez et décompressez l'archive Fourniture6.tbz. Vous obtenez alors quatre répertoires : 1.trier, 2.file1, 3.file1 et 4.file2 pour les questions suivantes.

1. Démonstration du fonctionnement de base du debugger ddd

Placez-vous dans le répertoire 1.trier et construisez l’exécutable trier avec le Makefile. Le programme trier prend des nombres entiers en argument, séparés par des espaces. Exécutez les expériences suivantes (vous pouvez avoir des résultats différents sur votre machine, car ce programme est buggé) :

~>trier 5 9 3
après tri:
3 5 9
ce qui a l’air de marcher... Mais avec d’autres jeux d’essai, en mettant 4 ou 5 arguments dont certains négatifs ou au contraire très grands (1 million), vous constaterez l’apparition de valeurs intruses dans les résultats. En suivant la démo faîte par votre enseignant sur les fonctionnalités de base de ddd, vous trouverez la cause du problème en tapant les mêmes commandes que lui sur votre machine.
Lancez en parallèle la commande ddd trier & et suivez les instructions de votre enseignant. Cette démo est celle indiquée par Andreas Zeller dans son manuel de ddd.

2. Recherche d’une cause d’avortement de programme (abort)

Placez-vous dans le répertoire 2.file1. Ce répertoire contient le programme de test tfile1.c du type abstrait file1.c du TP précédent, mais avec un bug qui produit une erreur d’adressage détectée par le matériel et indiquée au noyau de votre système par une interruption matérielle qui prévient alors votre programme par un signal (SIGSEGV, Segmentation Fault).
Si votre programme n’a pas de traitant (handler) pour ce signal, c’est le traitement par défaut qui est appliqué. Celui-ci est d’écrire, sur le répertoire courant, une image de la mémoire du processus fautif dans un fichier core (appelé aussi dump) et un message d’avertissement du genre Segmentation fault (core dumped).
L’absence des mots (core dumped) dans le message précédent indique qu’aucun fichier core n’a été produit, ce que vous pouvez vérifier avec la commande ls. La raison est que votre shell est dans un état où l’écriture des fichiers core est interdite. Tapez la commande ulimit -c. Elle affiche la taille maximum autorisée pour un fichier core. Si elle est nulle, cela interdit son écriture. Tapez alors la commande ulimit -c 1000000 pour autoriser l’écriture de fichiers core jusqu’à un million d’octets et rééxécutez le programme tfile1. Cette fois, vous obtenez le message segmentation fault (core dumped) et constatez l’apparition d’un fichier core¹.
Ce fichier est analysable par un debugger, comme adb, gdb (debuggers CLI - Command Line Interface) ou dbx, ddd (debuggers graphiques). ddd utilise gdb par défaut pour analyser les fichiers core. Lancez ddd en indiquant le fichier binaire du programme à analyser et le fichier core produit :

# Sous Linux , tapez la commande suivante avec complétion pour faire apparaître XXX :
~> ddd tfile1 core.XXX

La fenêtre du bas indique l’adresse du plantage dans le code source. Avec les commandes élémentaires de ddd, vous pouvez facilement :

  • identifier que c’est la fonction nbPlaces qui plante;
  • examiner le contenu de la variable fi et constater qu’elle est nulle, ce qui explique l’erreur d’adressage mémoire lors du déférencement du pointeur;
  • ensuite rechercher pourquoi ce pointeur est nul. Comme il indique l’adresse du descripteur de file, il faut examiner le fonctionnement de la routine creer. Pour cela placez un point d’arrêt (breakpoint) au début de cette routine. Exécutez en pas à pas son fonctionnement et suivez l’évolution des valeurs des variables f et fi. Vous constatez qu’on sort de la routine avant d’avoir appelé malloc. En examinant le code, vous en trouverez la cause : une faute de frappe classique, dénoncée par les livres de programmation en langage C...

3. Recherche d’une cause de mauvais fonctionnement détectée par un programme de test

Placez-vous maintenant dans le répertoire 3.file1. Ce répertoire contient encore une fois une implémentation du type abstrait File avec un programme de test simplifié, tfile1, sans utiliser check. Le programme file1.c contient un bug qui fait fait échouer le programme de test, mais sans plantage et donc sans production d’un fichier core. Avec ddd, il faut trouver le bug, le corriger et vérifier que les tests sont alors sans erreur.

  • Exécutez le Makefile et repérez le numéro de ligne de l’échec (31).
  • Lancez ddd sur le programme exécutable tfile1.
  • Affichez les numéros de ligne (Source/Display Line Numbers).
  • Placez un point d’arrêt sur la ligne incriminée (31) et lancez l’exécution.
  • Examinez les variables qui sont à l’origine de l’échec (sor.info vaut "e2" au lieu de "e1"). Le problème vient donc du dernier appel à sortir.

  • Placez un point d’arrêt sur l’appel à sortir (ligne d’au dessus) et relancez l’exécution.
  • Avancez d’un pas pour entrer dans sortir. Exécutez ensuite sortir en mode pas à pas.
  • Affichez la variable interne fi et en double-cliquant sur sa représentation, faite apparaître sa valeur en mode vertical (avec les noms des champs). Vous trouvez que la file a trois places, trois éléments et les curseurs entree=2 et sortie=0.
  • Affichez le contenu de la mémoire fi->mem[fi->sortie] (tapé dans le tampon d’entrée, après l’avoir vidé par ()). Vous trouvez "e2", la valeur qui est sortie. Donc sortir fonctionne apparemment de façon correcte et le problème doit venir de l’état de la représentation de la file fi. Il faut donc inspecter le fonctionnement de entrer.

  • Abandonnez l’exécution (Program/Kill) et rechargez le source tfile1.c (menu File) pour placer un point d’arrêt sur le premier appel à entrer (ligne 27).
  • Relancez l’exécution, puis entrez en pas à pas dans la routine entrer. Comme précédemment, dépliez le contenu de fi avec des doubles clics. Vous voyez ainsi apparaître le contenu de FileImpl avec ses champs. Si vous double-cliquez sur mem, vous affichez fi->mem[0]. Pour afficher les autres valeurs du tableau mem, il faut:
    • soit expliciter les adresses (car mem est déclaré comme un pointeur dans le programme²) i.e. Afficher donc fi->mem[1] et fi->mem[2],
    • soit indiquer la tranche du tableau que l’on veut afficher : fi->mem[0..2].

Visualisation des variables avec ddd
Les champs info ont des adresses nulles (en rouge).

  • Continuez l’exécution jusqu’à la fin de la routine (until). Lorsque vous quitterez la routine entrer, l’affichage des variables disparaîtra. Mais dès que vous entrerez à nouveau dedans, il réapparaîtra. Vous pouvez ensuite poursuivre l’exécution en pas à pas et observer les changements de valeur des champs de FileImpl. En réfléchissant aux valeurs que devraient avoir ces différents champs, vous trouverez facilement quel champ est incorrect et donc quelle instruction de la routine entrer est incorrecte. Corrigez l’erreur, relancez le test et concluez.

4. Pour les plus courageux

Placez-vous dans le répertoire 4.file2 et exécutez le Makefile. Ce répertoire contient la version 2 de la file, comme dans le TP de synthèse, mais avec un petit programme de test et deux erreurs d’implémentation à trouver avec ddd. Cette expérience devrait vous convaincre qu’il ne faut utiliser un debugger que lorsqu’il n’y a aucune autre solution. L’utilisation d’assertions exécutables évite le debugging...

Remarque finale : Le problème posé par le debugging d’un programme de test piloté par check est compliqué par l’ajout des primitives de check qui sont censées être fiables et sans effet sur la cause du problème. Si les codes source de check sont accessibles, ils seront montrés par ddd comme les autres routines du programme à tester, ce qui nécessite de les sauter lors des exécutions en pas à pas. Une solution à ce problème est d’écrire un programme de test sans check, comme ci-dessus ou de changer la définition des macros de check pour les rendre inoffensives.


¹: Sous Cygwin le fichier obtenu est tfile1.stackdump, mais ce fichier, destiné au logiciel DRWatson sous Windows, n’est pas utilisable par ddd.
²: Avec un langage à objets, on n’aurait pas cet inconvénient, car tout objet est typé et le système peut donc afficher son contenu comme il convient. En C, il faut aider le debugger.

-- NicolasNobelis - 15 Mar 2008
to top

I Attachment sort Action Size Date Who Comment
Fourniture6.tbz manage 6.6 K 09 Mar 2008 - 15:08 NicolasNobelis Fourniture pour le TP6
corrige06.pdf manage 41.0 K 15 Mar 2008 - 20:44 NicolasNobelis Corrigé du TP6

You are here: Linfo > GLEnvtProg0708 > GLEnvtProg0708TP6

to top

Copyright © 1999-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding WIKIDeptinfo? Send feedback