Skip to topic
|
Skip to bottom
Jump:
Linfo
Linfo Web
Linfo Web Home
Changes
Index
Search
Webs
Accueil
Docs
Enseignants
Linfo
MIPS
Main
Minfo
Minfo03
Minfo04
Minfo05
Minfo06
Minfo07
Private
Projets
Sandbox
TWiki
Create
personal sidebar
Edit
Attach
Printable
Linfo.GLEnvtProg0708TP6
r1.4 - 15 Mar 2008 - 20:45 -
NicolasNobelis
topic end
Start of topic |
Skip to actions
---+ TP 6 - Debugging de programmes avec DDD _Licence 3 Informatique - 2007-2008_ Main.PhilippeCollet, Main.NicolasNobelis URL de cette page : http://deptinfo.unice.fr/twiki/bin/view/Linfo/GLEnvtProg0708TP6 %BR% 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 [[%ATTACHURL%/Fourniture6.tbz][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é) : <verbatim> ~>trier 5 9 3 après tri: 3 5 9 </verbatim> 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.%BR% 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 [[http://www.gnu.org/software/ddd/manual/html_mono/ddd.html#Sample%20Session][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_).%BR% 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)*.%BR% 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[[#NoteUn][¹]]_.%BR% 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 : <verbatim> # Sous Linux , tapez la commande suivante avec complétion pour faire apparaître XXX : ~> ddd tfile1 core.XXX </verbatim> 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.%BR% * 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=.%BR% * 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=.%BR% * 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[[#NoteUn][²]]) 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]=. <p align="center"><img src="%ATTACHURLPATH%/Capture-DDD_small.png" alt="Visualisation des variables avec ddd" width="500" height="451" /><br/>Les champs =info= ont des adresses nulles (en rouge).<p> * 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...%BR% *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. ------ #NoteUn ¹: Sous Cygwin le fichier obtenu est _tfile1.stackdump_, mais ce fichier, destiné au logiciel DRWatson sous Windows, n’est pas utilisable par ddd.%BR% #NoteDeux ²: 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. * %N% [[%ATTACHURL%/corrige06.pdf][corrige06.pdf]] : Corrigé du TP6 -- Main.NicolasNobelis - 15 Mar 2008
to top
End of topic
Skip to action links
|
Back to top
Edit
|
Attach image or document
|
Printable version
|
Raw text
|
More topic actions
Revisions: | r1.4 |
>
|
r1.3
|
>
|
r1.2
|
Total page history
|
Backlinks
You are here:
Linfo
>
GLEnvtProg0708TP6
to top
Copyright © 1999-2021 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding WIKIDeptinfo?
Send feedback