Aller au contenu

Introduction aux traces d'exécution et au débogage

Objectifs de la section

3-1 Construction d’une trace d’exécution utile
3-2 Techniques de localisation d’erreur et d’identification des causes
3-3 Correction de programme

Temps requis

20 minutes

La recherche d'erreurs (aussi appelé bogues) fait partie intégrante du développement de système informatique. Pour s'aider, diverses techniques de débogage sont utilisées par les développeurs et développeuses pour faciliter le processus. Nous verrons deux techniques : une plus informelle et une plus systématique.

La syntaxe et le débogage

Il est important de mentionner que la syntaxe d'un programme doit être adéquate avant de commencer le débogage. On fait référence aux erreurs logiques plutôt que de syntaxe dans une activité de débogage.

Afficher l'état du programme

L'idée de cette technique est d'ajouter des instructions d'affichage pour suivre l'état des variables. Prenons l'exemple du code suivant :

Code à déboguer
def afficher_heure(temps):
    """
    Converti une heure du format décimal en affichage standard. Par exemple, pour 1.5 heures, cela affiche 1 h 30 m.
    Tous les éléments initiaux à 0 ne sont pas affichés. Donc il sera affiché 30 m au lieu de 0 h 30 m

    Parametre :
    temps -- le temps sous forme de nombre décimal
    """
    heures = int(temps)
    minutes = temps - heures * 100 % 60
    secondes = temps - heures - minutes * 100 % 60

    if minutes <=0:
        print(f"{temps} = {heures} h")
    if secondes <= 0 and minutes >= 0:
        print(f"{temps} = {heures} h {minutes} m")
    else :
        print(f"{temps} = {heures} h {minutes} m {secondes} s")

afficher_heure(0.2)
afficher_heure(1.5)
afficher_heure(4)
afficher_heure(34.6547)

La sortie du programme est la suivante : on remarque aisément que cela n'est pas le résultat souhaité.

La première étape consiste à s'interroger sur le calcul des variables, on peut donc en faire l'affichage de certains bouts pour suivre leur valeur.

Code à déboguer
def afficher_heure(temps):
    """..."""
    heures = int(temps)
    print(f"temps {temps} | heures en minutes {heures * 100 % 60}")
    minutes = temps - heures * 100 % 60
    secondes = temps - heures - minutes * 100 % 60

    if minutes <=0:
        print(f"{temps} = {heures} h")
    if secondes <= 0 and minutes >= 0:
        print(f"{temps} = {heures} h {minutes} m")
    else :
        print(f"{temps} = {heures} h {minutes} m {secondes} s")

L'ajout de l'affichage à la ligne 4 permet de constater que le calcul heures * 100 % 60affiche toujours la valeur 40, ce qui est évidemment un problème. On sait donc que ce calcul n'est pas bon. La suite de débogage consiste à se demander, mais pourquoi cette ligne affiche toujours la valeur 40 et proposer une nouvelle instruction qui fonctionne.

Après quelques minutes, on arrive au constat suivant qu'il faut remplacer heures * 100 % 60 par heures * 60.

Code à déboguer
def afficher_heure(temps):
    """..."""
    heures = int(temps)
    print(f"temps {temps} | heures en minutes {heures * 60}")
    minutes = temps - heures * 60
    secondes = temps - heures - minutes * 100 % 60

    if minutes <=0:
        print(f"{temps} = {heures} h")
    if secondes <= 0 and minutes >= 0:
        print(f"{temps} = {heures} h {minutes} m")
    else :
        print(f"{temps} = {heures} h {minutes} m {secondes} s")

On continue en notant l'erreur de priorité des opérations à la ligne 5 et une erreur de calcul semblable à celui de la ligne 6. Après la réécriture de tout ce code, on arrive à l'état suivant du programme.

Retirer les traces de débogage

Il ne faut pas oublier de retirer les traces de débogage après avoir trouver la source des erreurs. Il peut être utile de seulement les commenter (pour tout supprimer avant la remise), car les mêmes erreurs ont tendance à se réinviter, et donc nécessiter les mêmes traces de débogage.

Code à déboguer
def afficher_heure(temps):
    """..."""
    heures = int(temps)
    minutes = int((temps - heures) * 60)
    secondes = ((temps - heures) * 60) - minutes

    if minutes <=0:
        print(f"{temps} = {heures} h")
    if secondes <= 0 and minutes >= 0:
        print(f"{temps} = {heures} h {minutes} m")
    else :
        print(f"{temps} = {heures} h {minutes} m {secondes} s")

À cette étape, on voit que l'affichage se fait encore en double, mais que les heures, minutes et secondes se calculent correctement. Pour déboguer l'affichage, nous allons utiliser la technique plus systématique : le débogueur de Thonny.

Débogueur de Thonny

Un débogueur permet d'exécuter un script de façon contrôlée et en suivant l'état de la mémoire du programme. Pour lancer le débogueur, il faut appuyer sur l'icône d'insecte à droite du bouton pour lancer normalement. Dans le débogueur, le code s'exécute jusqu'au premier point d'arrêt. Un point d'arrêt est un endroit où l'on observe l'état du code. On définit un point d'arrêt en cliquant dans la marge de la ligne de code à laquelle on souhaite s'arrêter. Le débogueur arrête avant d'exécuter la ligne.

On ne peut pas s'arrêter partout

Il n'est pas possible de définir des points d'arrêt sur des lignes blanches, dans des commenaires ou sur la signature de fonction. En effet, ces lignes ne sont jamais exécutées à proprement dire.

Lancer le débogueur dans Thonny
Ajouter un point d'arrêt dans Thonny

Une fois l'exécution arrêtée, on peut la repartir de 4 façons :

  1. Étape suivante (F6) : continue le programme en exécutant la ligne surlignée. Si la ligne est une fonction alors on reçoit immédiatement le résultat de la fonction, sans entrer dans les instructions de cette fonction.
  2. Entrer dans l'étape en cours (F7) : continue le programme en exécutant la ligne surlignée. Si la ligne est une fonction alors on entre dans les instructions de cette fonction ce qui permet de voir le calcul du résultat par étape.
  3. Sortir de l'étape en cours : continue le programme en mettant fin à la fonction actuelle et en retournant dans la fonction appelante.
  4. Reprendre (F8) : reprend l'exécution normale du programme jusqu'au prochain point d'arrêt.
Exécution du code en mode de débogage

En exécutant le code étape par étape (avec étape suivante), on se rend compte que plusieurs branches du if sont faites. Il faut donc réécrire les conditions pour les rendre toutes exclusives.

Code fonctionnel
def afficher_heure(temps):
    """
    Converti une heure du format décimal en affichage standard. Par exemple, pour 1.5 heures, cela affiche 1 h 30 m.
    Tous les éléments initiaux à 0 ne sont pas affichés. Donc il sera affiché 30 m au lieu de 0 h 30 m

    Parametre :
    temps -- le temps sous forme de nombre décimal
    """
    heures = int(temps)
    minutes = int((temps - heures) * 60)
    secondes = (((temps - heures) * 60) - minutes) * 60

    if heures == 0:
        if minutes == 0:
            print(f"{temps} = {heures:.0f}s")
        else :
            print(f"{temps} = {minutes}m {secondes:.0f}s")
    else:
        print(f"{temps} = {heures}h {minutes}m {secondes:.0f}s")

afficher_heure(0.2)
afficher_heure(1.5)
afficher_heure(4)
afficher_heure(34.6547)
Résultat d'exécution du programme complet