" Me gustan mis errores, no quisiera renunciar
a la deliciosa libertad de equivocarme".
Charles Chaplin
Prólogo
El siguiente artículo está
concebido con el propósito de mostrar una
serie de conceptos básicos a todo aquel que
nunca a utilizado un depurador o que,
habiéndolo utilizado, esta buscando un
entorno gráfico más agradable para su
trabajo diario con esta herramienta. Sobre la
potencia y posibilidades del depurador descrito (el
gdb) se podría escribir mucho por lo que la
simplicidad con que se han tratado los
términos es con un fin meramente
didáctico, como siempre :).
¿Qué es un depurador ?
(Basado en una historia real ;) ).
"Erase una vez un programador cuya
única tarea, cuando se hallaba con un error en
su software, era la siguiente:
/*Código*/
(...)
bucle
cambio_en_una_variable
mostrar valor_variable;
fin_bucle
(...)
Tenía que insertar numerosas líneas
en su código fuente para que, durante la
ejecución de su programa, se le fuesen
mostrando los valores de las variables de
interés. Ésto hacía su vida
difícil y el coste de depurar (=revisar) su
código fuera dos veces mayor que el de
escribir el código ... (..)".
¿Quien no se ha encontrado alguna vez ante
este panorama?, tenemos un error en un programa, lo
hemos intentado y modificado todo, estamos
convencidos de que "es culpa del
compilador" ya que no se puede hacer
más, se han intentado
alternativas.... aquí es donde entra en juego
este tipo de software.
Un depurador permite controlar la
ejecución de un programa en todo momento, de
esta forma podemos conocer el estado de las
variables, definición de las mismas, que
pasaría ante ciertos cambios, etc... Todo
ello, repito, de forma interactiva y mientras se
ejecuta el código a depurar. Si con esta
definición no ha quedado muy claro espero
hacerlo a lo largo de este artículo.
¿Qué pasaría si el programador de
nuestra historia dispusiera de un programa que le
permitiese hacer lo siguiente?.
curro # espiar mi_programa
(Suponiendo que el programa a depurar se llame
mi_programa)
Una vez invocado el programa "espia",
hiciera lo siguiente:
espia > ejecuta mi_programa "hasta la línea 50"
espia > muestrame el valor de la variable <X>
espia > ¿de que tipo es la variable <X>
En este punto nuestro amigo da un salto de
alegría, ha encontrado su error, metiendo números de
centenares de cifras de bytes puede ser malo para su
salud.
espia > sigue ejecutándote hasta el final
espia > salir.
Hemos visto que el programa llamado
"espia" para nuestra historia le ha
ayudado, y mucho, ha estado ejecutando el programa a
su antojo, observando valores de variables y
cómo están definidas. Pues bien, ESO
ES UN DEPURADOR, a grandes rasgos, claro.
Aviso !!: Para poder hacer uso de esta
capacidad el programa ha de ser compilado con el
parámetro "-g", caso de tratase de
un programa en C y el compilador GNU gcc.
Existe una herramienta de este tipo disponible
para todos los usuarios de LINUX (y otras muchas
plataformas), el GDB " The GNU Source-Level
Debugger". Por el mismo precio, y bajo la
misma licencia, que el sistema operativo con el que
seguramente estás leyendo este
artículo, la GNU General Public
License. Permite depurar código generado en
C, C++, Modula-2 y ensamblador.
Seguramente la distribución que utilizas
lo incorpora, si no es así, cambia de
distribución inmediatamente u obtenlo de
cualquier lugar en la red donde esté
disponible ;). Echa un vistazo a los paquetes que
tienes disponibles, si obtienes un "command
not found" al invocarlo como
"gdb" es que muy probablemente no lo has
instalado.
Introducido en el directorio
"/usr/src/gdb-4.xxxx/gdb", teclea
"./configure", tras lo cual puedes
introducirte en el directorio "doc" y
teclear "make gdb.dvi;make refcard.dvi"
con lo que obtienes dicho manual en el formato
especificado, de ahí a obtener un fichero que
tu impresora pueda entender lo dejo como ejercicio
para el lector :).
¿Qué es DDD?
En vez de continuar por una descripción
detallada del funcionamiento de este programa, todos
sus comandos y ejemplos de funcionamiento, pienso
que es mucho más útil para el no
iniciado el facilitarle la tarea mediante el uso un
entorno más amable. En este caso
explicaré el uso general de uno de los
disponibles, el llamado DDD de Display Data
Debugger.
Este entorno permite, en términos
generales, el disponer de un interfaz mucho
más amigable además de poder
configurar la tarea de depurar código a
nuestro gusto, pero una advertencia previa,
simplemente se trata de un entorno gráfico
que funciona "por encima" del gdb, por lo
que necesita de este para poder ejecutarse, no se
reinventa la rueda. De echo veremos como su aspecto
nos posibilitará el utilizar el gdb
directamente si lo deseamos. También es
posible utilizar otros depuradores (dbx y xdb).
Puede obtenerse el software e información
sobre el mismo en ,
http://www.cs.tu-bs.de/softech/ddd/ aunque si
utilizas Red Hat, por ejemplo, lo tienes disponible
en formato .rpm". Una advertencia, puede que
tengas dos versiones del mismo, una estática
y otra dinámica, esto es debido a que se basa
en las librería Motif, por lo que de no
tenerlas ha de optarse por la versión
estática del mismo.
El autor desconoce la situación actual en
cuanto a si DDD funciona correctamente con LESSTIF
(
http://www.lesstif.org), implementación
libre de dicha librería gráfica. No
hace mucho tiempo funcionaba añadiendo un
parche al código fuente del depurador, el
autor de este artículo lo usaba de esta forma
en un kernel 1.2.13 y con una lesstif 0.75 (si no
recuerdo mal). Consulta la página de este
proyecto para más información.
Al grano, invocando ddd obtendremos:
Figura 1. Pantalla que se obtiene al invocar el programa
Podemos invocar el programa de tres formas, la
descrita y las dos siguientes:
ddd <programa> core
ddd <programa> <identificador_de_proceso>
Un fichero "core" se produce ante un
error en un programa, que automáticamente
generará dicho fichero para poder ser
inspeccionado con este tipo de herramientas. Si en
tu máquina no se generan este tipo de
ficheros echale un vistazo al valor del
intérprete de comandos para este fichero (con
'ulimit -a' muestra todos ellos, con 'ulimit -c
<valor>' puedes definir el tamaño
máximo de estos ficheros, además de
otros parámetros de interés).
Mediante el identificador de proceso podemos
inspeccionar el programa mientras se está
ejecutando.
Con este entorno gráfico existen
múltiples formas de realizar una tarea, no
voy a describirlas todas, simplemente la más
sencilla o la más directa, asimismo
podrás observar en la ventana del depurador
(la última de abajo) el reflejo vía
comandos de todo lo que estas haciendo, lo cual
puede serte muy útil para aprender el manejo
del gdb desde la línea de comandos.
Aspecto del Entorno
En la Figura 1 puede observarse que la ventana
está dividida en 3 zonas, la de más
abajo se corresponde con la ventana del depurador
"puro y duro", en ésta podemos
introducir los comandos como si no tuviésemos el
interfaz a nuestra disposición, la zona
central es la que contendrá al código
fuente, la superior puede servirnos para obtener el
contenido de una variable en forma
gráfica. La barra de tareas o ventana
flotante que aparece nos permitirá el
controlar la ejecución del programa en todo
momento.
Existen asimismo una ventana de ejecución
de las aplicaciones y otra que muestra el
código fuente del software a depurar. Ambas
opcionales.
Podemos obtener información sobre lo que
estamos haciendo, o deseamos hacer en cualquier
momento de múltiples formas, por ejemplo,
cuando estamos encima de una variable o de cualquier
botón del entorno nos aparecerá un
diálogo mostrando información sobre el
mismo; en la parte inferior de la ventana tenemos
una línea de estado sobre lo que se
está haciendo y su resultado; tenemos en la
parte derecha un menú desplegable con la
ayuda disponible; pulsando F1 y después sobre
lo que queremos conocer obtendremos una ventana de
diálogo con lo que queremos saber ... En la
ventana del depurador podemos introducir
"help" para una ayuda general o para
cualquier comando deseado.
Mediante el menú "Preferences"
podemos hacer, si lo deseamos, que se nos muestren
las ventanas separadas, en vez de unidas.
Figura 2. Ayuda sobre el menú "File"
Empezando por Abajo
En la ventana del depurador
("DDD:Debugger Console") podremos
dar nuestros primeros pasos en el uso del depurador
o, si nuestros conocimientos lo permiten, introducir
los comandos deseados sin hacer uso del ratón
ni de menú alguno. Puede ayudarnos para esto
el observar su estado cuando realizamos cualquier
maniobra usando el entorno gráfico. Mediante
la opción del menú
"Commands->Command History" podremos
obtener una ventana con el histórico de todos
los comandos introducidos hasta el momento.
Para un mejor conocimiento de las
características recomiendo recurrir al propio
manual del mismo. De todas formas trataré de
especificar en algunos casos cómo se
realizaría una tarea desde el depurador
directamente.
Ideas Generales
Una vez cargado el código fuente deseado,
mediante la invocación del depurador o
mediante el menú "File",
aparecerá el código fuente del mismo
en el área correspondiente. A partir de este
momento ya podemos navegar por el código
fuente, obtener el valor y definición de las
variables, ejecutar el programa controlando su
evolución...
Podemos visualizar la salida de un programa
mediante una ventana de ejecución (Options
-> Run in Execution Window ), o viendo su
salida en la consola del depurador (salvo si el
programa está concebido para ejecutarse con
Motif, u otra librería de desarrollo de
entornos gráficos).
Ubicando el puntero del ratón encima de
cualquier variable del código fuente podemos
obtener su valor actual en la linea de estado o en
forma de información emergente. Si pulsamos
el botón derecho del ratón obtenemos
un menú como el siguiente:
Este menú permite el obtener el valor de
la variable "fname", en la ventana
inferior, mostrarla visualmente en la ventana
superior ("área de dibujo"), tanto
si se trata de una variable como si es un puntero
(variable que contiene la dirección de
memoria de otra, no su valor). Con "What
is" podemos conocer la estructura o tipo de
variable señalada. Lookup permite la
búsqueda de ocurrencias de la misma. Break
at y Clear at permiten el manejo de los
denominados "puntos de ruptura" (
breakpoints) que explicaré en
breve.
Podemos utilizar también las opciones
disponibles desde la barra de botones ubicada debajo
del área del código fuente, colocando
el parámetro deseado dentro de la ventana
izquierda y escogiendo posteriormente la
acción deseada.
Un punto de ruptura permite ejecutar el programa
hasta una línea concreta del programa, para
después poder observar el valor de las
variables, seguir ejecutando paso a paso el mismo,
revisar el estado de las hebras
(threads).... Hay que tener en cuenta que, en
general, si no ponernos un punto de ruptura en
nuestro programa puede finalizar su ejecución
correctamente o salir debido a un error, con lo que
no seremos capaces de ejecutar acción alguna
sobre el mismo, hay que depurar el código
"en marcha".
Para colocar un punto de ruptura en nuestro
código fuente podemos hacer varias cosas:
- Colocar el ratón en la parte izquierda
de la línea en la que deseamos colocarlo,
pulsar el botón derecho del mismo y escoger
Set Breakpoint o Set Temporary
Breakpoint. La diferencia consiste en que uno
temporal se desactiva la primera vez que se ha
alcanzado, el otro ha de hacerse mediante el
comando correspondiente (¿lo adivina? :) ).
- Escoger el botón del área del
código fuente"Break at
()".
- Teclear en la consola del depurador la
línea "break
<numero_linea>".
- Mediante la opción del menú
Source->Edit Breakpoints, con la que
obtendremos una ventana para el control de esta
utilidad:
En la figura podemos observar dos puntos de
ruptura en las líneas 70 y 71 del
código fuente, el símbolo es bastante
auto-explicativo.
El menú siguiente permite el control de los mismos:
- Mediante la opción Condition
puede establecerse una condición para
el punto de ruptura, se detendrá la
ejecución del programa caso de que
está condición se cumpla en la
línea marcada. Con Ignore Count
puede establecerse un contador para que se
detenga la ejecución del programa cuando
haya pasado <n> veces sobre dicha
línea, por ejemplo, pensemos en un bucle en
el que queremos detener la ejecución
pasadas 15 iteraciones del mismo.
- Llegados al
punto de ruptura podemos estudiar el estado de las
variables definidas en el programa mediante las
opciones mostradas en el menú
correspondiente. Vuelvo a recordar que todas estas
y otras opciones se encuentran en el menú
principal (p.ej: menú Data).
- Para la ejecución del código
utilizaremos la barra de tareas habilitada para
tal fin, que aparece en la parte superior derecha
de la ventana principal.
Con este sencillo montaje se puede apreciar el
paralelismo entre la barra de tareas y el
menú correspondiente.
Podemos ejecutar el programa e interrumpirlo, si
lo hacemos desde el menú podemos introducir
parámetros para la ejecución mediante
un cuadro de diálogo. Step ejecuta el
programa una línea mas (paso a paso), esto
es, si se hace una llamada a una función, se
recorrerán las líneas de ésta,
a diferencia de Next, que la tratará
como una sola línea.
Continue permite continuar con la
ejecución del programa tras el punto de
ruptura. Kill, Interrupt y Abort permiten la
interrupción del programa depurado.
Llegados a este punto podemos inspeccionar el
valor de las variables con los métodos
descritos hasta el momento, mostrarlas
gráficamente, examinar pila de llamadas a
rutinas, Threads, registros del
procesador... (ver menú Status).
Quizá el elemento más llamativo de
este entorno gráfico es su área de
dibujo para los datos, en la parte superior de la
ventana, en la que podemos apreciar
gráficamente tanto la estructura y contenido
de los datos, como la relación existente
entre los mismos, de existir, veamos un ejemplo en
el que se muestra un vector (Argumentos) y cuatro de
sus componentes:
La información a mostrarse en esta ventana
puede ser muy variada, basta con echarle un vistazo
al menú Data->More Status Displays
en el que se puede configurar todo lo que
deseamos que se muestre en pantalla, para el caso
anterior podemos ampliar la información al
estado de los registros del procesador,
librerías dinámicas utilizadas y
estado de la ejecución del programa:
Consideraciones Finales
El entorno es configurable desde el propio
programa, mediante el menú
Options->Preferences, principales y
mediante el mecanismo de recursos propio del mundo
de las aplicaciones Motif (fichero $HOME/.dddinit),
pero saldría de el objetivo del presente
artículo el describir todos los recursos
configurables y cómo hacerlo.
Recomendar encarecidamente la lectura del manual
del programa (ddd.ps) que acompaña al mismo y
del manual del depurador propiamente
("Debuggin with GDB"), aunque el
lector con un poco de curiosidad puede manejarse en
este entorno en poco tiempo, basta con depurar un
código fuente conocido para ver todas las
posibilidades existentes.
Por último pedir disculpas ante cualquier
error grave que haya podido cometerse en la
redacción de este documento por mi parte
:).
|