Adloun

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.

Sur d'autres machines, le même programme s'arrête brutalement sur un signal.

ImportantTrois leçons, et la troisième est la plus dure
  • 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 -O0 ne 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.