---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---

+++ {"nbgrader": {"grade": false, "grade_id": "cell-415f04d27b610548", "locked": true, "schema_version": 3, "solution": false, "task": false}}

# Semaine 9 : débogage, tests, projet

:::{todo}
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

:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-6d952c4decd8c708", "locked": true, "schema_version": 3, "solution": false, "task": false}}

Vous trouverez ici le matériel pédagogique pour la Semaine 9, dont
l'objectif premier est de **lancer votre travail sur le
[projet](http://nicolas.thiery.name/Enseignement/Info111/projet.html)**.
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**.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-6d952c4decd8c707", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## 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?

+++ {"nbgrader": {"grade": false, "grade_id": "cell-bf8b493b391072c0", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## [Cours : débogage et tests](cours-debogage-et-tests.md)

+++ {"nbgrader": {"grade": false, "grade_id": "cell-6d952c4decd8c705", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## TD

:::{note}

Vous n'aurez pas de TD cette semaine, par contre vous avez bien un TP !
Il est fortement recommandé d'utiliser le temps libéré par le TD pour commencer le TP
(partie déboguage ou partie [projet](http://nicolas.thiery.name/Enseignement/Info111/projet.html))
avant la séance de TP.
Pour ceux qui recontreraient des difficultés,
n'hésitez pas à relire le cours et à refaire le TD/TP de la semaine précédente, en vous aidant des corrections.

:::

:::{attention}

**Il y aura un dernier TD la semaine prochaine!**

:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-666b2425eb568c70", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## TP : déboguage, projet

+++ {"nbgrader": {"grade": false, "grade_id": "cell-0c6bc75af33eb28f", "locked": true, "schema_version": 3, "solution": false, "task": false}}

:::{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).**

:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-170cfbbf2f50acb7", "locked": true, "schema_version": 3, "solution": false, "task": false}, "tags": ["instructor"]}

:::{admonition} Notes aux enseignants

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

:::

+++

% ## Exercice 0: entraînement (5 minutes)
%  
% +++
%  
% À partir de maintenant, nous vous fournirons chaque semaine une collection de petits exercices d'entraînement et de révisions. Aujourd'hui, trois premiers pour tester l'outil.
%  
% Exécutez la cellule suivante, suivez les consignes qui s'affichent et cliquez sur `Valider` pour vérifier votre réponse. Utilisez les différents boutons pour parcourir les exercices et leurs variantes.
%  
% ```{code-cell} ipython3
% import glob
% from exercizer.jupyter_exercizer import Exercizer
% Exercizer(glob.glob("exercizer/*.md"))
% ```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-1a171b7d649f69a3", "locked": true, "schema_version": 3, "solution": false, "task": false}}

### 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](https://en.wikipedia.org/wiki/AddressSanitizer).

+++ {"nbgrader": {"grade": false, "grade_id": "cell-2fc0f7d48f389cb7", "locked": true, "schema_version": 3, "solution": false, "task": false}}

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` :
    ```
    g++ -g -fsanitize=address calcul.cpp -o calcul
    ```

    :::{attention}

    Notez que nous avons utilisé `g++` comme compilateur. La
    version de `clang++` que nous utilisons -- un peu ancienne pour
    des questions de compatibilité avec l'interpréteur `cling` -- ne
    gère en effet pas correctement l'option `-fsanitize=address`.

    :::

4.  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!

% :::{note}
%
% Dans ce TP, nous vous donnons les commandes de compilation directement
% avec `clang++` pour bien montrer le rôle de chacune des options. Vous
% pouvez aussi utiliser `info-111 clang++` qui ajoute plusieurs options,
% dont l'option `-g` (revoir les [explications](../Semaine7/index.md) de
% la Semaine7). Il sera recommandé de le faire pour le travail sur le
% projet et les TPs suivants.
%
% :::


+++ {"nbgrader": {"grade": false, "grade_id": "cell-37c69afe65f1223c", "locked": true, "schema_version": 3, "solution": false, "task": false}}

### 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.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-37c69afe65f1223d", "locked": true, "schema_version": 3, "solution": false, "task": false}}

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

:::{attention}

**Si vous travaillez sur myDocker, passez directement à l'exercice
suivant sur l'exécution pas à pas avec le débogueur.**

Explication : comme décrit dans le cours, 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; sur d'autres systèmes il peut être
nécessaire d'utiliser une variante.

:::

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 concentrer 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.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-b12d3c63adefa4c2", "locked": true, "schema_version": 3, "solution": false, "task": false}}

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
    ```

    :::

%   :::{admonition} Instructions sur myDocker
%  
%     Sur myDocker, le fichier `core` produit est enregistré dans le
%     dossier courant, sous un nom comme `core.12431`. Utilisez la
%     commande `ls` pour trouver ce fichier. Si vous avez plusieurs
%     fichiers core et que vous avez un doute, supprimez les tous avec
%   ```
%   rm core.*
%   ```
%   puis relancez le programme `calcul`.
%  
%     Lancez le débogueur :
%     ```
%     gdb --tui --silent calcul
%     ```
%  
%     Lancez l'analyse du fichier `core` avec la commande ci-dessous, en
%     remplaçant `core.12431` par le nom complet du nouveau
%     fichier `core` produit :
%     ```
%     (gdb) core core.12431
%     ```
%  
%   :::


5.  À quelle ligne a eu lieu l'erreur?

6.  Afficher la pile d'appel avec la commande suivante :
    ```
    where full
    ```

7.  Affichez le code avec :
    ```
    list
    ```

8.  Déduisez-en la cause de l'erreur. Ne la corrigez pas!

9.   Quittez le débogueur avec la commande :
    ```
    quit
    ```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-f5aa7ce5bc47c6c4", "locked": true, "schema_version": 3, "solution": false, "task": false}}

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

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

:::{admonition} Rappel
:class: attention

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

:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-bdc708aa3cbf0e8e", "locked": true, "schema_version": 3, "solution": false, "task": false}}

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.

+++ {"tags": ["instructor"]}

:::{admonition} 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.

:::

+++ {"nbgrader": {"grade": false, "grade_id": "cell-d4630df86c427579", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## Travail sur le [projet](http://nicolas.thiery.name/Enseignement/Info111/projet.html)

+++ {"nbgrader": {"grade": false, "grade_id": "cell-c9cc1722d57f505f", "locked": true, "schema_version": 3, "solution": false, "task": false}, "tags": ["instructor"]}

::::{admonition} Notes aux enseignants

:::{admonition} Notes sur le projet Images

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
entête avec un code (ex: P1), des dimensions, etc.

:::

:::{admonition} 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](../Semaine8/cours-etat-fichier.md)
  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](../Semaine8/cours-etat-fichier.md) 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
    chaînes de caractères lues comme nombres, donnant lieu à des
    totaux farfelus
  - code du type
    ```c++
    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.

:::

::::
