Bienvenidos sean a este post, hoy veremos uno de los posibles analisis a realizar para depurar nuestro codigo..
Analisis dinamico es una abreviacion de Analisis de Programa Dinaimico, este analiza la performance de un programa ejecutandolo en un procesador real o virtual. De manera similar a analisis estatico, del cual hablamos en el post anterior, tambien se puede correr de manera manual o automatica. Por ejemplo, los test de unidades, test de integracion, tests de sistema, y test de aceptacion son tipicos procesos de analisis dinamico donde intervienen humanos. Y por otro lado, la depuracion de memoria, deteccion de fugas de memoria, y herramientas de profiling tales como IBM Purify, Vangrid y los sanitizadores de Clang son herramientas de analisis dinamico automaticos. Un proceso de analisis dinamico contiene pasos tales como:
- Preparar los datos de entrada
- Lanzar un programa de test
- Reunir los parametros necesarios
- Analizar su salida
Con esto podemos decir que el mecanismo de las herramientas de analisis dinamico es el uso de instrumentacion del codigo o simulacion de entorno para realizar chequeos en el codigo analizado mientras se ejecuta. Nuestra interaccion con el programa puede ser la siguiente:
- Instrumentacion del codigo fuente: se inserta un bloque de codigo especial en el codigo fuente antes de compilarlo
- Instrumentacion del codigo del objeto: un codigo binario especial es agregado en el ejecutable
- Instrumentacion durante la compilacion: mediante opciones especiales del compilador se agrega un codigo de chequeo
- No se modifica el codigo fuente y en su lugar se hace una ejecucion especial con ciertas librerias para detectar errores
Pasemos a ver algunas ventajas de trabajar con analisis dinamico:
- No habra falsos positivos ni falsos negativos porque los errores seran detectados y no predichos desde un modelo
- No es necesario el codigo fuente, esto permite que el codigo propietario pueda ser analizado por un tercero
Ahora pasemos a ver sus desventajas:
- Encuentra defectos solo en la ruta de los datos de entrada, otros defectos por fuera de estos pueden no ser encontrados
- Solo puede chequear un path de ejecucion por vez, esto nos lleva a que debe correrse varias veces para obtener una foto completa y esto incrementa la cantidad de recursos para realizarlo
- No puede chequear la exactitud de un codigo, y nos puede llevar a obtener el resultado correcto pero de una operacion erronea
- Ejecutar codigo incorrecto en un procesador real puede llevar a resultados inesperados
Todo muy lindo en la teoria pero ahora pasemos a la practica, para ello vamos a utilizar a valgrind, en caso de no tenerlo deben ejecutar el siguiente comando en linux basados en debian:
$ sudo apt-get update
Esta es una buena practica antes de instalar cualquier elemento en el S.O, como es actualizar los repositorios. Con esto realizado podemos pasar a instalarlo con el siguiente comando:
$ sudo apt-get install valgrind
Una vez instalado nuestra aplicacion, vamos a crear un nuevo archivo con el siguiente codigo:
#include <iostream>
int main()
{
int n=10;
float *p = (float *)malloc(n * sizeof(float));
for( int i=0; i<n; ++i){
std::cout << p[i] << std::endl;
}
return 0;
}
Un codigo simple para ubicar datos en memoria mediante un puntero pero que nunca lo liberamos. Si lo compilan normalmente o con alguna de las opciones que vimos en el post anterior, no devolvera ningun error porque el problema no influye en la logica del codigo. Por lo tanto, este codigo pasa el analisis estatico pero que sucede con el analisis dinamico? Veamos que sucede mediante el siguiente comando:
$ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=log.txt ./depurar
Observen que tenemos dos opciones, las primeras, encargadas de verificar todo lo referido a las fugas de memoria (leaks), que haga un rastreo de los origenes de estos inconvenientes, que lo muestre todo (verbose) pero que lo guarde en un archivo. Para finalmente, ejecutar el programa sin necesidad del codigo fuente. Veamos como es la salida de este comando:
$ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=log.txt ./depurar
0
0
0
0
0
0
0
0
0
0
$
Como pueden ver lo ejecuto correctamente sin ninguna intervencion o interferencia pero que sucedio con el archivo? Veamos una porcion del contenido generado:
--23528-- REDIR: 0x4b18e20 (libc.so.6:free) redirected to 0x4843110 (free)
==23528==
==23528== HEAP SUMMARY:
==23528== in use at exit: 40 bytes in 1 blocks
==23528== total heap usage: 3 allocs, 2 frees, 73,768 bytes allocated
==23528==
==23528== Searching for pointers to 1 not-freed blocks
==23528== Checked 147,136 bytes
==23528==
==23528== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==23528== at 0x48407B4: malloc (vg_replace_malloc.c:381)
==23528== by 0x109198: main (in /home/tinchicus/lenguajes/cpp/07/depurar)
==23528==
==23528== LEAK SUMMARY:
==23528== definitely lost: 40 bytes in 1 blocks
==23528== indirectly lost: 0 bytes in 0 blocks
==23528== possibly lost: 0 bytes in 0 blocks
==23528== still reachable: 0 bytes in 0 blocks
==23528== suppressed: 0 bytes in 0 blocks
==23528==
==23528== ERROR SUMMARY: 606 errors from 63 contexts (suppressed: 0 from 0)
==23528==
Vamos a tener un monton de informacion antes y despues de esto pero para este caso vamos a centrarnos en esta segmento de datos. Observen que se indica que malloc fue llamado para asignar memoria en 0x48407B4. A su vez, en el heap summary nos indica que hay 40 bytes perdidos en esa direccion de memoria.. Lo mismo ocurre en el segmento leak summary donde tambien indica que se perdio un bloque de 40 bytes de memoria. Si buscamos la direccion de memoria mas algunas secciones mas del registro nos daremos cuenta que esto ocurre porque no los liberamos. Tomemos el codigo y hagamos el siguiente cambio:
#include <iostream>
int main()
{
int n=10;
float *p = (float *)malloc(n * sizeof(float));
for( int i=0; i<n; ++i){
std::cout << p[i] << std::endl;
}
free(p);
return 0;
}
Simplemente agregamos a la funcion free para que libere el puntero antes de terminar. Compilen nuevamente y ejecuten a valgrind con las mismas opciones pero en el nuevo ejecutable. Si ingresan al log y buscan a la seccion de heap summary tendran lo siguiente:
--23616-- REDIR: 0x4b18e20 (libc.so.6:free) redirected to 0x4843110 (free)
==23616==
==23616== HEAP SUMMARY:
==23616== in use at exit: 0 bytes in 0 blocks
==23616== total heap usage: 3 allocs, 3 frees, 73,768 bytes allocated
==23616==
==23616== All heap blocks were freed -- no leaks are possible
==23616==
Observen que no nos hace ninguna notificacion y a su vez desaparecio leak summary. Con esto podemos concluir que con la ayuda de analisis dinamico en conjunto con el estatico y la depuracion podemos mejorar considerablemente nuestros codigos. Sin embargo, siempre debe estar involucrado un humano aunque sea para supervisar todo… por el momento porque con el avance de las IA puede quedar obsoleto hasta el mismisimo humano.😱
En resumen, hoy hemos visto analisis dinamico, que es, para que sirve, algunas ventajas, algunas desventajas, como este no reemplaza a analisis estatico sino que lo complementa, asi como tambien un ejemplo para verlo en accion y como nos puede ayudar. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
