En el post que escribí anteriormente sobre el tema, veíamos una introducción al manejo de GDB con un programa de un único fichero.
En esta entrega vamos a indagar un poco más y vamos a ver cómo se trabaja con diferentes ficheros, y algunos comandos adicionales que nos harán la vida más sencilla, y que nos olvidemos definitivamente de «depurar con printfs«.
Un comentario inicial, ¿Por qué depurar?
El desarrollo de software es un proceso que no se libra de riesgos y fallos, y es habitual, sobre todo cuando se está empezando a aprender a programar, que los pequeños ejecutables que creemos, no funcionen como esperamos (Fallos de segmentación, cuelgues, variables que dan un resultado erróneo …).
Por esta razón el depurador, o debugger, es una pieza de software que debemos dominar si queremos ser más productivos y encontrar antes los fallos en nuestros programas.Este artículo, y el anterior, se refieren a una herramienta bastante básica, pero potente y sencilla para aprender los conceptos clave.
Existen entornos integrados de desarrollo (IDE) como Netbeans, Eclipse, o Visual Studio, que cuentan con sus propias herramientas de depuración, pero todas se basan el los conceptos mencionados aquí.
Los comandos
Si vienes de la entrega anterior habrás visto que usamos dos comandos básicos:
- break, para provocar puntos de ruptura y detener la ejecución de nuestro programa en una línea concreta
- print, para mostrar los valores de una variable.
En esta entrega agregamos los siguientes:
- continue, nos permite continuar la ejecución de nuestro programa desde el punto en el que habíamos escrito el break
- step, nos permite ejecutar la línea siguiente, en caso de ser una llamada a una función, se ejecutará la primera línea de la función llamada, es decir, cambia al ámbito de dicha función.
- next, nos permite ejecutar la línea siguiente, pero a diferencia de la anterior, no «entra» en las funciones, sino que se mantiene en el mismo ámbito, de tal manera que si la siguiente línea fuese una llamada a una función, nos permite comprobar directamente, el resultado.
Múltiples ficheros
Antes explicabamos lo sencillo que resultaba parar la ejecución en una línea concreta, pero cuando tenemos múltiples ficheros, lo interesante es poder depurar cualquier linea de cualquier fichero disponible.
break file.c:22
En el ejemplo anterior file.c es el fichero que queremos depurar y 22 la línea, si forma parte de los ficheros de nuestro programa y la línea es correcta GDB nos aceptará la instrucción, y listo, ya podemos trabajar con múltiples ficheros.
Ejemplo
Para mostraros el uso de los comandos que he comentado anteriormente, y el manejo de ficheros, vamos a tener tres ficheros fuente independientes, uno de ellos el main.c:
#include <stdio.h> #include "aux.h" int main(){ int entrada, salida; entrada = 2; salida = operacionCompleja(entrada); }
El otro será aux.h, que contendrá la cabecera de la función:
int operacionCompleja(int numero);
Finalmente, aux.c contendrá la función que queremos llamar:
#include "aux.h" int aux(int numero){ int intermedio = 3; intermedio = intermedio * numero; return intermedio; } int operacionCompleja(int numero){ int intermedio; intermedio = 3 * numero + 12 - 1; intermedio = aux(intermedio); return intermedio * 2; }
Lo primero es compilarlo, para lo cual nos vamos a una consola de comandos y escribimos:
gcc -ggdb main.c aux.c -o programa
Lo primero que vamos a probar va a ser la depuración de ficheros, para lo cual cargamos el depurador con gdb programa, y una vez dentro, introducimos nuestro punto de ruptura:
(gdb) break aux.c:11 Punto de interrupción 1 at 0x80483da: file aux.c, line 11.
Si ejecutamos el programa, mediante el comando run, como esperabamos, se detendrá en la linea 5:
Breakpoint 1, operacionCompleja (numero=2) at aux.c:11 11 intermedio = 3 * numero + 12 - 1;
Si quisieramos seguir con la ejecución, podemos escribir next, y se nos mostrará la línea siguiente, facil, eh?:
12 return intermedio * 2;
Veamos para finalizar la diferencia entre next y step, situandonos en esta línea, si ejecutamos step, entrará dentro de la función aux, y continuará la ejecución desde ese punto
(gdb) step aux (numero=17) at aux.c:4 4 int intermedio = 3;
Por el contrario, si ejecutamos next, satará la instrucción, y tendremos disponible el resultado del cálculo.
(gdb) next 13 return intermedio * 2;
Conclusiones
Espero que os haya resultado interesante, y que sirva como introducción al manejo de GDB, si necesitas más información, en los enlaces de abajo tienes un documento muy completo, además de la primera parte de este artículo.
Más información
Introduction to GDB, por el profesor Daniel Spiegel
Tutorial rápido de GDB, entrega anterior de esta serie.