Difference between revisions of "Ipol:Modulo3"
(→Valgrind) |
(→Profiler) |
||
(26 intermediate revisions by 3 users not shown) | |||
Line 24: | Line 24: | ||
En el [[ipol:Modulo1|Módulo 1]] se realiza una primera aproximación a <tt>makefile</tt>. Reutilizaremos ese ejemplo para ver más detalles de esta herramienta. | En el [[ipol:Modulo1|Módulo 1]] se realiza una primera aproximación a <tt>makefile</tt>. Reutilizaremos ese ejemplo para ver más detalles de esta herramienta. | ||
Dado <tt>makefile</tt> utilizado: | Dado <tt>makefile</tt> utilizado: | ||
− | :< | + | :<source lang="bash"> |
quinto : quinto.o auxiliares.o | quinto : quinto.o auxiliares.o | ||
gcc -c auxiliares.c | gcc -c auxiliares.c | ||
gcc -c quinto.c | gcc -c quinto.c | ||
gcc auxiliares.o quinto.o -o quinto | gcc auxiliares.o quinto.o -o quinto | ||
− | </ | + | </source> |
# Escribir nuevamente las dependencias para que solamente se compile lo modificado desde la última compilación. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile2 makefile2]). | # Escribir nuevamente las dependencias para que solamente se compile lo modificado desde la última compilación. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile2 makefile2]). | ||
# Utilizar los comandos <tt>$@</tt> y <tt>$^</tt> para modificar el <tt>makefile</tt> anterior, de modo de evitar repetir el nombre de los archivos fuente. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile3 makefile3]). | # Utilizar los comandos <tt>$@</tt> y <tt>$^</tt> para modificar el <tt>makefile</tt> anterior, de modo de evitar repetir el nombre de los archivos fuente. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile3 makefile3]). | ||
# Utilizar la declaración de una variable que incluya todos los objetivos de las compilaciones sin enlazar (archivos <tt>.o</tt>), y carácteres especiales como <tt>$</tt> y <tt>%</tt> para escribir de manera reducida el <tt>makefile</tt>. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile4 makefile4]). | # Utilizar la declaración de una variable que incluya todos los objetivos de las compilaciones sin enlazar (archivos <tt>.o</tt>), y carácteres especiales como <tt>$</tt> y <tt>%</tt> para escribir de manera reducida el <tt>makefile</tt>. (Solución en [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile4 makefile4]). | ||
: Nota: para compilar utilizando un <tt>makefile</tt> con otro nombre, debe utilizarse la opción <tt>-f</tt> de <tt>make</tt>, por ejemplo: | : Nota: para compilar utilizando un <tt>makefile</tt> con otro nombre, debe utilizarse la opción <tt>-f</tt> de <tt>make</tt>, por ejemplo: | ||
− | :< | + | :<source lang="bash"> |
> make -f makefile2 | > make -f makefile2 | ||
− | </ | + | </source> |
: Archivos: [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile2 makefile2] [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile3 makefile3] [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile4 makefile4]. | : Archivos: [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile2 makefile2] [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile3 makefile3] [https://github.com/haldos/ipol/blob/master/curso_c/parte1/makefile4 makefile4]. | ||
Line 47: | Line 47: | ||
== Debugger == | == Debugger == | ||
− | Utilizaremos la herramienta <tt>ddd</tt> presentada en el [[ipol:Modulo2|Módulo 2]]. Se aplicará a | + | === Ejemplo 1 === |
+ | Utilizaremos la herramienta <tt>ddd</tt> presentada en el [[ipol:Modulo2|Módulo 2]]. Se aplicará a tres ejemplos de código existentes. | ||
# Para el primer ejemplo (<tt>primero.c</tt>), utilizar el <tt>ddd</tt> para ver como queda guardado en memoria una variable de tipo <tt>struct</tt>. Analizar también el puntero a un <tt>struct</tt> declarado en el programa. | # Para el primer ejemplo (<tt>primero.c</tt>), utilizar el <tt>ddd</tt> para ver como queda guardado en memoria una variable de tipo <tt>struct</tt>. Analizar también el puntero a un <tt>struct</tt> declarado en el programa. | ||
#: Código: | #: Código: | ||
− | #: < | + | #: <source lang="c"> |
#include <stdio.h> | #include <stdio.h> | ||
Line 70: | Line 71: | ||
return 0; | return 0; | ||
} | } | ||
− | </ | + | </source> |
#* Usando <tt>ddd</tt> se pueden ver las variables asociadas al programa de la siguiente manera: | #* Usando <tt>ddd</tt> se pueden ver las variables asociadas al programa de la siguiente manera: | ||
#:http://iie.fing.edu.uy/~haldos/ipol/images/dddgraph_primero.png | #:http://iie.fing.edu.uy/~haldos/ipol/images/dddgraph_primero.png | ||
+ | |||
+ | === Ejemplo 2 === | ||
# Para el segundo ejemplo (<tt>segundo.c</tt>), analizar como se guarda en memoria una lista encadenada. Ver la asignación de punteros en el ciclo <tt>for</tt> que imprime el valor de los elementos de la lista. | # Para el segundo ejemplo (<tt>segundo.c</tt>), analizar como se guarda en memoria una lista encadenada. Ver la asignación de punteros en el ciclo <tt>for</tt> que imprime el valor de los elementos de la lista. | ||
#: Código: | #: Código: | ||
− | #: < | + | #: <source lang="c"> |
#include <stdio.h> | #include <stdio.h> | ||
Line 101: | Line 104: | ||
return 0; | return 0; | ||
} | } | ||
− | </ | + | </source> |
#* Usando <tt>ddd</tt> se pueden ver las variables asociadas al programa de la siguiente manera: | #* Usando <tt>ddd</tt> se pueden ver las variables asociadas al programa de la siguiente manera: | ||
#:http://iie.fing.edu.uy/~haldos/ipol/images/dddgraph_segundo.png | #:http://iie.fing.edu.uy/~haldos/ipol/images/dddgraph_segundo.png | ||
Line 117: | Line 120: | ||
''Nota'': Recordar compilar los archivos utilizando la opción <tt>-g</tt> y luego ejecutar el <tt>ddd</tt> pasando como argumento el ejectuable generado. Por ejemplo: | ''Nota'': Recordar compilar los archivos utilizando la opción <tt>-g</tt> y luego ejecutar el <tt>ddd</tt> pasando como argumento el ejectuable generado. Por ejemplo: | ||
− | < | + | <source lang="bash"> |
> gcc -g primero.c -o primero | > gcc -g primero.c -o primero | ||
> ddd primero | > ddd primero | ||
− | </ | + | </source> |
+ | |||
+ | === Ejemplo 3 === | ||
+ | Considere el programa presentado en el ejemplo [https://github.com/haldos/ipol/blob/master/examples/debugger/tercero.c tercero.c] que se muestra a continuación | ||
+ | <source lang="c"> | ||
+ | persona* crear_persona(char* name, char sexo, int edad); | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | /// Puntero a un struct | ||
+ | persona *p1=crear_persona("Juan",'M',300); | ||
+ | |||
+ | //uso mediante puntero | ||
+ | int edad=p1->edad; | ||
+ | //printf("edad: %d\n",edad); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </source> | ||
+ | El programa crea un struct persona igual que en el ejemplo 2, pero con una función que devuelve un puntero al struct creado. Esto es un procedimiento estándar en C. | ||
+ | # Compilar el programa sin información de debug, y ejecutar. Se recomienda hacerlo desde el archivo tercero.c y no copiado desde el wiki. | ||
+ | # Descomentar la línea del printf. Deducir en qué línea ocurre el error y determinar la causa del mismo. Debería ser posible hacerse sin mirar la implementación de la función crear_persona() | ||
+ | # Utilizar el debugger para deducir la causa del error, en caso de no poder hacerlo de otra forma. | ||
+ | === Referencias === | ||
: Archivos: [https://github.com/haldos/ipol/blob/master/examples/debugger/primero.c primero.c] [https://github.com/haldos/ipol/blob/master/examples/debugger/segundo.c segundo.c]. | : Archivos: [https://github.com/haldos/ipol/blob/master/examples/debugger/primero.c primero.c] [https://github.com/haldos/ipol/blob/master/examples/debugger/segundo.c segundo.c]. | ||
− | == Profiler == | + | == Profiler de uso de memoria: Valgrind == |
=== Valgrind === | === Valgrind === | ||
Line 136: | Line 162: | ||
''Valgrind'' incluye además otras herramientas como ''Cachegrind'', que mide el rendimiento de la caché durante la ejecución, de acuerdo a sus características (capacidad, tamaño del bloque de datos, grado de asociatividad, etc.). | ''Valgrind'' incluye además otras herramientas como ''Cachegrind'', que mide el rendimiento de la caché durante la ejecución, de acuerdo a sus características (capacidad, tamaño del bloque de datos, grado de asociatividad, etc.). | ||
− | # Un error común es intentar acceder a elementos de un array fuera de sus límites (ejemplo en <tt>primero.c</tt>). Veamos como se ve este error en ''Valgrind'': | + | ==== Ejemplo 1 ==== |
− | #:< | + | # Un error común es intentar acceder a elementos de un array fuera de sus límites (ejemplo en <tt>primero.c</tt>). |
+ | #* Al ejecutar el programa se obtiene la siguiente salida: | ||
+ | #:<source lang="bash"> | ||
+ | > ./primero | ||
+ | *** glibc detected *** ./primero: free(): invalid next size (fast): 0x0000000001edc010 *** | ||
+ | primero: malloc.c:2451: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed. | ||
+ | Aborted (core dumped) | ||
+ | </source> | ||
+ | #* Normalmente uno esperaría alguna especie de queja del sistema operativo (si lo hay) por acceder a memoria no reservada. Esto ocurre siempre y cuando la posición accedida no este dentro de la memoria asignada a este programa. Sin embargo en este caso, el programa da un error relacionado con el free. | ||
+ | #* la solución intuititiva es eliminar el free(), sin embargo, estaríamos dejando de liberar la memoria que pedimos y eso es un problema. Esto es un típico caso de errores oscuros de tiempo de ejecución, donde el problema aparece en un lugar poco relacionado con la causa real del problema. Cambiar el programa y verificarlo. | ||
+ | #* Para comprender lo que ocurre hay que ver bien en detalle como funcionan el malloc y el free. Si observan, cuando uno hace malloc, solicita una cierta cantidad de bytes. Sin embargo cuando se hace free() no se especifica. La pregunta que surge entonces es: como sabé free cuantos bytes liberar? | ||
+ | #* Cuando se hace malloc de n bytes, se reservan esos bytes más una cierta cantidad de bytes que contienen información alternativa. Esta información entre otros contiene el tamaño almacenado. Normalmente esta información se almacena de forma contigua, antes o después de los datos, dependiendo del compilador y del sistema operativo. | ||
+ | #* lo que ocurre en este caso, es que la escritura fuera de lugar esta corrompiendo la información administrativa y por lo tanto el free no puede leerla correctamente. | ||
+ | #* Veamos como se ve este error en ''Valgrind'': | ||
+ | #:<source lang="bash"> | ||
> gcc -g primero.c -o primero | > gcc -g primero.c -o primero | ||
> valgrind ./primero | > valgrind ./primero | ||
Line 144: | Line 184: | ||
==5598== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info | ==5598== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info | ||
==5598== Command: ./primero | ==5598== Command: ./primero | ||
− | + | ==5598== Invalid write of size 4 | |
− | ==5598== Invalid write of size 4 | + | ==5598== at 0x8048434: main (primero.c:12) |
− | ==5598== | + | ==5598== Address 0x41a5050 is 0 bytes after a block of size 40 alloc'd |
− | ==5598== | + | ==5598== at 0x4025BD3: malloc (vg_replace_malloc.c:236) |
− | ==5598== | + | ==5598== by 0x8048408: main (primero.c:7) |
− | ==5598== | + | ==5598== |
− | ==5598== | + | ==5598== |
− | ==5598== | ||
==5598== HEAP SUMMARY: | ==5598== HEAP SUMMARY: | ||
==5598== in use at exit: 0 bytes in 0 blocks | ==5598== in use at exit: 0 bytes in 0 blocks | ||
Line 160: | Line 199: | ||
==5598== For counts of detected and suppressed errors, rerun with: -v | ==5598== For counts of detected and suppressed errors, rerun with: -v | ||
==5598== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 6) | ==5598== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 6) | ||
− | </ | + | </source> |
#* <tt>Invalid write of size 4</tt> marca el error (se intentó escribir un <tt>int</tt> fuera del lugar que asignó <tt>malloc</tt>). | #* <tt>Invalid write of size 4</tt> marca el error (se intentó escribir un <tt>int</tt> fuera del lugar que asignó <tt>malloc</tt>). | ||
#* <tt>at 0x8048434: main (primero.c:12)</tt> marca que el error se cometió en la línea 12 de código. | #* <tt>at 0x8048434: main (primero.c:12)</tt> marca que el error se cometió en la línea 12 de código. | ||
#* También puede verse que se hizo un <tt>malloc</tt> y un <tt>free</tt>, con lo que no hay ''leaks''. | #* También puede verse que se hizo un <tt>malloc</tt> y un <tt>free</tt>, con lo que no hay ''leaks''. | ||
+ | ==== Ejemplo 2 ==== | ||
# Otro error común es olvidarse de inicializar una variable o un array antes de usarlo. Investigar el código implementado en <tt>segundo.c</tt> y utilizar ''Valgrind'' para encontrar los errores de no inicialización de variables. | # Otro error común es olvidarse de inicializar una variable o un array antes de usarlo. Investigar el código implementado en <tt>segundo.c</tt> y utilizar ''Valgrind'' para encontrar los errores de no inicialización de variables. | ||
#: ''Nota'': probar el funcionamiento de la opción <tt>--track-origins=yes</tt> de ''Valgrind''. En ese caso se ejecutaría: | #: ''Nota'': probar el funcionamiento de la opción <tt>--track-origins=yes</tt> de ''Valgrind''. En ese caso se ejecutaría: | ||
− | #:: < | + | #:: <source lang="bash"> |
> valgrind --track-origins=yes ./segundo | > valgrind --track-origins=yes ./segundo | ||
− | </ | + | </source> |
+ | ==== Ejemplo 3 ==== | ||
# ''Valgrind'' también sirve para buscar ''memory leaks'', o huecos de memoria que dejan los programas (memoria que se pide pero no se libera). Analizar entonces el código implementado en <tt>tercero.c</tt>, compilarlo, y utilizar ''Valgrind'' con la opción <tt>--leak-check=full</tt> para ver un informe del uso de memoria del programa. Encontrar los ''memory leaks''. | # ''Valgrind'' también sirve para buscar ''memory leaks'', o huecos de memoria que dejan los programas (memoria que se pide pero no se libera). Analizar entonces el código implementado en <tt>tercero.c</tt>, compilarlo, y utilizar ''Valgrind'' con la opción <tt>--leak-check=full</tt> para ver un informe del uso de memoria del programa. Encontrar los ''memory leaks''. | ||
− | #: < | + | #: <source lang='bash'> |
> valgrind --leak-check=full ./tercero | > valgrind --leak-check=full ./tercero | ||
==3714== Memcheck, a memory error detector | ==3714== Memcheck, a memory error detector | ||
Line 195: | Line 236: | ||
==3714== For counts of detected and suppressed errors, rerun with: -v | ==3714== For counts of detected and suppressed errors, rerun with: -v | ||
==3714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 13 from 8) | ==3714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 13 from 8) | ||
− | </ | + | </source> |
#:''Nota'': ver el ''HEAP SUMMARY''. | #:''Nota'': ver el ''HEAP SUMMARY''. | ||
− | # Otro ejemplo un poco más complejo de ''memory leaks'' puede encontrarse en el ejemplo <tt>cuarto.c</tt>. Al igual que en el caso anterior, compilar este programa y utilizar ''Valgrind'' con la opción <tt>--leak-check=full</tt> para ver un informe del uso de memoria del programa. | + | |
+ | ==== Ejemplo 4 ==== | ||
+ | # Otro ejemplo un poco más complejo de ''memory leaks'' puede encontrarse en el ejemplo <tt>cuarto.c</tt>. Al igual que en el caso anterior, compilar este programa y utilizar ''Valgrind'' con la opción <tt>--leak-check=full</tt> para ver un informe del uso de memoria del programa. Encontrar los ''memory leaks''. | ||
+ | |||
+ | ==== Ejemplo 5 ==== | ||
# ''Valgrind'' no sirve en casos de memoria estática, por ejemplo cuando se define un array de tamaño fijo: <tt>int a[10];</tt>. Para ver esto, compilar el archivo <tt>quinto.c</tt>, ejecutarlo, y encontrar el error que se produce. Luego usar ''Valgrind'' para analizar el funcionamiento del programa y ver que la herramienta no encuentra errores. | # ''Valgrind'' no sirve en casos de memoria estática, por ejemplo cuando se define un array de tamaño fijo: <tt>int a[10];</tt>. Para ver esto, compilar el archivo <tt>quinto.c</tt>, ejecutarlo, y encontrar el error que se produce. Luego usar ''Valgrind'' para analizar el funcionamiento del programa y ver que la herramienta no encuentra errores. | ||
− | |||
− | Tutorial sobre ''Valgrind'': http://lsi.ugr.es/~jagomez/sisopi_archivos/Valgrind.htm. | + | ==== Referencias ==== |
+ | * Archivos de los ejemplos: [https://github.com/haldos/ipol/blob/master/examples/valgrind/primero.c primero.c] [https://github.com/haldos/ipol/blob/master/examples/valgrind/segundo.c segundo.c] [https://github.com/haldos/ipol/blob/master/examples/valgrind/tercero.c tercero.c] [https://github.com/haldos/ipol/blob/master/examples/valgrind/cuarto.c cuarto.c] [https://github.com/haldos/ipol/blob/master/examples/valgrind/quinto.c quinto.c]. | ||
+ | *Tutorial sobre ''Valgrind'': http://lsi.ugr.es/~jagomez/sisopi_archivos/Valgrind.htm. | ||
+ | *Otro tutorial sobre ''Valgrind'': http://pages.cs.wisc.edu/~bart/537/valgrind.html. | ||
− | + | === Profiler de uso de CPU: Callgrind+KCachegrind === | |
− | |||
− | === KCachegrind === | ||
''KCachegrind'' es una herramienta de visualización de los datos de profiling generados por ''Cachegrind'' y ''Calltree ''. ''Callgrind'' es parte de ''Valgrind'' (observe que antes se denominaba ''Calltree'', pero el nombre podía inducir a error). Vamos a utilizar esta herramienta para estudiar el funcionamiento del FIR implementado en el [[ipol:Modulo3|Módulo 3]]. | ''KCachegrind'' es una herramienta de visualización de los datos de profiling generados por ''Cachegrind'' y ''Calltree ''. ''Callgrind'' es parte de ''Valgrind'' (observe que antes se denominaba ''Calltree'', pero el nombre podía inducir a error). Vamos a utilizar esta herramienta para estudiar el funcionamiento del FIR implementado en el [[ipol:Modulo3|Módulo 3]]. | ||
# Para utilizar ''KCachegrind'', primero se debe generar el archivo <tt>callgrind.out.pid</tt> utilizando la opción <tt>--tool=callgrind</tt> del comando <tt>valgrind</tt>: | # Para utilizar ''KCachegrind'', primero se debe generar el archivo <tt>callgrind.out.pid</tt> utilizando la opción <tt>--tool=callgrind</tt> del comando <tt>valgrind</tt>: | ||
− | #:< | + | #:<source lang="bash"> |
> valgrind --tool=callgrind ./test_fir | > valgrind --tool=callgrind ./test_fir | ||
==7546== Callgrind, a call-graph generating cache profiler | ==7546== Callgrind, a call-graph generating cache profiler | ||
Line 226: | Line 271: | ||
==7546== | ==7546== | ||
==7546== I refs: 161,006 | ==7546== I refs: 161,006 | ||
− | </ | + | </source> |
# Esto genera el archivo <tt>callgrind.out.pid</tt> que se debe cargar en ''KCachegrind''. | # Esto genera el archivo <tt>callgrind.out.pid</tt> que se debe cargar en ''KCachegrind''. | ||
# Abrir el ''KCachegrind'' y cargar el archivo mencionado. Va a aparecer una ventana similar a la siguiente: | # Abrir el ''KCachegrind'' y cargar el archivo mencionado. Va a aparecer una ventana similar a la siguiente: | ||
Line 243: | Line 288: | ||
== Documentación == | == Documentación == | ||
− | ''Doxygen'' es un generador de documentación para C y C++, entre otros lenguajes de programación. Para aplicar ''Doxygen'' utilizaremos el código del FIR implementado en el [[Modulo3|Módulo 3]]. | + | ''Doxygen'' es un generador de documentación para C y C++, entre otros lenguajes de programación. Para aplicar ''Doxygen'' utilizaremos primero un çódigo sencillo y luego el código del FIR implementado en el [[Modulo3|Módulo 3]]. |
* Generar el archivo de configuración de ''Doxygen'', llamado <tt>Doxyfile</tt>. Este archivo contiene todas las instrucciones para generar la documentación del proyecto. Se genera utilizando el comando: | * Generar el archivo de configuración de ''Doxygen'', llamado <tt>Doxyfile</tt>. Este archivo contiene todas las instrucciones para generar la documentación del proyecto. Se genera utilizando el comando: | ||
− | *: < | + | *: <source lang="bash"> |
> doxygen -g | > doxygen -g | ||
− | </ | + | </source> |
* Editar las variables necesarias en el archivo generado en el paso anterior. Algunas pueden ser: | * Editar las variables necesarias en el archivo generado en el paso anterior. Algunas pueden ser: | ||
− | *: < | + | *: <source lang=""> |
PROJECT_NAME = "Filtro FIR en C" | PROJECT_NAME = "Filtro FIR en C" | ||
PROJECT_NUMBER = 1.0 | PROJECT_NUMBER = 1.0 | ||
Line 261: | Line 306: | ||
HAVE_DOT = YES | HAVE_DOT = YES | ||
CALL_GRAPH = YES | CALL_GRAPH = YES | ||
− | </ | + | </source> |
* Comentar el código y utilizar el comando <tt>doxygen</tt> para generar automáticamente la documentación del proyecto. Inspeccionar dicha documentación generada. | * Comentar el código y utilizar el comando <tt>doxygen</tt> para generar automáticamente la documentación del proyecto. Inspeccionar dicha documentación generada. | ||
− | |||
− | |||
: Ejemplo de documentación generada: http://iie.fing.edu.uy/~haldos/ipol/doxygen_example/index.html. | : Ejemplo de documentación generada: http://iie.fing.edu.uy/~haldos/ipol/doxygen_example/index.html. | ||
Line 277: | Line 320: | ||
# Para el ejemplo utilizado en el ejercicio de <tt>makefile</tt>, generar el correspondiente archivo <tt>CMakeLists.txt</tt> y probar compilar el proyecto. | # Para el ejemplo utilizado en el ejercicio de <tt>makefile</tt>, generar el correspondiente archivo <tt>CMakeLists.txt</tt> y probar compilar el proyecto. | ||
#: Una vez creado el archivo <tt>CMakeLists.txt</tt> se deben correr dos comandos; <tt>cmake <directorio></tt> y <tt>make</tt>: | #: Una vez creado el archivo <tt>CMakeLists.txt</tt> se deben correr dos comandos; <tt>cmake <directorio></tt> y <tt>make</tt>: | ||
− | #: < | + | #: <source lang="bash"> |
> cmake . | > cmake . | ||
-- The C compiler identification is GNU | -- The C compiler identification is GNU | ||
Line 298: | Line 341: | ||
Linking C executable quinto | Linking C executable quinto | ||
[100%] Built target quinto | [100%] Built target quinto | ||
− | </ | + | </source> |
# Analizar el código que se implementa en los archivos <tt>a.c</tt>, <tt>b.c</tt>, <tt>c.c</tt>, <tt>libabc.h</tt> y <tt>demo.c</tt>. Generar un archivo <tt>CMakeLists.txt</tt> para compilar este programa utilizando <tt>cmake</tt>. | # Analizar el código que se implementa en los archivos <tt>a.c</tt>, <tt>b.c</tt>, <tt>c.c</tt>, <tt>libabc.h</tt> y <tt>demo.c</tt>. Generar un archivo <tt>CMakeLists.txt</tt> para compilar este programa utilizando <tt>cmake</tt>. | ||
## Compilar utilizando <tt>cmake</tt>. | ## Compilar utilizando <tt>cmake</tt>. | ||
##: Salida: | ##: Salida: | ||
− | ##: < | + | ##: <source lang="bash"> |
> cmake . | > cmake . | ||
-- The C compiler identification is GNU | -- The C compiler identification is GNU | ||
Line 325: | Line 368: | ||
Linking C executable demo | Linking C executable demo | ||
[100%] Built target demo | [100%] Built target demo | ||
− | </ | + | </source> |
## Cambiar la fecha de modificación (utilizando el comando <tt>touch</tt>) de alguno de los archivos (por ejemplo <tt>b.c</tt>), y volver a compilar utilizando <tt>cmake</tt>. | ## Cambiar la fecha de modificación (utilizando el comando <tt>touch</tt>) de alguno de los archivos (por ejemplo <tt>b.c</tt>), y volver a compilar utilizando <tt>cmake</tt>. | ||
##: Salida: | ##: Salida: | ||
− | ##: < | + | ##: <source lang="bash"> |
> touch b.c | > touch b.c | ||
> cmake . | > cmake . | ||
Line 339: | Line 382: | ||
Linking C executable demo | Linking C executable demo | ||
[100%] Built target demo | [100%] Built target demo | ||
− | </ | + | </source> |
##: ''Nota'': Observar que solamente se realiza la compilación de <tt>b.c</tt>, y luego el enlazado para obtener el ejecutable. | ##: ''Nota'': Observar que solamente se realiza la compilación de <tt>b.c</tt>, y luego el enlazado para obtener el ejecutable. | ||
#: Archivos: [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/a.c a.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/b.c b.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/c.c c.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/libabc.h libabc.h] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/demo.c demo.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/CMakeLists.txt CMakeLists.txt]. | #: Archivos: [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/a.c a.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/b.c b.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/c.c c.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/libabc.h libabc.h] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/demo.c demo.c] [https://github.com/haldos/ipol/blob/master/examples/cmake/primer/CMakeLists.txt CMakeLists.txt]. | ||
Line 345: | Line 388: | ||
#: ''Nota'': Considerar los comandos <tt>ADD_SUBDIRECTORY</tt> y <tt>ADD_LIBRARY</tt> de <tt>cmake</tt>. | #: ''Nota'': Considerar los comandos <tt>ADD_SUBDIRECTORY</tt> y <tt>ADD_LIBRARY</tt> de <tt>cmake</tt>. | ||
#: Salida: | #: Salida: | ||
− | #: < | + | #: <source lang="bash"> |
> cmake . | > cmake . | ||
-- The C compiler identification is GNU | -- The C compiler identification is GNU | ||
Line 371: | Line 414: | ||
Linking C executable demo | Linking C executable demo | ||
[100%] Built target demo | [100%] Built target demo | ||
− | </ | + | </source> |
#: Archivos: | #: Archivos: | ||
#:* En directorio raíz: [https://github.com/haldos/ipol/blob/master/examples/cmake/segundo/CMakeLists.txt CMakeLists.txt]. | #:* En directorio raíz: [https://github.com/haldos/ipol/blob/master/examples/cmake/segundo/CMakeLists.txt CMakeLists.txt]. | ||
Line 381: | Line 424: | ||
*Wiki de <tt>cmake</tt>: http://www.cmake.org/Wiki/CMake. | *Wiki de <tt>cmake</tt>: http://www.cmake.org/Wiki/CMake. | ||
*Otro tutorial de <tt>cmake</tt> (Daniel Molina Cabrera, 2009): http://www.uca.es/softwarelibre/publicaciones/cmake-material.pdf. | *Otro tutorial de <tt>cmake</tt> (Daniel Molina Cabrera, 2009): http://www.uca.es/softwarelibre/publicaciones/cmake-material.pdf. | ||
− | *Otro tutorial de <tt>cmake</tt> (Jan Engels, 2007): http://www-flc.desy.de/ldcoptimization/documents/talks/CMake_Tutorial.pdf | + | *Otro tutorial de <tt>cmake</tt> (Jan Engels, 2007): http://www-flc.desy.de/ldcoptimization/documents/talks/CMake_Tutorial.pdf. |
+ | *Using <tt>cmake</tt> for cross-platform compilation (IPOL): http://tools.ipol.im/wiki/author/code/using_cmake/. | ||
== Otro compilador: <strong><tt>clang</tt></strong> == | == Otro compilador: <strong><tt>clang</tt></strong> == | ||
Line 389: | Line 433: | ||
Por ejemplo, para compilar el FIR creado en el [[ipol:Modulo3|Módulo 3]] utilizando <tt>clang</tt>: | Por ejemplo, para compilar el FIR creado en el [[ipol:Modulo3|Módulo 3]] utilizando <tt>clang</tt>: | ||
− | :< | + | :<source lang='bash'> |
> clang -S fir.c | > clang -S fir.c | ||
> clang -S genera_senales.c | > clang -S genera_senales.c | ||
> clang -S test_fir.c | > clang -S test_fir.c | ||
> clang fir.s genera_senales.s test_fir.s -o test_fir | > clang fir.s genera_senales.s test_fir.s -o test_fir | ||
− | </ | + | </source> |
Un ejemplo de <tt>makefile</tt> utilizando <tt>clang</tt> para el caso anterior sería: | Un ejemplo de <tt>makefile</tt> utilizando <tt>clang</tt> para el caso anterior sería: | ||
− | :< | + | :<source lang='bash'> |
#!/bin/bash | #!/bin/bash | ||
Line 410: | Line 454: | ||
clean: | clean: | ||
rm -f $(OBJ) test_fir | rm -f $(OBJ) test_fir | ||
− | </ | + | </source> |
Página principal de <tt>clang</tt>: http://clang.llvm.org/. | Página principal de <tt>clang</tt>: http://clang.llvm.org/. | ||
+ | |||
+ | = Creación de una biblioteca = | ||
+ | referencias: | ||
+ | # http://crasseux.com/books/ctutorial/Building-a-library.html |
Latest revision as of 10:39, 21 September 2017
Contents
Entornos de desarrollo
Un entorno de desarrollo integrado (IDE - Integrated Development Environment) es un programa informático compuesto por un conjunto de herramientas de programación. Hasta ahora, no hemos mencionado ningún entorno de desarrollo particular, simplemente hemos editado los archivos con simples editores de texto, y hemos compilado directamente desde una terminal de UNIX utilizando gcc.
Un entorno de desarrollo, es más que un editor de texto. Contiene:
- Un editor de texto.
- Un compilador.
- Un intérprete.
- Un depurador.
- Un cliente.
- Posibilidad de ofrecer un sistema de control de versiones (e.g. svn, git).
- Factibilidad para ayuda en la construcción de interfaces gráficas de usuario (GUI).
Nota: se recomienda el uso del entorno de desarrollo NetBeans.
NetBeans
NetBeans (http://www.netbeans.org/) es un entorno de desarrollo integrado libre, hecho principalmente para el lenguaje de programación Java. Existe además un número importante de módulos para extenderlo. NetBeans IDE es un producto libre y gratuito, sin restricciones de uso. Si bien este entorno se usa principalmente para Java, existe una versión del IDE para C/C++. Más información: http://netbeans.org/features/cpp/index.html.
Makefile
En el Módulo 1 se realiza una primera aproximación a makefile. Reutilizaremos ese ejemplo para ver más detalles de esta herramienta. Dado makefile utilizado:
quinto : quinto.o auxiliares.o gcc -c auxiliares.c gcc -c quinto.c gcc auxiliares.o quinto.o -o quinto
- Escribir nuevamente las dependencias para que solamente se compile lo modificado desde la última compilación. (Solución en makefile2).
- Utilizar los comandos $@ y $^ para modificar el makefile anterior, de modo de evitar repetir el nombre de los archivos fuente. (Solución en makefile3).
- Utilizar la declaración de una variable que incluya todos los objetivos de las compilaciones sin enlazar (archivos .o), y carácteres especiales como $ y % para escribir de manera reducida el makefile. (Solución en makefile4).
- Nota: para compilar utilizando un makefile con otro nombre, debe utilizarse la opción -f de make, por ejemplo:
> make -f makefile2
- Archivos: makefile2 makefile3 makefile4.
Tutorial de makefile: http://www.gnu.org/software/make/manual/html_node/index.html.
Otro tutorial de makefile: http://iie.fing.edu.uy/~vagonbar/gcc-make/make.htm#ArchivosPrograma.
Adicional, un resumen de algunas herrmamientas de UNIX y C: http://iie.fing.edu.uy/ense/asign/str/curso-c/indice.htm.
Debugger
Ejemplo 1
Utilizaremos la herramienta ddd presentada en el Módulo 2. Se aplicará a tres ejemplos de código existentes.
- Para el primer ejemplo (primero.c), utilizar el ddd para ver como queda guardado en memoria una variable de tipo struct. Analizar también el puntero a un struct declarado en el programa.
- Código:
-
#include <stdio.h> int main(){ typedef struct { char *name; char sexo; int edad; } persona; persona p1; p1.name = "Juan"; p1.sexo = 'M'; p1.edad = 30; persona *pp1; pp1 = &p1; return 0; }
- Usando ddd se pueden ver las variables asociadas al programa de la siguiente manera:
Ejemplo 2
- Para el segundo ejemplo (segundo.c), analizar como se guarda en memoria una lista encadenada. Ver la asignación de punteros en el ciclo for que imprime el valor de los elementos de la lista.
- Código:
-
#include <stdio.h> struct elemento{ int valor; struct elemento * next; }; typedef struct elemento item; int main(){ item item1, item2, item3; item1.next = &item2; item1.valor = 10; item2.next = &item3; item2.valor = 20; item3.next = &item1; item3.valor = 25; int i; item * actual = &item1; for ( i=0 ; i<10 ; i++){ printf("Valor del elemento: %d.\n",actual->valor); actual = actual->next; } return 0; }
- Usando ddd se pueden ver las variables asociadas al programa de la siguiente manera:
- También pueden ponerse breakpoints dentro del ciclo for e inspeccionar el valor de las variables en cada paso de dicho ciclo (se muestran las imágenes desde i=0 hasta i=5):
i=0
i=1
i=2
i=3
i=4
i=5
Nota: Recordar compilar los archivos utilizando la opción -g y luego ejecutar el ddd pasando como argumento el ejectuable generado. Por ejemplo:
> gcc -g primero.c -o primero
> ddd primero
Ejemplo 3
Considere el programa presentado en el ejemplo tercero.c que se muestra a continuación
persona* crear_persona(char* name, char sexo, int edad);
int main()
{
/// Puntero a un struct
persona *p1=crear_persona("Juan",'M',300);
//uso mediante puntero
int edad=p1->edad;
//printf("edad: %d\n",edad);
return 0;
}
El programa crea un struct persona igual que en el ejemplo 2, pero con una función que devuelve un puntero al struct creado. Esto es un procedimiento estándar en C.
- Compilar el programa sin información de debug, y ejecutar. Se recomienda hacerlo desde el archivo tercero.c y no copiado desde el wiki.
- Descomentar la línea del printf. Deducir en qué línea ocurre el error y determinar la causa del mismo. Debería ser posible hacerse sin mirar la implementación de la función crear_persona()
- Utilizar el debugger para deducir la causa del error, en caso de no poder hacerlo de otra forma.
Referencias
Profiler de uso de memoria: Valgrind
Valgrind
Valgrind es un conjunto de herramientas libres que ayuda en la depuración de problemas de memoria y rendimiento de programas. La herramienta más usada es Memcheck. Memcheck introduce código de instrumentación en el programa a depurar, lo que le permite realizar un seguimiento del uso de la memoria y detectar los siguientes problemas:
- Uso de memoria no inicializada.
- Lectura/escritura de memoria que ha sido previamente liberada.
- Lectura/escritura fuera de los límites de bloques de memoria dinámica.
- Fugas de memoria.
- Otros.
Valgrind incluye además otras herramientas como Cachegrind, que mide el rendimiento de la caché durante la ejecución, de acuerdo a sus características (capacidad, tamaño del bloque de datos, grado de asociatividad, etc.).
Ejemplo 1
- Un error común es intentar acceder a elementos de un array fuera de sus límites (ejemplo en primero.c).
- Al ejecutar el programa se obtiene la siguiente salida:
> ./primero *** glibc detected *** ./primero: free(): invalid next size (fast): 0x0000000001edc010 *** primero: malloc.c:2451: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed. Aborted (core dumped)
- Normalmente uno esperaría alguna especie de queja del sistema operativo (si lo hay) por acceder a memoria no reservada. Esto ocurre siempre y cuando la posición accedida no este dentro de la memoria asignada a este programa. Sin embargo en este caso, el programa da un error relacionado con el free.
- la solución intuititiva es eliminar el free(), sin embargo, estaríamos dejando de liberar la memoria que pedimos y eso es un problema. Esto es un típico caso de errores oscuros de tiempo de ejecución, donde el problema aparece en un lugar poco relacionado con la causa real del problema. Cambiar el programa y verificarlo.
- Para comprender lo que ocurre hay que ver bien en detalle como funcionan el malloc y el free. Si observan, cuando uno hace malloc, solicita una cierta cantidad de bytes. Sin embargo cuando se hace free() no se especifica. La pregunta que surge entonces es: como sabé free cuantos bytes liberar?
- Cuando se hace malloc de n bytes, se reservan esos bytes más una cierta cantidad de bytes que contienen información alternativa. Esta información entre otros contiene el tamaño almacenado. Normalmente esta información se almacena de forma contigua, antes o después de los datos, dependiendo del compilador y del sistema operativo.
- lo que ocurre en este caso, es que la escritura fuera de lugar esta corrompiendo la información administrativa y por lo tanto el free no puede leerla correctamente.
- Veamos como se ve este error en Valgrind:
> gcc -g primero.c -o primero > valgrind ./primero ==5598== Memcheck, a memory error detector ==5598== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==5598== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==5598== Command: ./primero ==5598== Invalid write of size 4 ==5598== at 0x8048434: main (primero.c:12) ==5598== Address 0x41a5050 is 0 bytes after a block of size 40 alloc'd ==5598== at 0x4025BD3: malloc (vg_replace_malloc.c:236) ==5598== by 0x8048408: main (primero.c:7) ==5598== ==5598== ==5598== HEAP SUMMARY: ==5598== in use at exit: 0 bytes in 0 blocks ==5598== total heap usage: 1 allocs, 1 frees, 40 bytes allocated ==5598== ==5598== All heap blocks were freed -- no leaks are possible ==5598== ==5598== For counts of detected and suppressed errors, rerun with: -v ==5598== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 6)
- Invalid write of size 4 marca el error (se intentó escribir un int fuera del lugar que asignó malloc).
- at 0x8048434: main (primero.c:12) marca que el error se cometió en la línea 12 de código.
- También puede verse que se hizo un malloc y un free, con lo que no hay leaks.
Ejemplo 2
- Otro error común es olvidarse de inicializar una variable o un array antes de usarlo. Investigar el código implementado en segundo.c y utilizar Valgrind para encontrar los errores de no inicialización de variables.
- Nota: probar el funcionamiento de la opción --track-origins=yes de Valgrind. En ese caso se ejecutaría:
-
> valgrind --track-origins=yes ./segundo
-
- Nota: probar el funcionamiento de la opción --track-origins=yes de Valgrind. En ese caso se ejecutaría:
Ejemplo 3
- Valgrind también sirve para buscar memory leaks, o huecos de memoria que dejan los programas (memoria que se pide pero no se libera). Analizar entonces el código implementado en tercero.c, compilarlo, y utilizar Valgrind con la opción --leak-check=full para ver un informe del uso de memoria del programa. Encontrar los memory leaks.
-
> valgrind --leak-check=full ./tercero ==3714== Memcheck, a memory error detector ==3714== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==3714== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3714== Command: ./tercero ==3714== ==3714== ==3714== HEAP SUMMARY: ==3714== in use at exit: 400 bytes in 1 blocks ==3714== total heap usage: 2 allocs, 1 frees, 800 bytes allocated ==3714== ==3714== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==3714== at 0x4025BD3: malloc (vg_replace_malloc.c:236) ==3714== by 0x8048418: main (tercero.c:7) ==3714== ==3714== LEAK SUMMARY: ==3714== definitely lost: 400 bytes in 1 blocks ==3714== indirectly lost: 0 bytes in 0 blocks ==3714== possibly lost: 0 bytes in 0 blocks ==3714== still reachable: 0 bytes in 0 blocks ==3714== suppressed: 0 bytes in 0 blocks ==3714== ==3714== For counts of detected and suppressed errors, rerun with: -v ==3714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 13 from 8)
- Nota: ver el HEAP SUMMARY.
-
Ejemplo 4
- Otro ejemplo un poco más complejo de memory leaks puede encontrarse en el ejemplo cuarto.c. Al igual que en el caso anterior, compilar este programa y utilizar Valgrind con la opción --leak-check=full para ver un informe del uso de memoria del programa. Encontrar los memory leaks.
Ejemplo 5
- Valgrind no sirve en casos de memoria estática, por ejemplo cuando se define un array de tamaño fijo: int a[10];. Para ver esto, compilar el archivo quinto.c, ejecutarlo, y encontrar el error que se produce. Luego usar Valgrind para analizar el funcionamiento del programa y ver que la herramienta no encuentra errores.
Referencias
- Archivos de los ejemplos: primero.c segundo.c tercero.c cuarto.c quinto.c.
- Tutorial sobre Valgrind: http://lsi.ugr.es/~jagomez/sisopi_archivos/Valgrind.htm.
- Otro tutorial sobre Valgrind: http://pages.cs.wisc.edu/~bart/537/valgrind.html.
Profiler de uso de CPU: Callgrind+KCachegrind
KCachegrind es una herramienta de visualización de los datos de profiling generados por Cachegrind y Calltree . Callgrind es parte de Valgrind (observe que antes se denominaba Calltree, pero el nombre podía inducir a error). Vamos a utilizar esta herramienta para estudiar el funcionamiento del FIR implementado en el Módulo 3.
- Para utilizar KCachegrind, primero se debe generar el archivo callgrind.out.pid utilizando la opción --tool=callgrind del comando valgrind:
> valgrind --tool=callgrind ./test_fir ==7546== Callgrind, a call-graph generating cache profiler ==7546== Copyright (C) 2002-2010, and GNU GPL'd, by Josef Weidendorfer et al. ==7546== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==7546== Command: ./test_fir ==7546== ==7546== For interactive control, run 'callgrind_control -h'. Entrada: 0.000 0.000 0.000 0.000 0.000 1.000 1.000 1.000 1.000 1.000 Salida: 0.000 0.000 0.000 0.000 0.000 0.250 0.500 1.000 1.500 1.500 ==7546== ==7546== Events : Ir ==7546== Collected : 161006 ==7546== ==7546== I refs: 161,006
- Esto genera el archivo callgrind.out.pid que se debe cargar en KCachegrind.
- Abrir el KCachegrind y cargar el archivo mencionado. Va a aparecer una ventana similar a la siguiente:
- Estudiar las diferentes vistas del programa, como por ejemplo, la lista de funciones a las que se invoca durante la ejecución del programa, y el tiempo que estas consumen. Tambiién se puede ver un gráfico de la llamada de funciones durante la ejecución del programa en la pestaña Call Graph.
- Nota: En la lista de funciones invocadas (Flat Profile), si se utiliza la opción ELF Object para agrupar los objetos de la lista, podemos separar lo que son funciones de nuestro programa, de lo que son funciones del sistema operativo, y así ver solamente las funciones que se llaman dentro de nuestro programa. Ejemplo:
Página principal de KCachegrind: http://kcachegrind.sourceforge.net/html/Home.html.
Uso de KCachegrind: http://docs.kde.org/stable/es/kdesdk/kcachegrind/using-kcachegrind.html.
The KCachegrind Handbook: http://docs.kde.org/stable/en/kdesdk/kcachegrind/index.html.
Documentación
Doxygen es un generador de documentación para C y C++, entre otros lenguajes de programación. Para aplicar Doxygen utilizaremos primero un çódigo sencillo y luego el código del FIR implementado en el Módulo 3.
- Generar el archivo de configuración de Doxygen, llamado Doxyfile. Este archivo contiene todas las instrucciones para generar la documentación del proyecto. Se genera utilizando el comando:
-
> doxygen -g
-
- Editar las variables necesarias en el archivo generado en el paso anterior. Algunas pueden ser:
-
PROJECT_NAME = "Filtro FIR en C" PROJECT_NUMBER = 1.0 PROJECT_BRIEF = "Diseño de un filtro FIR en C." OUTPUT_DIRECTORY = doc/ OUTPUT_LANGUAGE = Spanish OPTIMIZE_OUTPUT_FOR_C = YES GENERATE_HTML = YES GENERATE_LATEX = YES HAVE_DOT = YES CALL_GRAPH = YES
-
- Comentar el código y utilizar el comando doxygen para generar automáticamente la documentación del proyecto. Inspeccionar dicha documentación generada.
- Ejemplo de documentación generada: http://iie.fing.edu.uy/~haldos/ipol/doxygen_example/index.html.
Documentación de Doxygen: http://www.stack.nl/~dimitri/doxygen/manual.html.
Comandos útiles de Doxygen: http://www.stack.nl/~dimitri/doxygen/commands.html.
Makefiles multiplataforma
CMake es una herramienta multiplataforma de generación o automatización de código. El nombre es una abreviatura para cross platform make (make multiplataforma). El proceso de construcción se controla creando uno o más ficheros CMakeLists.txt en cada directorio (incluyendo subdirectorios). Cada CMakeLists.txt consiste en uno o más comandos.
- Para el ejemplo utilizado en el ejercicio de makefile, generar el correspondiente archivo CMakeLists.txt y probar compilar el proyecto.
- Una vez creado el archivo CMakeLists.txt se deben correr dos comandos; cmake <directorio> y make:
-
> cmake . -- The C compiler identification is GNU -- The CXX compiler identification is GNU -- Check for working C compiler: /usr/bin/gcc -- Check for working C compiler: /usr/bin/gcc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/haldo/Documentos/Investigación/IPOL/ipol/curso_c/parte1 > make Scanning dependencies of target quinto [ 50%] Building C object CMakeFiles/quinto.dir/quinto.c.o [100%] Building C object CMakeFiles/quinto.dir/auxiliares.c.o Linking C executable quinto [100%] Built target quinto
- Analizar el código que se implementa en los archivos a.c, b.c, c.c, libabc.h y demo.c. Generar un archivo CMakeLists.txt para compilar este programa utilizando cmake.
- Compilar utilizando cmake.
- Salida:
-
> cmake . -- The C compiler identification is GNU -- The CXX compiler identification is GNU -- Check for working C compiler: /usr/bin/gcc -- Check for working C compiler: /usr/bin/gcc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/haldo/Documentos/IPOL/C/ipol/examples/cmake/primer > make Scanning dependencies of target demo [ 25%] Building C object CMakeFiles/demo.dir/demo.c.o [ 50%] Building C object CMakeFiles/demo.dir/a.c.o [ 75%] Building C object CMakeFiles/demo.dir/b.c.o [100%] Building C object CMakeFiles/demo.dir/c.c.o Linking C executable demo [100%] Built target demo
- Cambiar la fecha de modificación (utilizando el comando touch) de alguno de los archivos (por ejemplo b.c), y volver a compilar utilizando cmake.
- Salida:
-
> touch b.c > cmake . -- Configuring done -- Generating done -- Build files have been written to: /home/haldo/Documentos/IPOL/C/ipol/examples/cmake/primer > make Scanning dependencies of target demo [ 25%] Building C object CMakeFiles/demo.dir/b.c.o Linking C executable demo [100%] Built target demo
- Nota: Observar que solamente se realiza la compilación de b.c, y luego el enlazado para obtener el ejecutable.
- Compilar utilizando cmake.
- Nuevamente se utilizarán los archivos a.c, b.c, c.c, libabc.h y demo.c. Ahora mover los archivos a.c, b.c, c.c y libabc.h a un subdirectorio library y el archivo demo.c a otro subdirectorio demo. Generar entonces los tres archivos CMakeLists.txt necesarios (uno en el directorio raíz, y uno en cada subdirectorio creado). Compilar utilizando cmake.
- Nota: Considerar los comandos ADD_SUBDIRECTORY y ADD_LIBRARY de cmake.
- Salida:
-
> cmake . -- The C compiler identification is GNU -- The CXX compiler identification is GNU -- Check for working C compiler: /usr/bin/gcc -- Check for working C compiler: /usr/bin/gcc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/haldo/Documentos/IPOL/C/ipol/examples/cmake/segundo > make Scanning dependencies of target abc [ 25%] Building C object library/CMakeFiles/abc.dir/a.c.o [ 50%] Building C object library/CMakeFiles/abc.dir/b.c.o [ 75%] Building C object library/CMakeFiles/abc.dir/c.c.o Linking C shared library libabc.so [ 75%] Built target abc Scanning dependencies of target demo [100%] Building C object demo/CMakeFiles/demo.dir/demo.c.o Linking C executable demo [100%] Built target demo
- Archivos:
- En directorio raíz: CMakeLists.txt.
- En directorio demo: demo.c CMakeLists.txt.
- En directorio library: a.c b.c c.c libabc.h CMakeLists.txt.
Tutoriales
- Tutorial de cmake: http://www.cmake.org/cmake/help/cmake_tutorial.html.
- Wiki de cmake: http://www.cmake.org/Wiki/CMake.
- Otro tutorial de cmake (Daniel Molina Cabrera, 2009): http://www.uca.es/softwarelibre/publicaciones/cmake-material.pdf.
- Otro tutorial de cmake (Jan Engels, 2007): http://www-flc.desy.de/ldcoptimization/documents/talks/CMake_Tutorial.pdf.
- Using cmake for cross-platform compilation (IPOL): http://tools.ipol.im/wiki/author/code/using_cmake/.
Otro compilador: clang
Clang es un compilador libre para C y C++, que intenta ser una alternativa de gcc. Fue desarrollado y patrocinado por Apple, y funciona bajo la licencia free software licence. Para instalar clang (tanto en sistemas UNIX como en Windows) ver http://clang.llvm.org/get_started.html.
Por ejemplo, para compilar el FIR creado en el Módulo 3 utilizando clang:
> clang -S fir.c > clang -S genera_senales.c > clang -S test_fir.c > clang fir.s genera_senales.s test_fir.s -o test_fir
Un ejemplo de makefile utilizando clang para el caso anterior sería:
#!/bin/bash OBJ = fir.s genera_senales.s test_fir.s test_fir : $(OBJ) clang $(OBJ) -o $@ %.s: %.c clang -S $^ clean: rm -f $(OBJ) test_fir
Página principal de clang: http://clang.llvm.org/.
Creación de una biblioteca
referencias: