miércoles, 30 de marzo de 2016

Herramientas mágicas: Gcov

Hoy retomo la serie de "herramientas mágicas" para presentarles GCov. Es una herramienta que yo al principio ni conocía, ni tampoco conocía ninguna de su tipo. Alguien alguna vez en el foro me sugirió que la integre en ZinjaI, y lo hice (de una forma bastante básica) solo porque me costaba poco tomando la integración previa de GProf como base. Pero luego de eso, con el tiempo le fui encontrando usos, empezando por uno no tan obvio y, recientemente, para lo que usualmente se supone que es.

El "Cov" de GCov viene de coverage. Es una herramienta para analizar cobertura. El "analizar" es a modo de profiler. Es decir, se debe ejecutar el programa, y la información se genera durante la ejecución. Lo de "cobertura" es a nivel de código, más precisamente de líneas de código fuente. La herramienta sirve para ver cuáles líneas efectivamente se ejecutaron y cuáles no. La idea es detectar partes del código que nunca se ejecutan durante una prueba, de modo de mejorar la prueba para asegurarse de cubrir todos los casos.


GCov funciona de forma similar a GProf (¿qué? ¿nunca hablé de GProf??? ya vendrá un post al respecto). Es decir, que requiere ayuda del compilador para instrumentar el ejecutable, y durante la ejecución genera un archivo con la información recopilada, que luego podremos reportar con gcov.exe. ZinjaI tiene una opción en el menú herramientas para activarlo (agregar el argumento y recompilar todo) y ver el resultado. "El resultado" consiste en un contador por cada línea de código diciendo cuantas veces se ejecutó dicha línea. En la imagen se pueden ver los contadores en el margen izquierdo (los números más altos se acercan al rojo, mientras que losmás bajos al verde). Hay lineas con '######' en lugar de un número; esas son las líneas que sí generaron código ejecutable (a diferencia de las que no tienen nada) pero que nunca se usó durante las corridas.


Mi uso reciente de GCov viene de la mano de PSeInt. Hace poco hice unos cuantos cambios dentro del intérprete para simplificar el código. Había uno o dos cambios grandes, y cientos de cambios menores. Para animarme a modificar tanto, necesitaba tener mucha confianza en mis tests automáticos (en que si un cambio rompía algo, los tests lo iban a delatar). Por eso utilicé GCov para analizar qué partes del código no se probaban nunca con mi batería de tests. Al ir detectando estas partes, fui agregando nuevos tests, hasta llegar a un punto en que el conjunto de tests pasa al menos una vez por cada linea importante. Por ejemplo, si veo gracias a GCov que nunca se ejecuta una linea que informa sobre cierto error en el pseudocódigo, agrego un nuevo test con un algoritmo que tenga ese error, para asegurarme de que si cambio algo, el error se siga detectando correctamente.


El otro uso que le encontré tiene que ver con cuestiones de eficiencia. Uso GCov como una ayuda para hacer algo como PGO pero a mano. La idea es analizar cómo me conviene reordenar mi algoritmo para que la ejecución sea más rápida (a nivel pasos del algoritmo, ya que a nivel asm lo hace el compilador).

Supongamos, por ejemplo, que tengo una función para detectar colisiones entre dos objetos de un juego. Encontrar el punto exacto de colisión, matemáticamente, requiere una cuantas cuentas. Pero cuando las colisiones no son muy frecuentes, hay otros cálculos más baratos que se pueden probar antes para descartar situaciones comunes sin necesidad de completar todas esas cuentas (por ejemplo, comparar los bounding-boxes). Si en mi código tengo varios de estos criterios, puedo analizar en qué orden me conviene ponerlos para que queden primeros los que más descartes generan. En el código, cada criterio será un "if". El contador de la línea de cada "if" me dice cuántas veces se utilizó ese criterio en una corrida, y el de la siguiente cuántas veces sirvió para algo. Entonces, mirando estos contadores tengo información muy detallada sobre cuánto ayuda cada criterio en cada posición.


Finalmente, hay otras herramientas libres dando vueltas por la red para generar otras visualizaciones de la información que genera GCov, como por ejemplo LCov. Siempre pueden complementar lo que ofrece ZinjaI con alguna otra cosa como esta, y hacerla accesible desde el IDE como herramienta personalizada.


Algo que me sorprendió gratamente es que la merma en la velocidad de ejecución debido a la instrumentación que agrega GCov es sorprendentemente baja en relación a la cantidad de información que recopila. Me da detalles por línea (o sea, muy finos), y comparando con otra, como por ejemplo GProf, el impacto en el tiempo de ejecución se siente muchísimo menos. En conclusión, es una herramienta muy interesante para tener en la caja por si las moscas.

No hay comentarios:

Publicar un comentario