12
votes

GCOV et déclarations de commutation

Je suis en cours d'exécution GCOV sur un code C avec un relevé de commutateur. J'ai écrit des cas de test pour couvrir toutes les voies possibles grâce à cet énoncé de commutation, mais cela signale toujours une branche dans la déclaration de commutation comme non prise et moins de 100% sur la statistique "prise au moins une fois".

Voici quelques échantillons code pour démontrer: xxx

i construit avec " gcc.c -fprofile-arcs -freads-couverture ", a ran " a ", puis" gcov -b -c temp.c.c ". La sortie indique huit branches sur le commutateur et une (branche 6) non prise.

Quelles sont toutes ces branches et comment puis-je obtenir une couverture de 100%?


1 commentaires

Les contenus du fichier .gcda sont-ils utiles du tout?


4 Réponses :


1
votes

Êtes-vous sûr de courir a.out? Voici mes résultats (GCC 4.4.1):

File 't.c'
Lines executed:100.00% of 11
Branches executed:100.00% of 6
Taken at least once:100.00% of 6
Calls executed:100.00% of 5
t.c:creating 't.c.gcov'


3 commentaires

Il a dit Ran "A" , lequel pour moi suggère qu'il a exécuté A.exe et utilise Windows.


Je suppose - par défaut, j'associe GCC avec UNIX. Les résultats ont semblé consistant à exécuter une version différente de l'exécutable par accident.


Oui, j'utilise Mingw sur Windows, qui est GCC 3.4.5. Cela pourrait-il être quelque chose qui est fixé dans des versions plus récentes de GCC?



2
votes

Je reçois le même résultat à l'aide de GCC / GCOV 3.4.6.

Pour une instruction de commutation, il devrait normalement générer deux branches pour chaque déclaration de cas. L'un est si le cas est vrai et doit être exécuté, et l'autre est une branche "castthrough" qui passe au cas suivant. p>

Dans votre situation, il semble que GCC fait une branche "casquette" pour le dernier cas, ce qui n'a pas de sens, car il n'y a rien à tomber dans. P>

Voici un Extrait du code de montage généré par GCC (j'ai modifié certaines des étiquettes pour la lisibilité): P>

    cmpl    $2, -4(%ebp)
    je  CASE2
    cmpl    $2, -4(%ebp)
    jg  L7
    cmpl    $1, -4(%ebp)
    je  CASE1
    addl    $1, LPBX1+16
    adcl    $0, LPBX1+20
    jmp DEFAULT
L7:
    cmpl    $3, -4(%ebp)
    je  CASE3
    addl    $1, LPBX1+32
    adcl    $0, LPBX1+36
    jmp DEFAULT


7 commentaires

Je doute que GCov fait une succursale en cas de franchise; Il semble plus susceptible d'être GCC le faire. Que se passe-t-il si vous activez l'optimisation?


Il n'y a rien de mal à avoir une casquette pour par défaut : après tout ce qu'il pourrait y avoir un cas ci-dessous pour éviter d'exécuter le code spécifique à par défaut . Ce qui ne va pas, c'est avoir une cassette contre la dernière déclaration du commutateur, car il n'y a rien à tomber dans.


@Brooks activant l'optimisation ne change pas le problème et on dirait que si j'utilise -O3 ou au-dessus, il ajoute plus de branches.


@Matthieu je suis d'accord, cela a plus de sens. J'ai édité ma réponse pour refléter cela.


@BDE: Ah, eh bien. Voilà pour cette idée!


@Brooks: c'était une bonne idée. J'ai posté un extrait du code de montage dans ma réponse, peut-être que quelqu'un qui sait davantage sur GCC peut expliquer ce qui se passe.


@BDE: Merci! Cette décharge de l'assemblée semble effectivement expliquer les choses; Voir la réponse que j'ai posté.



4
votes

oho! Le décharge de l'Assemblée de BDE montre que cette version de GCC compilait cette instruction de commutation comme une approximation d'un arbre binaire, à partir du milieu de l'ensemble. Donc il vérifie si i est égal à 2, puis vérifie s'il est supérieur ou inférieur à 2, puis pour chaque côté, il vérifie s'il est égal à 1 ou 3 respectivement, et sinon, alors il va. à défaut.

Cela signifie qu'il existe deux chemins de code différents pour atteindre le résultat par défaut - un pour les chiffres supérieurs à 2 qui ne l'en ont pas 3 et un pour les nombres inférieurs à 2 ne sont pas 1.

On dirait que vous obtiendrez une couverture à 100% si vous modifiez ce i <4 dans votre boucle sur i <= 4 , afin de tester le chemin chaque côté.

(et oui, c'est quelque chose qui est très susceptible d'avoir changé de GCC 3.x à GCC 4.x. Je ne dirais pas que c'est "réparé", car ce n'est pas "faux" exactement en dehors de la fabrication des résultats GCOV déroutant. C'est juste que sur un processeur moderne avec une prédiction de succursale, il est probablement lent et trop compliqué.)


0 commentaires

0
votes

J'utilise MINGW sur Windows (qui n'est pas le dernier GCC) et on dirait que cela peut être trié dans des versions plus récentes de GCC.


0 commentaires