Lihuen
RSSRSS AtomAtom

Diferencia entre revisiones de «Cómo usar GDB»

Línea 12: Línea 12:
 
Una vez instalado el programa, ya está listo para funcionar. GDB funciona desde la terminal y si bien existen interfaces gráficas como ddd y xxgdb, el funcionamiento de las mismas no se cubre en este tutorial. Para ejemplificar el uso de GDB utilizaremos algunos ''sniplets'' de código C, en particular de una lista genérica.
 
Una vez instalado el programa, ya está listo para funcionar. GDB funciona desde la terminal y si bien existen interfaces gráficas como ddd y xxgdb, el funcionamiento de las mismas no se cubre en este tutorial. Para ejemplificar el uso de GDB utilizaremos algunos ''sniplets'' de código C, en particular de una lista genérica.
  
==Comenzando==
+
==Preliminares==
 
Los archivos de ejemplo pueden descargarse aquí. Permanentemente estaré haciendo referencia a los archivos aquí contenidos.
 
Los archivos de ejemplo pueden descargarse aquí. Permanentemente estaré haciendo referencia a los archivos aquí contenidos.
  
Línea 28: Línea 28:
 
El problema que tiene este programa es que utiliza la lista sin crearla, por lo que, tanto ''l'', ''l->dato'' y ''l->sig'' son diferentes de ''NULL''. El problema es obvio y el error grosero (otros son mucho más sutiles) pero ¿cómo nos ayuda GDB a darnos cuenta?
 
El problema que tiene este programa es que utiliza la lista sin crearla, por lo que, tanto ''l'', ''l->dato'' y ''l->sig'' son diferentes de ''NULL''. El problema es obvio y el error grosero (otros son mucho más sutiles) pero ¿cómo nos ayuda GDB a darnos cuenta?
  
 +
==Los comandos básicos GDB==
 +
Desde la terminal, ejecutamos:
 +
~$ gdb lista
 +
Lista es el nombre de nuestro programa. Si el programa recibe argumentos, pueden pasarse aquí o pueden setearse luego. La salida será algo así como:
 +
GNU gdb (GDB) 7.4.1-debian
 +
Copyright (C) 2012 Free Software Foundation, Inc.
 +
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 +
This is free software: you are free to change and redistribute it.
 +
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
 +
and "show warranty" for details.
 +
This GDB was configured as "i486-linux-gnu".
 +
For bug reporting instructions, please see:
 +
<http://www.gnu.org/software/gdb/bugs/>.
 +
(gdb)_
 +
La línea de comando de GDB (''(gdb)_'') espera que ingresemos los comandos. Una prueba simple puede ser utilizar el comando ''list'' (o la versión abreviada ''l'') para ver el código fuente de la función main.
 +
(gdb) l 41,51
 +
41      #ifdef TEST
 +
42      int main(int argc, char ** argv){
 +
43              Lista l;
 +
44              puts("Pregunto si la lista sin inicializar es vacía.");
 +
45              printf("%s\n", (l_EsVacia(l)?"Es vacía":"No es vacía"));
 +
46              puts("No es vacía, así que trato de imprimir el contenido del nodo...");
 +
47              printf("%s\n", (char*)l->dato);
 +
48              return 0;
 +
49      }
 +
50      #endif
 +
El problema parece estar en la línea 47 por que lo último que se ejecuta correctamente es el ''puts'' de la línea 46. Sin embargo, esto puede no ser tan fácil de determinar en un programa que no tenga salida. Lo que generalmente se hace en estos casos es ejecutar el programa en el entorno de GDB, con el comando run:
 +
(gdb) run
 +
Starting program: /home/jwackito/cvss/git/apuntes/lista-generica-c/lista
 +
Pregunto si la lista sin inicializar es vacía.
 +
No es vacía
 +
No es vacía, así que trato de imprimir el contenido del nodo...
 +
 +
Program received signal SIGSEGV, Segmentation fault.
 +
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
 +
99      ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No existe el fichero o el directorio.
 +
Esto no nos dice mucho. Lo importante aquí es que el programa recibió una señal SIGSEGV, es decir una Violación de Segmento.
 +
Program received signal SIGSEGV, Segmentation fault.
 +
Pero la línea a la que hace referencia está en:
 +
99      ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
 +
Es decir, en una librería del sistema operativo. Para determinar en qué línea de qué función falló el programa, se puede utilizar el comando ''backtrace'', inmediatamente después de haber ejecutado run.
 +
(gdb) backtrace
 +
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
 +
#1  0xb7ec0ae5 in _IO_puts (str=0x158d7c <Address 0x158d7c out of bounds>) at ioputs.c:37
 +
#2  0x0804850a in main (argc=1, argv=0xbffff324) at lista1.c:47
 +
El error es el del último mensaje, es decir, en la función main, del archivo lista1.c, en la línea 47.
  
 
+
==Puntos de parada y ejecución paso a paso==
  
 
[[Category:Documentación]]
 
[[Category:Documentación]]

Revisión de 16:27 6 ago 2012

Introducción

Aquellos que desarrollan en C, conocen de las dificultades a las que se enfrenta cuando trata de depurar un programa, que por ejemplo, por qué no se agrega un nodo a una lista o por qué no se copia determinado string. GDB (Gnu Project Debugger) es una herramienta que permite entre otras cosas, correr el programa con la posibilidad de detenerlo cuando se cumple cierta condición, avanzar paso a paso, analizar que ha pasado cuando un programa se detiene o cambiar algunas cosas del programa como el valor de las variables.

GDB es una herramienta muy poderosa que nos ayudará a encontrar esos errores difíciles, por ejemplo cuando los punteros no apuntan a donde estamos pensando. Si bien este tutorial está pensado para el lenguaje C, probablemente también sirva para depurar programas en Fortran o C++ con los mismos comandos o similares.

DESCARGO: Este no es un tutorial de programación en C. Muchas de las cosas aquí explicadas, sobre todo en el código de muestra, no necesariamente estén correctas. Solo se escribieron de la forma en que se escribieron a fin de mostrar alguna determinada característica de GDB.

Instalación

GDB no viene en Lihuen por lo que es necesario instalarlo desde los repositorios. Esto se puede hacer utilizando una interfaz gráfica como Synaptic, instalando el paquete gdb o desde la consola como superusuario, ejecutando los siguientes comandos:

~#apt-get update
~#apt-get install gdb

Una vez instalado el programa, ya está listo para funcionar. GDB funciona desde la terminal y si bien existen interfaces gráficas como ddd y xxgdb, el funcionamiento de las mismas no se cubre en este tutorial. Para ejemplificar el uso de GDB utilizaremos algunos sniplets de código C, en particular de una lista genérica.

Preliminares

Los archivos de ejemplo pueden descargarse aquí. Permanentemente estaré haciendo referencia a los archivos aquí contenidos.

En el primer ejemplo haremos referencia al archivo lista1.c. Para compilar, se puede correr el siguiente comando:

~$ gcc -o lista lista1.c -std=c99 -Wall -DTEST --debug

El parámetro -DTEST define la macro TEST para que se incluya la función main, que permite testear la funcionalidad de la lista. El parámetro --debug agrega símbolos de depuración, importantes a la hora de utilizar GDB.

Para correr el programa simplemente ejecutamos:

~$ ./lista

La salida debería ser algo así:

 Pregunto si la lista sin inicializar es vacía.
No es vacía
No es vacía, así que trato de imprimir el contenido del nodo...
Violación de segmento

El problema que tiene este programa es que utiliza la lista sin crearla, por lo que, tanto l, l->dato y l->sig son diferentes de NULL. El problema es obvio y el error grosero (otros son mucho más sutiles) pero ¿cómo nos ayuda GDB a darnos cuenta?

Los comandos básicos GDB

Desde la terminal, ejecutamos:

~$ gdb lista

Lista es el nombre de nuestro programa. Si el programa recibe argumentos, pueden pasarse aquí o pueden setearse luego. La salida será algo así como:

GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb)_

La línea de comando de GDB ((gdb)_) espera que ingresemos los comandos. Una prueba simple puede ser utilizar el comando list (o la versión abreviada l) para ver el código fuente de la función main.

(gdb) l 41,51
41      #ifdef TEST
42      int main(int argc, char ** argv){
43              Lista l;
44              puts("Pregunto si la lista sin inicializar es vacía.");
45              printf("%s\n", (l_EsVacia(l)?"Es vacía":"No es vacía"));
46              puts("No es vacía, así que trato de imprimir el contenido del nodo...");
47              printf("%s\n", (char*)l->dato);
48              return 0;
49      }
50      #endif

El problema parece estar en la línea 47 por que lo último que se ejecuta correctamente es el puts de la línea 46. Sin embargo, esto puede no ser tan fácil de determinar en un programa que no tenga salida. Lo que generalmente se hace en estos casos es ejecutar el programa en el entorno de GDB, con el comando run:

(gdb) run
Starting program: /home/jwackito/cvss/git/apuntes/lista-generica-c/lista 
Pregunto si la lista sin inicializar es vacía.
No es vacía
No es vacía, así que trato de imprimir el contenido del nodo...

Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99      ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No existe el fichero o el directorio.

Esto no nos dice mucho. Lo importante aquí es que el programa recibió una señal SIGSEGV, es decir una Violación de Segmento.

Program received signal SIGSEGV, Segmentation fault.

Pero la línea a la que hace referencia está en:

99      ../sysdeps/i386/i686/multiarch/../../i586/strlen.S

Es decir, en una librería del sistema operativo. Para determinar en qué línea de qué función falló el programa, se puede utilizar el comando backtrace, inmediatamente después de haber ejecutado run.

(gdb) backtrace
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1  0xb7ec0ae5 in _IO_puts (str=0x158d7c <Address 0x158d7c out of bounds>) at ioputs.c:37
#2  0x0804850a in main (argc=1, argv=0xbffff324) at lista1.c:47

El error es el del último mensaje, es decir, en la función main, del archivo lista1.c, en la línea 47.

Puntos de parada y ejecución paso a paso


 Ante cualquier duda o inconveniente no dudes en visitar nuestros foros.
 http://lihuen.linti.unlp.edu.ar/foros