gentoo linux, java, software libre y otras hierbas
mar, 02 2007 - 5:51 pm

Depuración de programas en Linux

Todo buen entorno de desarrollo debe proporcionar la capacidad de depurar nuestros programas, por ello Linux posee una herramienta llamada gdb. gdb es una excelente herramienta de depuración con interfaz de línea de comandos (modo texto). Aunque existen herramientas como gdbtui que es una versión del mismo depurador con una interfaz más amigable (pero en modo texto también). Y si sos de los que les da pereza la consola, tranquilo, también existe una herramienta llamada xxgdb que es una versión del gdb con interfaz gráfica que se ejecuta en X Windows.

gdb

gdb le permite analizar el funcionamiento de un programa paso a paso, establecer puntos de interrupción (breakpoints), examinar y modificar variables por su nombre. Se puede utilizar tanto en programas de C como de C++. Si has manejado alguna vez depuradores y debuggers como el OllyDbg, el manejo de este se te hará realmente fácil.

Para preparar un programa para su depuración, es necesario compilarlo con la opción -g. Esto hace que en alguna parte del ejecutable, se guarde información del código fuente tal como es. De otra forma no prodrás usar el depurador de forma óptima. Si estás utilizando el make para crear los programas, puedes indicar esta opción en la variable CFLAGS (¿la recuerdas?), desde la línea de comandos así:

make CFLAGS=-g

Cuando hayas creado el programa, puedes comenzar la sesión de depuración con el comando: gdb dado. (Suponiendo que el ejecutable se llama dado):

GNU gdb 6.1-debian
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “i386-linux”…
Using host libthread_db library “/lib/libthread_db.so.1″.
(gdb)

Estamos frente al prompt del gdb, que se representa con los caracteres (gdb), indicándonos que podemos escribir los comandos a ejecutar. gdb tiene muchos comando disponibles, para verlos digita el siguiente comando:

(gdb) help

Como puedes observar, el comando help despliega la lista de clases de comandos existentes en el gdb, para ver las instrucciones de determinada clase de comandos, digita help seguido del nombre del comando, por ejemplo así:

(gdb) help breakpoints

Por último para salir del gdb presiona la tecla q. A continuación veremos una tabla con comandos más útiles para el gdb:

Comando Función
break [nombrearhivo:]función Establecer un punto de interrupción en la entrada a la función del archivo llamado nombrearchivo.
run [listaargumentos] Iniciar el programa, y pasarle argumentos si es necesario.
bt Desplegar la pila del programa.
print expre Evaluar la expresión e imprimir el resultado. La expresión puede ser un nombre de variable o una función que retorne un valor.
c
cont
Cualquiera de estos dos comandos, continúan con la ejecución del programa desde le punto actual.
next Ejecutar la siguiente línea de l programa. Si la siguiente línea es una función, dicha función se ejecutará y el programa se detendrá en la siguiente línea después de la llamada a la función.
step Ejecutar la siguiente línea del programa, y entrar a la función si esa es una línea de función.
help [nombre] Mostrar la ayuda general, o mostrar la ayuda específica a [nombre] si se indico uno.
q salir del gdb.

Sesión de ejemplo de depuración con gdb

Ahora vamos a ver un ejemplo de como trabaja esta herramienta. Habiendo creado ya el programa (el de los dados) con la opción -g así:

g++ -g -o dados *.c

Pasamos a correr el depurador:

gdb dados

Con lo que aparece lo siguiente:

GNU gdb 6.1-debian
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “i386-linux”…Using host libthread_db library “/lib/libthread_db.so.1″.

(gdb)

Vamos entonces a ver el código fuente de nuestro programa principal (el que contiene la función main), esto lo hacemos con el comando list y le indicamos de que línea a que línea queremos ver, en este caso de la 1 a la 25:

(gdb) list 1,25
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4
5 int tirarDado(void);
6
7 int main(int argc, char * argv[])
8 {
9 int i, iIter, dado[6];
10
11 if(argc<2)
12 {
13 printf(“Uso: %s n\n”,argv[0]);
14 return 1;
15 }
16 iIter = atoi(argv[1]);
17 memset(dado, 0, sizeof(dado));
18 for(i=0; i<iIter; i++)
19 {
20 dado[tirarDado() - 1]++;
21 }
22 printf(“%d tiradas\n”,iIter);
23 printf(“\tCara\tTiradas\n”);
24 for(i=0; i<6; i++)
25 {

Ahora establecemos un punto de interrupción (breakpoint), en la línea 16 (iIter = atoi(argv[1]);), que hará que le programa se detenga en este punto al correrlo:

(gdb) break 16
Breakpoint 1 at 0x804850b: file juego.c, line 16.

Corremos el programa con el comando run seguido de los argumentos:

(gdb) run 5
Starting program: /mnt/hda2/archivos/programas C/juego/dados 5
Breakpoint 1, main (argc=2, argv=0xbffffd24) at juego.c:16
16 iIter = atoi(argv[1]);
Current language: auto; currently c++

Y vemos como se ejecuta, y posteriormente para en la línea 16. Nos muestra el nombre de la función en donde nos encontramos (en este caso main), y nos muestra el trozo de código fuente. Ahora veamos el valor de la variable iIter, utilizando el comando print iIter. Es de resaltar que utilizamos el nombre real de la variable para referirnos a esta, y que al estar sobre la línea en el cual se asigna el valor a la variable, el valor que nos imprime es el de la variable sin inicializar:

(gdb) print iIter
$1 = -1073742556

Veamos entonces que pasa si saltamos a la siguiente instrucción, y volvemos a imprimir el valor de iIter:

(gdb) next
17 memset(dado, 0, sizeof(dado));
(gdb) print iIter
$2 = 5

En este caso si se imprime el valor actual de la variable iIter (5). Avancemos hasta la línea 20, y observemos que podemos utilizar print para imprimir el valor de retorno de una función:

(gdb) next
18 for(i=0; i<iIter; i++)
(gdb) next
20 dado[tirarDado() - 1]++;
(gdb) print tirarDado()
$3 = 2

Ahora observemos que al estar aún dentro del for, al terminar la línea 20 el gdb nos devuelve automáticamente hasta la línea 18 (que es donde se evalua la condición del for):

(gdb) next
18 for(i=0; i<iIter; i++)
(gdb) next
20 dado[tirarDado() - 1]++;

Ahora continuemos con la ejecución normal del programa y salgamos del gdb, para ello digitamos el comando cont y después q:

(gdb) cont
Continuing.
5 tiradas
Cara Tiradas
1 : 0
2 : 2
3 : 0
4 : 1
5 : 1
6 : 1
Program exited normally.
(gdb) q

Eso es todo!!! Fácil ¿no? para un manejo avanzado del gdb leer la documentación del programa localmente (comando man) o en Internet.

Sin comentarios | deja el tuyo

¡Déjanos tu comentario!