Vous êtes sur la page 1sur 27

Ingénierie

logicielle

Débogage

Michel Gagnon et Julien Gascon-Samson - École Polytechnique de Montréal


Commentaires généraux
 Contrairement aux tests, qui visent à détecter les
erreurs, le débogage sert à identifier les causes des
erreurs et les corriger
 Comme les tests, il ne s‟agit pas d‟une méthode
d‟amélioration du code
 Certains développeurs sont 20 fois plus rapides que
d‟autres pour déboguer
 La correction de bogue introduit souvent de
nouveaux défauts
Avantages du débogage
 Apprendre sur le programme
• … il y a quelque chose à apprendre, sinon il n‟y aurait pas de défauts !

 Apprendre sur les types d‟erreurs commises


• Feedback par rétroaction; ne plus répéter les mêmes erreurs

 Apprendre sur la qualité du code du point de vue de quelqu‟un qui doit le lire
• Effectuer en même temps une rétroingéniérie?

 Apprendre comment on résout les problèmes


• A-t-on la bonne approche de déboguage?

 Apprendre comment les défauts sont corrigés


• Goto et « quickfix » ou une approche plus systématique ?
Mauvaises pratiques
 Trouver le défaut par divination
• « Print » partout dans le code
• Essayer de modifier le programme aléatoirement
• Ne pas conserver de sauvegarde

 Ne pas essayer de comprendre le problème


• Le problème est tellement trivial à corriger, c‟est suffisant

 Appliquer les corrections les plus évidentes


• x = Calculer (y)
si (y = 17) alors
x = 25,15$
Déboguer par superstition

 Source du problème :
• Bugs “mystérieux” de compilateur
• Défauts du langage de programmation
• Mauvais jeux de données
• Perte des changements importants
• Éditeur qui ne sauvegarde pas correctement
 Toujours être humble et assumer en premier
que l‟erreur vient de vous !
Méthode scientifique de
débogage
1 Stabiliser l‟erreur (être capable de la faire « manifester »)
2 Localiser la source
a. Collecter les données qui produisent le défaut
b. Analyser les données et former une hypothèse
c. Trouver une manière de prouver ou réfuter l‟hypothèse
d. Prouver ou réfuter l‟hypothèse
3 Corriger le défaut
4 Tester la correction
5 Rechercher des erreurs similaires
Stabiliser l’erreur
 Erreurs intermittentes : très difficile à stabiliser
• Initialisation, pointeur, « timing », etc.

 Réduire le cas de test à l‟exécution la plus simple


capable de produire l‟erreur
 La simplification du test peut exiger de faire appel à la
méthode scientifique
• L‟erreur peut dépendre de plusieurs facteurs
• Isoler l‟ensemble minimal de facteurs causant l‟erreur
Remarque importante

Si vous avez du mal à trouver les défauts,


c’est peut-être parce que votre code n’est pas
bien écrit.
Conseils pour trouver les
défauts
 Utiliser toutes les données disponibles pour faire les
hypothèses…
• Les hypothèses peuvent être imprécises mais seront raffinées
graduellement

 Raffiner les cas de tests qui produisent l‟erreur


 Utiliser les tests unitaires (en isolation)
 Faire appel à tous les outils disponibles (très important !)
 Reproduire l‟erreur de plusieurs manières différentes
 Générer davantage de données
 Utiliser aussi les tests négatifs pour raffiner les hypothèses
Conseils pour trouver les
défauts (suite)
 Pratiquer le remue-méninges pour trouver de nouvelles
hypothèses
 Prendre des notes
 Essayer de réduire la partie douteuse du code
• Commenter le code !

 Se méfier des endroits où des erreurs ont déjà été détectées


 Vérifier le code récemment modifié
 Suspecter une région plus étendue du code
Conseils pour trouver les
défauts (suite et fin)
 Intégrer de manière incrémentale
 Rechercher les défauts courants
• Si vous avez de la rétroaction dans votre processus,
vous avez peut-être une liste de défauts typiques à
votre organisation…
 Discuter du problème avec une autre personne
• Pour parfois découvrir soi-même l‟erreur!
 Prendre une petite pause… Facebook, YouTube,
etc. Mais pas trop longtemps !!
Utilisation de la force brute
 Révision complète du code
 Recoder au complet
 Compiler le code avec des avertissements détaillés
 Créer une très grosse suite de tests automatisés
 Débogage pas à pas
 Traçage du code avec des affichages
 Compiler le code avec un autre compilateur
 Répliquer la configuration machine de l‟utilisateur final
 Intégrer le code pièces par pièces (assemblage de « blocs »)
 Limiter le temps consacré au déboguage « quick and dirty »
Erreurs de syntaxe
 Ne pas faire confiance aux numéros de ligne dans
les messages d‟erreur du compilateur STL...

 Ne pas trop se fier aux messages du compilateur


 Ne pas se fier au second message du compilateur
 Diviser pour régner
 Rechercher les commentaires, et les guillemets mal
placés, les virgules, points-virgules, accolades, etc.
Correction du défaut
 Comprendre avant de corriger
 Comprendre le programme, pas seulement le problème
 Confirmer les diagnostic avant de corriger
 Relaxer
 Enregistrer le code source original
 Corriger le problème, pas le symptôme
• On salit le design
• On met en relief les particularité au lieu de réduire la complexité
Correction du défaut (suite)

 Ne modifier le code que si on a de bonnes


raisons de le faire
 Une modification à la fois
 Vérifier la correction
 Ajouter un cas de test correspondant à
l‟erreur détectée
 Rechercher les défauts similaires
Conditionnement psychologique

Paris in the
the Spring
Conditionnement psychologique
 Construction : aspect plus créatif
 Déboguage : pensée plus rigide
 While en programmation vs While dans la langue
naturelle
 If ( x < y)
swap = x;
x = y;
y = swap;

 Distance psychologique : strncpy vs strcpy, strncmp vs strcmp


Outils de débogage
 Comparateurs de code source (diff)  qui a changé quoi?
 Messages d‟avertissement du compilateur
• Niveau d‟avertissement maximal
• Avertissements = erreurs !
• Mêmes paramètres de compilation pour tous

 Outils de vérification de syntaxe et de logique (cppcheck pour C++)


 Profileurs d‟exécution (performance)
 Environnements de test (cas de tests, etc.)
 Débogueurs
Exemple 1
void strReverse(const int str_length,
const char* str, char* buff) {
// special case for zero-length strings
if( str_length == 0 ) {
return;
}

// reverse
int i;
for(i = 0; i < str_length; ++i) {
buff[i] = str[str_length - i];
}

buff[i] = '\0‟;
}
Exemple 1

 Hypothèses:
• On n‟entre jamais dans la boucle
• On entre dans la boucle, mais quand on en
sort, on a buff[0] = „\0‟
Exemple 2
void List::insert(int newY) void List::scootOver(int jj)
{ {
if (numY_ = 0) { // Y empty so far int k;
Y_[0] = newY; for (k = numY_-1; k > jj; k++)
return; Y_[k] = Y_[k-1];
} }
// need to insert just before the first Y
// element that newY is less than
for (int j = 0; j < numY_; j++) {
if (newY < Y_[j]) {
// shift Y_[j], Y_[j+1],... rightward
// before inserting NewY
scootOver(j);
Y_[j] = newY;
return;
}
}
}
Exemple 2 – tests unitaires
void ListTest::test4()
{
liste.insert(10);
liste.insert(5);
CPPUNIT_ASSERT_EQUAL(5,liste.getElement(0));
CPPUNIT_ASSERT_EQUAL(10,liste.getElement(1));
}

void ListTest::test6()
{
liste.insert(5);
liste.insert(2);

liste.insert(100);
CPPUNIT_ASSERT_EQUAL(1,liste.getElement(0));

CPPUNIT_ASSERT_EQUAL(100,liste.getElement(9));
}
Exemple 2

 Problème: pas de résultat retourné


 Hypothèse:
• boucle infinie
Exemple 2

 Nouveau problème: segmentation fault


 Hypothèse:
• Débordement de tableau
Exemple 2

 Nouveau problème: l‟insertion fonctionne mal


 Hypothèse:
• L‟insertion ne se fait pas quand la position
d‟insertion est à la fin du tableau
Exemple 2

 Nouveau problème: ne fonctionne toujours


pas quand la liste est en désordre
 Hypothèse:
• Le problème est dans scootOver
Exemple 2

 Encore un problème: les tests unitaires


donnent des erreurs de débordement
 Hypothèse
• La taille des données n‟est pas mise à jour