2010-05-11 10 views
12

Ich führe gcov über einige C-Code mit einer switch-Anweisung. Ich habe Testfälle geschrieben, um jeden möglichen Pfad durch diese switch-Anweisung abzudecken, aber es meldet immer noch eine Verzweigung in der switch-Anweisung als nicht genommen und weniger als 100% in der Statistik "Mindestens einmal genommen".gcov und switch statements

Hier einige Code-Beispiel zu demonstrieren:

#include "stdio.h" 

void foo(int i) 
{ 
    switch(i) 
    { 
     case 1:printf("a\n");break; 
     case 2:printf("b\n");break; 
     case 3:printf("c\n");break; 
     default: printf("other\n"); 
    } 
} 

int main() 
{ 
    int i; 
    for(i=0;i<4;++i) 
     foo(i); 
    return 0; 
} 

Ich baute mit "gcc temp.c -fprofile-arcs -ftest-coverage" ran "a", tat dann "gcov -b -c temp.c". Der Ausgang zeigt acht Zweige auf dem Schalter an und ein Zweig (Zweig 6) ist nicht belegt.

Was sind all diese Zweige und wie bekomme ich 100% Abdeckung?

+0

Ist der Inhalt der .gcda-Datei überhaupt hilfreich? – Cascabel

Antwort

0

Ich benutze Mingw auf Windows (das ist nicht das neueste gcc) und es sieht so aus in neueren Versionen von gcc aussortiert.

1

Sind Sie sicher, dass Sie a.out ausführen? Hier ist meine Ergebnisse (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' 
+0

er sagte 'ran" a "', was mir vorschlägt, dass er 'a.exe' ausgeführt hat und Windows verwendet. – nategoose

+0

Ich nehme an - standardmäßig verknüpfe ich GCC mit Unix. Die Ergebnisse sahen konsistent aus, dass versehentlich eine andere Version der ausführbaren Datei ausgeführt wurde. – ergosys

+0

Ja, ich verwende MinGW unter Windows, das ist gcc 3.4.5. Könnte das etwas sein, das in neueren Versionen von gcc behoben wurde? – Matt

2

ich das gleiche Ergebnis mit gcc/gcov 3.4.6 bekommen.

Für eine switch-Anweisung sollte normalerweise zwei Zweige für jede case-Anweisung generiert werden. Einer ist, wenn der Fall wahr ist und ausgeführt werden sollte, und der andere ist ein "Fallthrough" -Zweig, der zum nächsten Fall weitergeht.

In Ihrer Situation sieht es so aus, als ob gcc für den letzten Fall einen "Fallthrough" -Abzweig macht, was keinen Sinn ergibt, da nichts darin enthalten ist.

Hier ist ein Auszug aus der Code-Zusammenstellung von gcc erzeugt (I einige der Etiketten zur besseren Lesbarkeit geändert):

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 

Ich gebe zu, dass ich über x86-Assembler nicht viel wissen, und ich weiß nicht verstehe die Verwendung des L7-Labels, aber es könnte etwas mit der zusätzlichen Verzweigung zu tun haben. Vielleicht kann jemand mit mehr Wissen über gcc erklären, was hier vor sich geht.

Es hört sich an, als könnte es ein Problem mit der älteren Version von gcc/gcov sein, ein Upgrade auf einen neueren gcc/gcov könnte das Problem beheben, besonders angesichts des anderen Posts, wo die Ergebnisse korrekt aussehen.

+0

Ich bezweifle, dass gcov eine Fallthrough-Branche ist; es sieht wahrscheinlicher aus, dass gcc es tut. Was passiert, wenn Sie die Optimierung aktivieren? –

+0

Es ist nichts falsch daran, einen Fallthrough für 'default' zu haben: Schließlich könnte es unten einen 'case' geben, um zu vermeiden, dass der für' default' spezifische Code ausgeführt wird. Was falsch ist, ist ein Fallthrough für die letzte Anweisung des Switches, da es nichts gibt, in das man hineinfallen könnte. –

+0

@Brooks Das Aktivieren der Optimierung ändert das Problem nicht, und es sieht so aus, als ob ich -O3 oder höher dazu verwende, weitere Zweige hinzuzufügen. – WildCrustacean

4

Oho! Der Assembly-Dump von bde zeigt, dass diese Version von GCC diese switch-Anweisung als eine Annäherung an einen binären Baum kompiliert, beginnend in der Mitte des Satzes. So prüft es, ob i gleich 2 ist, prüft dann, ob es größer oder kleiner als 2 ist, und prüft dann für jede Seite, ob es gleich 1 oder 3 ist, und wenn nicht, dann geht es auf Standard.

Das heißt, es sind zwei verschiedene Codepfade für die es auf den Standard Ergebnis zu erhalten - eine für Zahlen von mehr als 2, die nicht mehr als 3 und eine für Zahlen von weniger als 2, die nicht 1

sind

Es sieht so aus, als ob Sie eine 100% ige Abdeckung erreichen, wenn Sie i<4 in Ihrer Schleife zu i<=4 ändern, um den Pfad auf jeder Seite zu testen.

(Und, ja, das ist etwas, das sich sehr wahrscheinlich von GCC 3.x zu GCC 4.x geändert hat. Ich würde nicht sagen, es ist "fixed", da es nicht genau falsch ist, abgesehen von der gcov Ergebnisse sind verwirrend, es ist nur so, dass es auf einem modernen Prozessor mit Verzweigungsvorhersage wahrscheinlich langsam und übermäßig kompliziert ist.