Skip to content

Latest commit

 

History

History
252 lines (210 loc) · 13.9 KB

File metadata and controls

252 lines (210 loc) · 13.9 KB

Débogage, tests et profilage

Débogage ([wikipedia w])

Bogue, débogage et débogueur

  • Un bogue (bug) est un défaut dans un programme qui l’empêche de fonctionner correctement

  • Le débogage (debugging) est une activité ayant pour objectif de localiser les bogues dans un programme

  • Le débogage est basé sur la confirmation

    • c’est un processus destiné à confirmer les choses que l’on croit vrai jusqu’à en trouver une qui ne l’est pas

  • Un débogueur (debugger) est un outil fournissant une aide pour le débogage

Pourquoi utiliser un débogueur ?

  • Pour gagner du temps !

  • Les alternatives sont moins efficaces (mais parfois utiles tout de même)

    • utiliser des affichages (printf, …)

      • perte de temps

      • beaucoup d’éditions/compilations pour ajouter/enlever les affichages

      • moins informatif

    • utiliser une bibliothèque de journalisation (logging) ([wikipedia w])

      • plus pratique que les affichages

      • moins informatif que le débogueur

Fonctionnalités d’un débogueur

  • Exécution contrôlée du programme

    • pas à pas sommaire (sans entrer dans les fonctions)

    • pas à pas détaillé

    • retour en arrière (plus rare)

  • Pose de points d’arrêt (breakpoints)

    • repère sur une instruction signalant au débogueur qu’il doit faire un pause dans l’exécution lorsqu’il arrive à cette instruction

    • peut être également associé à une condition

    • un point d’observation (watchpoint) stoppe le programme lorsque l’état d’une expression change

    • un catchpoint fait de même quand un événement se déclenche

  • Visualisation de l’état du programme

    • variables, pile d’appel, …

    • certains débogueurs permettent l’affichage de structure de données complexes

  • Modification de l’état du programme

  • Débogage à distance

Processus de débogage

  1. Tenter de reproduire le bogue

  2. Simplifier les entrées du programme

  3. Exécuter le programme sous le contrôle du débogueur

  4. Se positionner à l’endroit de l’erreur signalée ou au milieu du programme (pose d’un breakpoint) si aucune erreur n’est signalée

  5. Examiner le contexte pour confirmer que les variables possèdent les valeurs attendues

  6. Déterminer la position suivante à étudier et retourner en 5

Important
nécessite de compiler le programme avec les informations de débogage (option -g de javac ou de gcc)
Quelques débogueurs

Définition et intérêt

  • Les tests visent à mettre en évidence des défauts de l’élément testé

  • L’objectif final est bien sûr de réduire le nombre de bogues présents dans un programme

  • Un test est un ensemble de cas à tester (conditions initiales, entrées, observations attendues)

Warning

Un test ne permet pas de prouver l’absence de bogue (≠ des méthodes formelles).

  • Il est impossible d’exécuter des tests exhaustifs dans la plupart des cas

  • Les tests sont toutefois une aide précieuse pour améliorer la qualité du logiciel

Quelques types de tests

  • Un test boite blanche (white box) s’appuie sur une connaissance de la structure interne de l’élément à tester

  • Un test boite noire (black box) s’appuie sur les spécifications de l’élément

  • Un test de non régression vérifie que le système ne se dégrade pas lors de ses évolutions

  • Un test fonctionnel s’assure que les résultats attendus sont bien obtenus

  • Un test non fonctionnel analyse les propriétés non fonctionnelles d’un système

    • test des performances pour vérifier l’efficacité du système

    • test de sécurité pour s’assurer du respect des règles de confidentialité

Granularité de tests

Unitaire

Les tests unitaires vérifient la conformité des éléments de base d’un programme (fonctions, classes, …) et sont en général réalisés par le développeur.

Intégration

Les tests d’intégration vérifient la cohérence des différents modules et la bonne communication entre eux.

Système

Les tests systèmes concernent l’ensemble du projet et son intégration dans son environnement.

Recette

Les tests de recette (ou d’acceptation) confirment la conformité du système avec les besoins.

Intégration au processus de développement

  • Généralement (cycle de développement en V par exemple), les tests sont réalisés par un groupe de testeurs après la réalisation des fonctionnalités

  • Une pratique encouragée par les méthodes Agiles et XP consiste à débuter le processus par les tests (Développement dirigé par les tests)

Quelques outils pour les tests
Unitaires et d’intégration

cf. ci-dessous.

Fonctionnel ([wikipedia w])

Fit, FitNesse.

Non fonctionnels ([wikipedia w])

Apache JMeter, JUnitPerf.

Tests unitaires

Définition et objectifs

  • Un test unitaire (unit test) vise à augmenter la confiance du programmeur dans des portions du code source

  • Une unité fait référence à la plus petite partie testable de l’application (fonction, méthode)

  • Le but des tests unitaires est d’isoler chaque partie du programme pour la tester indépendamment

    • isoler les différents éléments nécessite souvent d’avoir recours à du code de substitution (stub, fake ou mock object)

  • Ces tests peuvent être réalisés à la main (affichages et vérification visuel, débogueur) ou avec un framework spécialisé par exemple de type xUnit

Table 1. Quelques frameworks de test unitaire ([wikipedia w])
Java Python Bash

JUnit, TestNG,

unittest

Bats

Principe

  • Pour chaque unité, on écrit une ou plusieurs méthodes de test

    • un outil de gestion est nécessaire vu le nombre de tests

  • Une possibilité intéressante est d’écrire le test avant la méthode

    • précise d’abord ce que doit faire la méthode

  • L’ensemble des tests peut ensuite être répété autant que nécessaire

    • l’exécution des tests après chaque modification permet de vérifier la non régression

Caractéristiques des tests unitaires

  • Petits (analyse d’un point précis) et rapides (exécutés souvent)

  • Totalement automatisés

  • Toujours au niveau de l’unité

  • Indépendants les uns des autres (pas de contraintes d’ordre)

  • N’utilisent pas de ressources externes (SGBD, …)

Doublure de tests

  • Un test unitaire se focalise sur un élément particulier

  • Ce dernier peut être dépendant d’autres éléments

  • Une doublure de test permet de remplacer ces dépendances

Plusieurs types de doublure (cf. Mocks Aren’t Stubs, Martin Fowler, 2007)
Fantôme

un objet fantôme (dummy) sert juste à remplir des listes de paramètres

Substitut

un objet substitut (fake) fournit une implémentation simplifiée

Bouchon

un objet bouchon (stub) retourne des réponses prédéfinies spécifiques aux tests

Simulacre

un objet simulacre (mock) sont préprogrammés par des attentes, i.e. une spécification du comportement attendu

Table 2. Quelques frameworks de doublure de test ([wikipedia w])
Java

Mockito, JMockit, EasyMock, PowerMock,

Couverture de code

  • L’objectif est de vérifier que les tests unitaires couvrent bien l’ensemble du code écrit

  • La couverture de code (code coverage) est un outil de mesure de la qualité des tests effectués

  • Le degré de couverture est mesuré par des indices statistiques

  • Les portions de codes non testées sont mises en évidence

Quelques métriques

  • Le Statement Coverage (ou Line Coverage) mesure le degré d’exécution de chaque ligne

    • simple mais ignore un certain nombre d’erreurs simples (ne prend pas en compte la logique du programme)

  • Le Condition Coverage indique si toutes les conditions ont été évaluées

    • les conditions doivent être évaluées à vrai et à faux pour obtenir un taux de 100\%

    • aide à résoudre les problèmes de la mesure précédente

  • Le Path coverage examine si chaque chemin a été parcouru

  • Le Function Coverage vérifie si chaque fonction a été appelée

Important
Un score de 100% ne garantit pas la correction du programme. Ce n’est même pas un objectif !
Table 3. Couverture des tests ([wikipedia w])
Java

Cobertura, EMMA, Clover,

Développement piloté par les tests

  • Le développement piloté par les tests (Test Driven Development ou TDD) est une méthode de développement mettant l’accent sur les tests unitaires

  • Cette méthode préconise d’écrire les tests avant le code

    • Only ever write code to fix a failing test

  • Cette approche permet de spécifier ce que l’on attend du système avant de le réaliser

  • Elle est basée sur les tests et le \href{http://www.refactoring.com/{refactoring}}

  • Le refactoring consiste à améliorer la conception du programme sans en changer le comportement (les fonctionnalités)

  • Le TDD n’est pas limité aux tests unitaires mais s’applique aussi aux tests de recette (Acceptance TDD)

Cycle de développement

  • Le TDD s’appuie sur de courtes itérations

  • Chaque itération possède cinq étapes

    1. Écrire un test

    2. Exécuter les tests et vérifier que le nouveau échoue

    3. Écrire juste le code nécessaire pour faire passer le test

    4. Réexécuter les tests et vérifier que tous les tests passent

    5. Corriger la conception du système (refactoring)

  • La phase de refactoring s’applique aussi bien au code de l’application qu’au code des tests

Programmation pilotée par le comportement ([wikipedia w])

  • Les pratiques de TDD peuvent être étendues vers la Programmation pilotée par le comportement (Behaviour-Driven Development ou BDD)

Quelques frameworks de BDD

Cucumber, Spock, JBehave,

Optimisation et profilage ([wikipedia w])

  • L'optimisation est la pratique qui consiste à modifier un système pour qu’il fonctionne plus efficacement

    • par exemple plus rapidement ou en consommant moins de ressources

    • L’optimisation est souvent un compromis entre différents facteurs

  • L'analyse dynamique (profiling) d’un programme a pour objectif de collecter des informations sur le comportement d’une application pendant son exécution

    • Les éléments à surveiller sont l’utilisation des CPU, l’utilisation de la mémoire, les threads, …

Warning
Ce type d’analyse a un impact sur le comportement de l’application.

Mise en œuvre

  • Un outil d’analyse dynamique permet de collecter et de présenter les informations résultant de l’analyse de l’exécution

  • Utilisé pour l’analyse de performances, un tel outil permet de localiser les points chauds (hot spots) du programme

    • point chaud: portion de code longue à exécuter

    • rapports sur les fonctions appelées, temps passé dans chaque fonction, …

Quelques outils

gprof (GNU Profiler), Valgrind, VisualVM.

A propos de l’optimisation prématurée

Warning
La phase d’optimisation ne doit intervenir qu’une fois que le programme fonctionne et répond aux spécifications fonctionnelles.
Quelques citations
  • More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity., W.A. Wulf

  • We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%., Donald Knuth

  • Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you have proven that’s where the bottleneck is., Rob Pike

  • The First Rule of Program Optimization: Don’t do it. The Second Rule of Program Optimization (for experts only!): Don’t do it yet., Michael A. Jackson

Optimisation à différents niveaux

  • Conception

    • algorithmes, architecture de l’application, …

  • Code source

    • utilisation d’idiomes adaptés au langage WARNING: attention de ne pas perturber les optimisations du compilateur

  • Compilateur

    • utiliser les optimisations fournies par le compilateur

  • Assembleur

    • spécifique à une plateforme

  • Exécution

    • compilateur just in time

Marche à suivre pour l’optimisation

  1. Choisir un paramètre à optimiser (temps CPU, occupation mémoire, …)

  2. Localiser les portions de code les plus coûteuses vis à vis de ce paramètre

    • permet d’obtenir le meilleur rendement

    • règle des 80/20 ([wikipedia w])

  3. Appliquer les optimisations puis mesurer le résultat