Cent pour cent des instructions, quatre-vingt-trois pour cent des arcs
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 4 — Discipline de programmation, validation et test
Énoncé
Mesurer la couverture réelle du jeu de tests sur ces deux fonctions, avec gcc –coverage. Puis exécuter f(-1).
int classe(int x) {
int r;
if (x < 0) { r = -1; }
else if (x == 0) { r = 0; }
else { r = 1; }
return r;
}
int f(int x) {
int y = 0;
if (x > 0) { y = 1; } /* pas de else */
return 10 / y;
}Corrigé
La mesure, avec gcc –coverage puis llvm-cov gcov -b :
Lines executed: 100.00% of 13
Taken at least once: 83.33% of 6
1: 11: if (x > 0) { y = 1; } /* pas de else */
branch 0 taken 100%
branch 1 taken 0% <-- l'arc jamais emprunte
Toutes les instructions sont exécutées, et un arc sur six ne l'est pas : celui qui saute le corps du if, c'est-à-dire exactement celui qui laisse à zéro. Le chiffre n'est pas une approximation : l'outil compte six arcs, et cinq ont été pris.
Ajouter f(-1) au jeu de tests porte les deux mesures à .
Et l'exécution ? C'est ici que l'exercice devient inquiétant. Le même programme, sur la même entrée, avec deux niveaux d'optimisation :
gcc -O0 : f(-1) = 0 code de sortie 0
gcc -O2 : f(-1) = 10 code de sortie 0
Deux réponses différentes, et aucun plantage. La division entière par zéro est un comportement indéfini en C : le langage n'exige rien du tout, et deux compilations du même texte peuvent légitimement diverger.
- À
-O0sur ce processeur, l'instruction de division rend sans lever d'exception matérielle. - À
-O2, l'optimiseur raisonne : «10 / yest évaluée, donc , donc leifa été pris, donc , donc le résultat est ». Il élimine la branche et écrit la constante.
Sur d'autres machines, le même programme s'arrête brutalement sur un signal.
- La couverture des sommets ne vaut rien seule. des instructions, et le défaut n'est pas atteint. On vise les arcs, au minimum.
- Un comportement indéfini ne se manifeste pas forcément par un plantage. Il peut rendre une valeur plausible, différente d'une compilation à l'autre. C'est pire qu'un arrêt : cela passe les tests.
- Un test « qui passe » sous
-O0ne dit rien de la version livrée, compilée en-O2. C'est la raison d'être de-fsanitize=undefined, qui, lui, arrête le programme à la ligne fautive quel que soit le niveau d'optimisation.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.