Génie Logiciel Cours 4: produire du bon code

Transcription

Génie Logiciel Cours 4: produire du bon code
Génie Logiciel
Cours 4: produire du bon code
David Baelde
[email protected]
MPRI
29 novembre 2013
Le problème
C’est quoi du bon code ?
Critères
Le problème
C’est quoi du bon code ?
Critères
I
Pas de crash, fuite mémoire, comportement non défini, etc.
I
Respect de la spécification fonctionnelle
Le problème
C’est quoi du bon code ?
Critères
I
Pas de crash, fuite mémoire, comportement non défini, etc.
I
Respect de la spécification fonctionnelle
I
Efficacité : temps, espace, réactivité
I
Facilité à corriger et adapter
Le problème
C’est quoi du bon code ?
Critères
I
Pas de crash, fuite mémoire, comportement non défini, etc.
I
Respect de la spécification fonctionnelle
I
Efficacité : temps, espace, réactivité
I
Facilité à corriger et adapter
Debugging is twice as hard as writing the code in the
first place. Therefore, if you write the code as cleverly as
possible, you are, by definition, not smart enough to
debug it. — Brian Kernighan
Différentes pistes
Méthodologie
I
Limiter le couplage, favoriser la purité.
I
Tester systématiquement, d’abord en isolation.
Différentes pistes
Méthodologie
I
Limiter le couplage, favoriser la purité.
I
Tester systématiquement, d’abord en isolation.
I
Relire et faire relire le code, systématiquement.
Différentes pistes
Méthodologie
I
Limiter le couplage, favoriser la purité.
I
Tester systématiquement, d’abord en isolation.
I
Relire et faire relire le code, systématiquement.
I
Conseils généraux du type “réfléchissez”. . .
Différentes pistes
Méthodologie
I
Limiter le couplage, favoriser la purité.
I
Tester systématiquement, d’abord en isolation.
I
Relire et faire relire le code, systématiquement.
I
Conseils généraux du type “réfléchissez”. . .
Aujourd’hui
Quelques outils et techniques utilisables en pratique :
1. Garanties statiques
2. Outillage de l’exécution
Garanties statiques
Éviter le pire
Sans faire tourner le code
on peut/doit déja faire plein de choses
Choisir un langage discipliné
I
Déclarations de variables : éviter les typos
I
Typage statique : garantir des invariants simple
plus de types
plus d’invariants exprimables
I
Utiliser des énumérations plutôt que des nombres magiques !
Éviter le pire
Sans faire tourner le code
on peut/doit déja faire plein de choses
Choisir un langage discipliné
I
Déclarations de variables : éviter les typos
I
Typage statique : garantir des invariants simple
plus de types
plus d’invariants exprimables
I
Utiliser des énumérations plutôt que des nombres magiques !
Profiter au maximum du compilateur
Même sur un langage avec typage fort et statique,
le compilateur n’est pas forcément très contraignant par défaut.
Recommandé en C++ :
-pedantic, -Wall, -Werror, -Wextra, etc.
En plus du compilateur
Deux outils faciles à utiliser
I
I
Lint, e.g., cpplint :
vérifier le respect de conventions et bonnes pratiques (Google)
Recherche d’erreurs simples communes : memory leaks,
unchecked error, trivial test, library misuse, etc.
I
I
cppcheck (cf. page Foung bugs sur leur wiki)
Clang static analyzer, PVS studio, etc.
En préparant ce cours, avec ces outils et grep j’ai trouvé des bugs
dans GTA et trois logiciels à moi (C++ et OCaml)
Méthodes formelles
De la science. . .
Model-checking, vérification déductive, interprétation abstraite,
génération de code certifié, etc.
. . . aux outils mûrs
I
I
Why, Frama-C, etc.
Chez Microsoft
I
I
Outils MSR : SLAM, Boogie, Z3. . .
Tout code écrit passe par des outils de vérification.
. . . pas forcément complets ni corrects !
I
I
I
correct : remonte tous les bugs (et peut-être de faux positifs)
complet : remonte de vrais bugs (mais pas forcément tous)
Sparse : check annotations in the Linux kernel
Et si on ne peut pas / ne sait pas utiliser ces outils ?
Programmation par contrats
Une “métaphore” pour la logique de Hoare :
pre-conditions, post-conditions, invariants, etc.
Une méthodologie de conception : design by contract
Support
I
Dans le langage : Eiffel, SpeC#
I
Extension (commentaires) : JML
Outils
I
(Preuve de programme)
I
Génération de documentation
I
Génération de tests unitaires
I
Vérifications à runtime
Outillage de l’exécution
Assertions
À défaut de formellement prouver la spécification,
on peut souvent l’exécuter.
I
Détection d’anomalies au plus tôt.
I
Une forme de commentaire actif.
Assertions
À défaut de formellement prouver la spécification,
on peut souvent l’exécuter.
I
Détection d’anomalies au plus tôt.
I
Une forme de commentaire actif.
La fonction assert
Prend un booléen et lève une erreur s’il est faux.
let add ?(needs_check=true) x rules kb =
assert (needs_check || not (mem_equiv x kb)) ;
if not (needs_check && mem_equiv x kb) then
add (fresh_statement x) kb
Souvent prédefinie, avec un moyen d’effacer l’assertion :
ocamlc -noassert ..., g++ -DNDEBUG ..., etc.
Assertions — Bon usage
Impératifs
I
Si assert lève une exception, attention à ne pas la récupérer
let main () =
try ... with _ -> eprintf "Oops!\n" ; main ()
I
L’effacement des assertion ne doit pas affecter
le comportement du programme
Assertions — Bon usage
Impératifs
I
Si assert lève une exception, attention à ne pas la récupérer
let main () =
try ... with _ -> eprintf "Oops!\n" ; main ()
I
L’effacement des assertion ne doit pas affecter
le comportement du programme
Au cas par cas
I
Quand est-ce qu’une assertion est trop coûteuse ?
Attention à l’optimisation prématurée, souvent non justifiée
Envisager un système d’assertions multi-niveaux
I
Faut-il distribuer un logiciel avec les assertions désactivées ?
Plutôt non : profiter au maximum de tests précis.
Envisager de les changer en gros avertissements non fatals.
Autres outillages
Malgré tous ces efforts, il restera toujours des bugs.
Une fois qu’on a un bug, il faut :
reproduire ; comprendre et minimiser ; corriger.
Tracer
Un système de log enrichit le traditionnel debug au printf :
I
Disponible chez l’utilisateur, configurable sans recompiler
I
Sauvegarde dans un fichier à joindre aux rapports de bug
I
Messages datés, taggés, avec indication de priorité :
erreur, avertissement, information, debug
I
Penser aux infos utiles au debug : version, plugins, plateforme
Si les logs deviennent ingérables, inventer son propre outil :
analyse statistique, graphiques, graphes, inspection à chaud, etc.
Conclusion
À retenir
I
Ecrire du code facile à comprendre et à adapter
I
Systématiquement éviter les erreurs simples
I
Provoquer les bugs, le plus tôt possible
Séances suivantes
I
Test
I
Débuguage et d’analyse de l’exécution