Semaine 9 : débogage, tests, projet#

À faire

Explore using libSegFault and addr2line for displaying stack-traces

LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libSegFault.so exemple-segmentation-fault sudo apt install glibc-tools

std::stacktrace: https://en.cppreference.com/w/cpp/utility/basic_stacktrace

Vous trouverez ici le matériel pédagogique pour la Semaine 9, dont l’objectif premier est de lancer votre travail sur le projet. Jusqu’ici, vous avez écrit des programmes de quelques lignes; avec le projet, vous allez progressivement aborder des programmes de une à plusieurs centaines de lignes. Aussi, pour vous y préparer, nous verrons en amphi des éléments de méthodologie et des outils de débogage.

Consignes#

Vous n’aurez pas de trop des quelques semaines à venir pour le projet; lancez vous à fond dès maintenant. Pour être fier de vos réalisations à venir. Pour de belles soutenances la semaine du 9 décembre. Et aussi parce que le projet est le moment où vous allez consolider et donner du sens à tout ce que nous avons vu depuis le début du semestre. Incidemment, c’est la meilleure préparation à l’examen du 18 décembre, dans moins d’un mois.

Les TPs seront des moments privilégiés des semaines à venir où vous pourrez bénéficier de toute l’aide d’un enseignant. Pour en tirer le meilleur parti, il est essentiel de l’avoir bien préparé, notamment en ayant travaillé sur le projet bien en amont; pour cette semaine:

  • Choix d’un binôme

  • Téléchargement et consultation du sujet (voir instructions sur la page web ci-dessus)

  • Début du travail en autonomie (quelques heures!)

  • Préparation des questions à l’attention de votre enseignant: quels sont les points à clarifier? les problèmes bloquants?

Cours : débogage et tests#

TD#

Note

Vous n’aurez pas de TD cette semaine; c’est autant de temps de libéré pour vous consacrer au projet.

Attention

Il y aura un dernier TD la semaine prochaine!

TP : déboguage, projet#

Attention

Le cœur de ce TP est le projet. Ne passez pas plus de 25 minutes sur les exercices de cette feuille (entraînement, débogage).

Notes aux enseignants

Vérifiez que les étudiants suivent bien la consigne ci-dessus!

Exercice : analyse post-mortem d’erreurs à l’exécution#

Il est possible de compiler un programme de sorte que les erreurs d’accès à la mémoire soient mieux détectées et que la pile d’appel soit affichée directement en cas de plantage (comme avec Python) sans avoir à utiliser le débogueur. Le programme consomme cependant un peu plus de ressources. En savoir plus.

  1. Compilez le programme calcul.cpp, exécutez le et observez l’affichage.

  2. L’exécution s’est terminée avec une erreur. Ouvrez le fichier calcul.cpp; constatez que l’instruction du programme ligne 20 n’a pas été exécutée. On sait donc que l’erreur s’est produite avant cette ligne, mais on ne sait pas où exactement elle s’est produite, ni dans quelles circonstances. Dans cet exercice et les trois suivants, nous allons mettre en application quatre techniques vues en cours pour analyser l’état du programme au moment de l’erreur.

  3. Recompilez le programme calcul.cpp en ajoutant les options -g et -fsanitize=address :

    clang++ -g -fsanitize=address calcul.cpp -o calcul
    
  1. Exécutez le programme, et lisez le message d’erreur détaillé. Déduisez en où a lieu l’erreur. Ne cherchez pas à corriger le programme!

Exercice : analyse d’erreurs à l’exécution par instrumentation du code#

Votre objectif est maintenant de déterminer quelle était la valeur de i et de x au moment de l’erreur.

  1. Instrumentez le programme calcul.cpp en ajoutant des affichages de x et de i.
    Rappel : pour ces affichages, utilisez cerr plutôt que cout.

  2. Compilez et exécutez le programme calcul.cpp et déduisez en la valeur de x et i au moment de l’erreur.

  3. Mettez vos affichages en commentaire (en rajoutant un // en début de ligne) avant de passer à la suite.

Exercice : analyse post-mortem d’erreurs à l’exécution avec le débogueur#

Attention

Rappel: la gestion des fichiers core dépends des systèmes d’exploitation. La procédure décrite ci-dessous est valide en local sur les machines des salles de TP et plus généralement sur des versions récentes de Ubuntu. Elle n’est pas encore disponible sur myDocker et sur d’autres systèmes il peut être nécessaire d’utiliser une variante.

Si vous travaillez sur myDocker, passez directement au projet.

Le but de cet exercice est de commencer à prendre en main l’utilisation du débogueur, afin de pouvoir l’utiliser seul par la suite lorsque vous rencontrerez des problèmes avec vos programmes du projet. Nous allons pour le moment nous consacrer sur l”analyse post-mortem d’erreurs à l’exécution, comme ferait un médecin légiste sur le corps d’une victime d’un crime.

Notez que, lorsque vous avez lancé le programme calcul précédemment, le message core dumped était affiché. Comme vu en cours, cela signifiait que, en cas d’erreur grave d’un programme, le système d’exploitation enregistre (dump) tout ou partie de l’état de la mémoire dans un fichier appelé core pour analyse ultérieure. Dans cet exercice, nous allons utiliser le déboggeur pour réaliser une telle analyse.

  1. Recompilez le programme avec l’option -g :

    clang++ -g calcul.cpp -o calcul
    
  2. Configurez votre terminal pour que, en cas d’erreur grave d’un programme, un fichier core soit enregistré, avec une taille maximale de 1M:

    ulimit -c 1000000
    
  3. Relancez le programme.

    ./calcul
    
  4. La commande suivante lance automatiquement le déboggueur sur le dernier fichier core produit par l’utilisateur :

    coredumpctl debug
    
  1. À quelle ligne a eu lieu l’erreur?

  2. Afficher la pile d’appel avec la commande suivante :

    where full
    
  3. Affichez le code avec :

    list
    
  4. Déduisez-en la cause de l’erreur. Ne la corrigez pas!

  5. Quittez le débogueur avec la commande :

```
quit
```

Exercice : exécution pas-à-pas avec le débogueur#

Nous allons maintenant analyser le même programme en l’exécutant pas à pas.

Attention

Rappel

L’exécution pas-à-pas avec gdb n’est actuellement pas disponible sur myDocker. Vous ne pouvez donc faire cet exercice que si vous avez installé les logiciels chez vous.

Attention

Rappel

Ne passez pas plus de 20 minutes sur le débogage. Si vous n’avez pas fini, vous reprendrez plus tard. Passez au projet.

  1. Retrouvez le mode d’emploi de gdb dans les notes du cours. Une fois le débogueur lancé, consultez les questions suivantes du TP pour savoir quelles commandes du débogueur utiliser. Il est recommandé d’avoir sous les yeux à la fois l’énoncé de TP (pour savoir quelles commandes on vous demande d’utiliser) et le mode d’emploi (qui indique à quoi correspondent ces commandes).

  2. Utilisez les commandes start, puis step pour exécuter successivement quelques instructions jusqu’à rentrer dans la boucle for.

  3. Essayez la commande print i, puis avancez de quelques instructions. Essayez la commande display i, puis avancez de quelques instructions.

  4. Continuez l’exécution pas à pas jusqu’à rentrer dans la fonction max. Utilisez la commande where full pour visualiser la pile d’appel.

  5. Utilisez la commande continue pour continuer l’exécution jusqu’à la fin.

  6. L’exécution se termine avec une erreur. Cette fois la ligne au moment de l’erreur est affichée. Utilisez les commandes vues plus haut pour consulter les valeurs des variables et retrouver ce qui a déclenché l’erreur.

Notes aux enseignants

Certains des étudiants ne comprennent pas du tout ce que fait le débogueur, il faut aller voir un par un ceux qui ont du mal et leur expliquer. Leur rappeler qu’ils ont eu une démo en amphi et que cela deviendra plus concret et motivant quand ils seront confrontés à des bogues dans leurs propres programmes).

Au lancement, s’ils ont oublié l’option --silent, le débogueur affiche un message disant d’appuyer sur la touche RET pour continuer ou q pour quitter, mais beaucoup d’élèves ne lisent pas ce message et n’appuient pas sur return.

Certains pensent qu’il faut corriger le programme. Leur dire que le but est juste de trouver comprendre l’erreur (et de savoir plus généralement utiliser le terminal pour comprendre l’erreur dans un programme erroné quelconque) : pour ce programme-là, on ne sait pas ce que le concepteur avait en tête, donc on ne peut pas le corriger à sa place. Par contre, quand ils débogueront leurs propres programmes (par exemple pour le projet), une fois qu’ils auront compris l’erreur ils pourront la corriger.

Travail sur le projet#

Notes aux enseignants

Notes sur le projet Images (2023-2024)

Si une image produite par une fonction d’un élève n’est pas correcte (par exemple si elle ne peut pas être ouverte avec une visionneuse d’images), dire aux élèves d’ouvrir cette image avec un éditeur de texte pour voir ce qui est vraiment écrit dedans. Beaucoup d’erreurs, que ce soit en lecture ou en écriture, concernent l’oubli qu’un fichier image ne contient pas uniquement des pixels, mais aussi un code (ex: P1), des dimensions, etc.

Notes sur le projet Données Libres (2024-2025)

Pour les élèves qui ne voient pas du tout comment faire, leur dire de regarder les exemples similaires qu’ils ont déjà vus:

  • lecture du fichier annuaire dans le cours.

  • calcul de la moyenne de notes contenues dans un fichier du TD8 (solution en ligne sur le site web du cours).

Erreurs typiques:

  • pour ouvrir un fichier de données:

    • oubli des guillemets autour du nom de fichier

    • oubli de l’extension .txt du nom de fichier

    • oubli d’indiquer le dossier donnees/ où est le fichier

    • typo dans le nom du fichier

    À l’exécution le fichier n’est pas trouvé, et cela ne donne pas de message d’erreur; simplement la boucle while n’est pas exécutée. Donc affichage de 0.

    Rappeler la bonne pratique du cours 8 : systématiquement vérifier si le fichier s’est bien ouvert, et sinon lever une exception. Leur dire que cette bonne pratique permet d’appliquer une dichotomie sur l’origine du problème : fichier mal ouvert ou suite du programme incorrect. C’est l’occasion d’illustrer concrètement ce principe de dichotomie discuté en cours.

  • pour lire l’information d’un fichier ouvert en lecture:

    • lire les champs deux par deux au lieu de trois par trois, du coup chaines de caractères lues comme nombres, donnant lieu à des totaux farfelus

    • code du type

      while ( fichier >> annee >> jour >> nombre ) {
          fichier >> annee >> jour >> nombre;
          total += nombre;
      }
      

      en pensant que l’instruction de lecture du while n’est pas vraiment exécutée; du coup obtention d’un total à peu près deux fois trop petit, puisqu’on n’a finalement tenu compte que d’une ligne sur deux

Quelques manifestations de joie intense lorsqu’ayant longtemps tâtonné on finit par obtenir le total recherché à partir des données du fichier.