Bienvenidos sean a este post, hoy hablaremos sobre una tecnica para analizar nuestros codigos.
Pero que es debug o tambien conocido como depuracion? Es la accion de visualizar los recursos utilizados por nuestro codigo fuente al momento de ejecutarlo. Esto es especialmente util para ver y analizar sus comportamientos.
Los compiladores pueden trabajar o no con simbolos. Si lo hacemos con simbolos se creara un mapeo necesario entre el codigo fuente y el programa generado. Esto es asi para poder apuntar la linea del codigo fuente correspondiente a la siguiente accion del programa. El debugger o depurador permite las siguientes acciones en el codigo fuente:
- Ver los resultados de cada instruccion ejecutada
- Ver el estado actual de cada variable
- Examinar estructuras de datos complejos
- Visualizar el valor de los datos miembros en las clases
- Ver los valores actuales en memoria de varios apuntadores y otras ubicaciones en memoria
De manera predeterminada, al momento de compilarlo lo hacemos sin simbolos. Por lo tanto, no se genera este mapeo para poder vincular al programa con el codigo fuente para que el depurador pueda manipularlo. Esto es la conducta predeterminada porque realiza una compilacion mas rapida. Pero podemos modificar esa conducta, para ello debemos ejecutar la compilacion de la siguiente manera:
$ g++ -g codigo_fuente -o programa
Simplemente agregando esa opcion nos permite establecer todo el codigo para la depuracion. Veamos un ejemplo, para ello generen un archivo con el siguiente codigo:
#include <fstream>
#include <iostream>
int main()
{
char archivo[80];
char texto[255];
std::cout << "Nombre del archivo: ";
std::cin >> archivo;
std::ofstream abrir(archivo);
std::cout << "Ingresa un texto: ";
std::cin.ignore(1, '\n');
std::cin.getline(texto,255);
abrir << texto << std::endl;
abrir.close();
std::ifstream leer(archivo);
char ch;
while(leer.get(ch))
std::cout << ch;
leer.close();
return 0;
}
Este codigo esta explicado en este post. De manera muy superficial este nos perrmite crear un nuevo archivo, agregarle un texto y mostrar lo ingresado. Pero aqui lo usaremos para ver las distintas posibilidades que nos brinda el depurador. Para crear nuestro enlace entre el codigo y el programa para la depuracion, debemos compilarlo como indicamos anteriormente. Este se generara internamente y no veremos nada mas en el directorio.
Nuestro siguiente paso sera agregar la herramienta para la depuracion. Para ello usaremos al depurador GNU propio de G++. Por lo general, se encuentran en repositorios de cualquier distro de Linux. Para instalarlo en debian deben usar el siguiente comando:
$ sudo apt-get install gdb
Este agregara una serie de componentes extras y una vez finalizado podemos comenzar a usarlo. Si ya compilaron el ejemplo con la opcion -g podemos usarlo al depurador de la siguiente manera:
$ gdb depurar
Simplemente le pasamos el nombre del programa, no del codigo fuente, para ingresar al depurador. Esto hara que nos quedemos con el siguiente prompt:
$ gdb depurar
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from depurar...
(gdb)
A partir de ahora podemos usar los comandos antes comentados. Probemos de ejecutar el siguiente:
(gdb) b 11
Este establecera un breakpoint en la linea 11. Para esto tambien podiamos haber usado a break 11 pero en un momento hablaremos sobre esto. Esto significa que el programa se ejecutara hasta ese breakpoint. Para correrlo debemos ejecutar lo siguiente:
(gdb) r
Pueden correrlo con esto o con la palabra run. Si lo ejecutan, tendremos una salida como la siguiente:
(gdb) r
Starting program: /home/tinchicus/lenguajes/cpp/04/depurar
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Nombre del archivo: p.txt
Breakpoint 1, main () at depurar.cpp:11
11 std::cout << "Ingresa un texto: ";
(gdb)
Nos pide que ingresemos un nombre de archivo y luego se interrumpe, informando que el codigo tiene un breakpoint en la linea que establecimos. Esto es ideal para cuando necesitamos analizar una parte de nuestro codigo. Por ejemplo, vamos a suponer que tenemos un error en esa parte y necesitamos analizar esa seccion. Con el breakpoint ejecutara hasta donde le indiquemos y podremos centrarnos en ello. Esto es principalmente para descartar si es esta seccion u otra la del codigo que falla. Para continuar debemos usar el siguiente comando:
(gdb) c
Esto hara que retomemos desde el ultimo breakpoint y continuar con la ejecucion del programa. Veamos para el caso de nuestro ejemplo:
(gdb) c
Continuing.
Ingresa un texto: Esto es de prueba.
Esto es de prueba.
[Inferior 1 (process 9221) exited normally]
(gdb)
Como pueden ver continua perfectamente nuestro codigo, tambien porque el mismo funciona perfectamente y no posee ningun error. Vamos a hablar un poco sobre algunos comandos que podemos usar con los breakpoints. Veamos como es el primero:
(gdb) info b
Si pasamos al comando info seguido de la letra b nos devolvera todos los breakpoints asignados. Veamos como es la salida:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000000128f in main() at depurar.cpp:11
2 breakpoint keep y 0x00000000000012df in main() at depurar.cpp:14
3 breakpoint keep y 0x0000000000001319 in main() at depurar.cpp:16
(gdb)
Nota:
Para ver mejor al ejemplo le agregue unos breakpoint mas.
Si observan tenemos varias opciones que nos sirven para poder manejarlas que van desde el identificador, el tipo, disponibilidad, si esta activado, la direccion y en que linea de que archivo y la funcion. Despues tenemos un comando para desactivarlos:
(gdb) disable b
Este desactiva todos los breakpoints que tengamos creados. Para desactivar un breakpoint especifico debemos pasarle el identificador:
(gdb) disable b 2
En este caso, desactiva al segundo breakpoint unicamente. Si lo comprobamos, lo veremos de la siguiente manera:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000000128f in main() at depurar.cpp:11
2 breakpoint keep n 0x00000000000012df in main() at depurar.cpp:14
3 breakpoint keep y 0x0000000000001319 in main() at depurar.cpp:16
(gdb)
Observen que unicamente el breakpoint 2 esta con un estado de desactivado, el resto se seguiran usando cada vez que lo corramos. Asi como los podemos desactivar, tambien los podemos activar. Siendo muy similar al anterior:
(gdb) enable b
Este activara todos los breakpoints que se encuentren asignados. Al igual que en disable, podemos especificar uno mediante el identificador y su sintaxis es la misma:
(gdb) enable b 2
Por ultimo, podemos eliminar acciones en nuestro depurador. Para ello usamos a delete, tomemos como ejemplo un breakpoint. Para eliminar uno debemos usar el siguiente comando:
(gdb) delete breakpoint 2
Esto elimina el segundo breakpoint, si usamos a info nos devuelve lo siguiente:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000000128f in main() at depurar.cpp:11
3 breakpoint keep y 0x0000000000001319 in main() at depurar.cpp:16
(gdb)
A continuacion, les dejo una tabla con los comando vistos anteriormente, asi como tambien otros que disponemos:
| Descripcion | Valor |
| break [archivo:]funcion | Establece un punto de interrupcion al entrar a funcion en archivo, archivo es opcional |
| bt | Forma abreviada de backtrace: muestra la pila del programa |
| c | Continua la ejecucion del programa despues del punto de interrupcion |
| disassemble frame | Muestra el codigo binario como codigo en lenguaje ensamblador en vez de lenguaje fuente para un frame |
| where | Nos devuelve el frame actual en uso |
| display exp | Muestra el valor de la variable exp cada vez que el programa se detenga |
| help | Muestra la ayuda en general, acompañado con el comando muestra las opciones de ese comando |
| list [[archivo:]linea] | Muestra ±5 lineas del codigo fuente. archivo y linea especifican el codigo fuente a mostrar y son opcionales |
| next | Ejecuta el siguiente paso del programa sin entrar en las funciones llamadas |
| print exp | Imprime exp, donde exp puede ser una variable, nombre de funcion o expresion compleja, el nombre de un Array, Esto permite examinar la memoria |
| quit | Sale del gdb |
| run arglista | Ejecutar el programa desde el principio con la lista opcional de argumentos de linea de comandos, contenidos en arglista |
| set variable = exp | Asigna a una variable del codigo fuente un valor. Esto permite alterar el estado de la memoria |
| set | Modifica las variables del entorno de gdb |
| step | Ejecutar el siguiente paso del programa, entrar en cualquier funcion llamada |
| undisplay | Cancelar un despliegue |
| watch | Crea un punto de observacion |
| whatis exp | Despliega el tipo de datos de exp |
En resumen, hoy hemos visto la depuracion, que es, para que sirve, como debemos compilarlo para habilitarlo, la herramienta para trabajar, asi como tambien un ejemplo para ver algunos de los comandos que podemos utilizar. 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.


Donatión
It’s for site maintenance, thanks!
$1.50
