2011-08-14 9 views
7

Estoy tratando de entender el resultado de la herramienta gcov. Ejecutarlo sin opciones tiene sentido, pero quiero tratar de entender las opciones de cobertura de la sucursal. Desafortunadamente es difícil entender qué hacen las ramas y por qué no se toman. A continuación se muestra el resultado de un método (compilar utilizando la última versión de LLVM/Clang).Entender ramas en archivos gcov

function -[TestCoverageAppDelegate loopThroughArray:] called 5 returned 100% blocks executed 88% 
     5: 30:- (NSInteger)loopThroughArray:(NSArray *)array { 
     5: 31: NSInteger i = 0; 
     22: 32: for (NSString *string in array) { 
branch 0 taken 0 
branch 1 taken 7 
     -: 33:   
     22: 34: } 
branch 0 taken 4 
branch 1 taken 3 
branch 2 taken 0 
branch 3 taken 3 
     5: 35: return i; 
     -: 36:} 

me he encontrado 5 de prueba a través de este, pasando en nil, una matriz vacía, una matriz con 1 objeto, y la matriz con 2 objetos y una matriz con 4 objetos. Puedo adivinar que en el primer caso, la rama 1 significa "entrar en el ciclo" pero no tengo idea de qué rama es 0. En el segundo caso, la rama 0 parece volver a pasar, la rama 1 parece terminar el ciclo y la rama 3 continuar/salir del ciclo, pero no tengo idea de qué rama es 2 o por qué/cuándo se ejecutará.

Si alguien sabe cómo descifrar la información de la sucursal, o sabe de cualquier documentación detallada sobre lo que significa, agradecería la ayuda.

+0

Intente obtener un conjunto de su función y verifique el número de instrucciones 'j **'. – osgx

+0

Mi ensamblaje no es muy bueno, pero parece que hay 4. El primero es un documento que creo que se salta el ciclo si no hay objetos para enumerar. Luego otro je que omite una excepción de mutación de enumeración, una jb de la que no tengo idea pero que vuelve al principio del ciclo y luego una jne que creo que se mueve al principio del ciclo si quedan objetos por enumerar. Curiosamente, la mutación causa que la primera rama 0 tome, lo que resuelve un misterio, pero la rama 2 todavía me escapa –

+0

Hmm. Otro caso posible es desmontar el archivo objeto, que se compiló con '-pg' (para gcov en ejecución). Debería ver las llamadas a algunas funciones de instrumentos de gcov en dicho desensamblaje ... como el incremento "__llvm_gcov_ctr" o la llamada "__llvm_gcda_edge". Además, compiló en 'O0' con' -fno-inline'? – osgx

Respuesta

3

Gcov funciona mediante la instrumentación (durante la compilación) de cada bloque básico de comandos de la máquina (puede pensar en el ensamblador). Basic block significa una sección lineal de código, que no tiene ramas dentro y ninguna etiqueta dentro de él. Entonces, si y solo si comienzas a ejecutar un bloque básico, alcanzarás el final del bloque básico. Los bloques básicos están organizados en CFG (gráfico de flujo de control, piénselo como gráfico dirigido), que muestra las relaciones entre bloques básicos (el flanco de V1 a V2 es V1 llama a V2 y V2 es llamado por V1). Entonces, el modo de compilador profile-arcs y gcov quieren obtener el conteo de ejecución para cada línea y hacer esto contando las ejecuciones de bloque básicas. Algunos de los bordes en CFG están instrumentados y otros no, porque hay relaciones algebraicas entre bloques básicos en el gráfico.

Su construcción ObjC (for..in) se reduce (se convierte en compilación anticipada) a varios bloques básicos. Entonces, gcov ve 4 ramas, porque solo ve BB reducidas. No sabe nada sobre este descenso, pero sabe qué línea corresponde a cada instrucción de ensamblador (esta es información de depuración). Entonces, las ramas son bordes de CFG.

Si desea ver bloques básicos, debe hacer un volcado de ensamblador del programa compilado o desensamblar un CFG binario o volcado del compilador. Puede hacer esto para los modos profile-arcs y no profile-arcs y compararlos.

profile-arcs modo tendrá muchas llamadas e incrementos de algo así como "__llvm_gcov_ctr" o "__llvm_gcda_edge" - es una instrumentación real de bloques básicos.