Delphi paso a paso (y XIII) El depurador Por Vladimir Algara En esta entrega, que lleva como número de orden el (y XIV), se pretende finalizar con esta serie de artículos que han llevado por nombre Delphi paso a paso. No quiero decir con esto que nunca más vuelva a tratar el tema de Delphi, ni mucho menos, pero sí que lo trataré de manera menos sistemática y, sobre todo, no concibiéndolo como un curso en el que se van quemando etapas y en el que se va aprendiendo, pautadamente, las características de un lenguaje. Cada vez que, puntualmente, me surja un tema interesante que pueda serlo asimismo para el lector de mis ideas, lo expondré en este medio, pues son muchos los temas que aún quedan por tratar. Uno de los temas no abordados en esta serie, y que en su concepción resultaba inimaginable no hacerlo así, es la gestión de bases de datos; entendiéndolos en su manera de explotarlos en fondo y forma. Gracias a que existen otros articulistas que han compartido conmigo las bondades de Delphi, creo que el acercamiento que yo hubiera hecho a tales temas ha quedado más que resuelto con sus respectivos razonamientos, lo cual me ha llevado a hacer este razonamiento, ajeno a lo que será el contenido de las líneas que nos ocupan, y llegar a la conclusión de que es mucho mejor abandonar la forma reglada de explicar Delphi y tomar como regla la explicación esporádica de los casos interesantes. En resumen, que el y XIV del título significa que el curso lo doy por concluido en su concepto, pero no que vaya a abandonar el lenguaje Delphi por otro mejor postor, sólo voy a deshacer mi monogamia informática arrastrada en los últimos meses. Bueno, después de toda esta retahíla de nosequé, voy a entrar en el meollo del artículo, que no es otro que el uso y abuso del depurador de Delphi para corrección y seguimiento de errores en las aplicaciones diseñadas en el lenguaje de Borland.

Tipos de errores Todo el mundo sabe a qué tipos de errores nos enfrentamos a la hora de diseñar una aplicación con cualquier herramienta, los peores de todos suelen encontrarse en la elección del analista que ha de perfilar el coherente funcionamiento del programa. Bromas aparte, los errores se clasifican históricamente en: -

Errores de compilación: Delphi posee un potente compilador/enlazador que, una vez entendida su idiosincrasia, permite fijar con total exactitud el/los puntos conflictivos del código erróneo. Estos errores consisten, esencialmente, en fallos sintácticos, del tipo falta punto y coma, inconsistencia entre tipos de variables, sentencias mal construidas, estructuras de control inacabadas, etc., etc., etc.

-

Errores de enlazado. Superada la fase de compilación llega la de enlazado, en la que se especifica que elementos, ajenos a nuestro código compilable, se van a usar para el correcto funcionamiento de la aplicación. Es la parte en la que se han de reseñar los módulos OBJ, librerías y DLLs, y la ausencia de alguna de ellas provocará la queja de Delphi.

-

Errores de ejecución. Los hay, a su vez, de dos tipos, y la forma de clasificarlos se limita a la dificultad de corregirlos. Los más evidentes son los denominados errores Run-Time, los cuales originan una ruptura del buen funcionamiento de la aplicación y una salida de la misma. Dado lo acotado del error su localización es fácil, aunque su corrección puede no serlo tanto (consisten en especificaciones de archivos que no existen y que se quieren utilizar, divisiones por cero, la obtención de la raíz cuadrada de un número negativo, etc.);

1 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

los menos evidentes son los llamados errores lógicos y consisten en un funcionamiento anómalo que, en el peor de los casos, pueden dar con nuestros huesos en el Psiquiátrico asignado a nuestra Área Sanitaria. En lo que a mi experiencia personal se refiere, el error lógico que más tiempo me ha llevado descubrir fue uno en el que se juntaron varios factores: 1.- No utilizaba la notación Word Mixing en la declaración de mis variables. 2.- El programa estaba hecho en un Pascal arcaico sobre una máquina arcaica (no disponía de depurador) 3.- No tenía la experiencia suficiente para enfrentarme a lo que debía resolver (vamos, que no tenía ni idea) El resultado de estas tres variables derivaron en que el programa se tiró sin funcionar más de una semana, todo por culpa de una línea en la que se utilizaba un contador de manera inadecuada:

I := 1 + 1

En la que realmente debería haber puesto, como todo el mundo alcanza a comprender:

I := I + 1

Este error siempre lo oculté y aquí y ahora me sincero con todos. En fin, experiencias personales aparte, cualquiera de los dos tipos de errores que se pueden dar en tiempo de ejecución los vamos a resolver (o lo intentaremos) por medio del depurador de Delphi.

Ventajas y desventajas Antes de pasar a explicar cómo manejar el depurador, debo hacer la advertencia que siempre se hace cuando se habla de este tipo de utilitarios, y que gira en torno a los recursos que estos utilitarios consumen. No es lo mismo entregar un ejecutable final con la información necesaria para ser depurado que sin ella. En tiempo de desarrollo podemos permitirnos el lujo de introducir dicha información dentro de nuestros ejecutables, pero una vez acabados y depurados habremos de eliminarla. Por una parte impediremos que el ejecutable sea inspeccionado por ojos espías, y, por otra, reduciremos su tamaño final (hay que tener presente que Delphi añade la información necesaria en el archivo .DCU asociado a la aplicación, y que la vuelve a eliminar cuando no se desea usar el debugger) y aumentaremos la velocidad de los procesos. Estableciendo que vamos a depurar el programa, sepamos de qué ventajas inmediatas vamos a disponer: 1.

La primera, y principal, la de poder recorrer e inspeccionar el código que se está ejecutando en el momento que el programa responde a nuestras especificaciones o a estímulos exteriores.

2.- Cambiar el contenido de las variables que influyen en la forma de trabajar de un determinado proceso.

2 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

3.- Provocar la parada del programa a tenor del contenido de una o más variables de referencia. O de la conjunción de todas ellas. 4.- Alterar el valor asociado a los campos de las bases de datos

Configuración del depurador El depurador se configura genéricamente en el menú Tools, en el ítem Options... Desde él accedemos a la ventana de configuración general Environment Options en la que, entre otras, se encuentra la pestaña Preferences. Dado que existen distintos aspectos que podemos tocar en lo que a preferencias del entorno se refiere, dentro de la pestaña Preferences existen otras subdivisiones correspondientes a sendas facetas configurables. La que a nosotros nos interesa se encuentra en el GroupBox reservado al Debugging que se ve en la figura 1.

Figura 1: Ventana para la configuración del depurador

Los valores que se pueden fijar en este cuadro de grupo son, como se ve en la figura de más arriba, los siguientes: 1.- Integrated Debugging: Es la piedra angular de todo este berenjenal. Cuando está marcado (valor por defecto) se dispone de depurador para las aplicaciones, si no, no. 2.- Step program block: Ejecuta el programa usando el depurador desde el principio de la aplicación, o a partir del módulo que contenga la información necesaria como para ser reconocido como bloque de comienzo (un punto de ruptura, por ejemplo). Estos dos puntos están íntimamente ligados al depurador, los otros tres ya no tanto, pero como se engloban dentro del mismo GroupBox los explicaré.

3 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

3.- Hide designer on run: Hace que el inspector de objetos y el lugar donde se diseñan las distintas ventanas desaparezcan, a la hora de ejecutar una aplicación. 4.- Break on exception: Habilita o no la salida de la aplicación cuando se produce un error de tipo Run-Time. El valor por defecto de este Check es marcado, lo cual significa que cuando ocurra un error aparecerá la ventana de la figura 2 y la aplicación acabará. Si no está marcado aparecerá una caja diferente a la anterior (la de la figura 3), que también nos informa de que ha sucedido un error, pero permite que el programa siga funcionando. Por ejemplo, en la porción de código de más abajo, se fuerza la aparición de un error. Se pretende leer el contenido de un archivo que no existe, lo cual provoca un error de excepción que, en un caso nos echa y en otro no.

nCapitulo := TabCapitulos.TabIndex; case nCapitulo of 0 : Memo1.Lines.LoadFromFile('KK.TXT'); archivo 1 : Memo1.Lines.LoadFromFile('CAP2.TXT'); 2 : Memo1.Lines.LoadFromFile('CAP3.TXT'); 3 : Memo1.Lines.LoadFromFile('CAP4.TXT'); end;

// Error, no existe el

Figura 2: Ventana informativa y el programa deja de ejecutarse

Figura 3: Ventana informativa y el programa continúa su normal ejecución

Cuando no marcamos la opción de disponer de depurador (punto 1.-), nunca aparecerá la ventana de la figura 2, siempre saldrá la de la 3. 5.- Minimize on run: Hace que Delphi se minimice al ejecutar la aplicación.

Puesta en marcha Para hacer funcionar el depurador ha de hacerse desde dentro de Delphi. Una vez funcionando podemos acceder a la parte del código que se quiere inspeccionar y allí poner una marca que

4 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

indique a la utilidad que debe parar la ejecución del programa. Estos puntos son los llamados BreakPoints (Puntos de ruptura) y son la forma más habitual de trabajar con el depurador. De cualquier forma, esta no es la única manera que tenemos de comunicarnos con el depurador, existen otras y todas ellas se engloban en el menú Run, del cual se ha hecho un extracto en la figura 4.

Figura 4: Operaciones con el depurador 1.- Run: Ejecuta la aplicación sin pausa, hasta que encuentra un Punto de Ruptura o se pulsa la combinación de teclas (varias veces) [Ctrl] + [Alt] +[Pet Sis]. 2.- Parameters: Permite pasar parámetros a la aplicación. 3.- Step Over: Ejecuta una línea de la aplicación. Si la línea consiste en una llamada a una función o a un procedimiento, ésta se realiza de una sola vez, sin entrar en el código que haya en su interior. 4.- Trace Into: Ejecuta, como el anterior, una línea de la aplicación, pero, en este caso, si la línea es una llamada a función o a procedimiento, se recorre el código contenido en ellos. 5.- Run to Cursor: Dado que una vez que se ejecuta un programa se puede uno mover libremente por el código que lo guía, podemos acceder a una línea sospechosa de contener error y elegir esta opción, lo cual hará que la aplicación se desarrolle libremente, sin trabas, hasta el momento que se alcance la línea escogida. 6.- Show Execution Point: Muestra la línea de programa por la que se va. Esto puede ser útil si nos hemos movido por otras porciones de código fuente o hemos minimizado la ventana que contiene el código ejecutable. 7.- Program Pause. Detiene, momentáneamente, la ejecución de la aplicación. 8.- Program Reset. Vuelve a ejecutar, desde el principio, la aplicación en curso.

Puntos de Ruptura Los Puntos de Ruptura son las herramientas más usadas en la interacción con el depurador. En Delphi hay varias formas de especificar uno de estos Puntos de Ruptura.

5 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

La forma de hacerlo es ir a una línea "sospechosa" de contener error y convertirla en un Punto de Ruptura. Esta operativa se puede realizar cuantas veces se desee. El resultado final puede ser algo similar a lo que se ve en la figura 5, en la que existen tres puntos de ruptura, uno de ellos inhabilitado (color verde) y dos no (color rojo, azul para el daltónico de Alfonso).

Figura 5: Puntos de Ruptura en el código de ejemplo

Para poner un Punto de Ruptura (o quitarlo) basta con realizar alguna de las acciones siguientes sobre la línea elegida. -

Pinchar con el botón principal del ratón en la parte izquierda del editor de código.

-

Pulsar la tecla [F5].

-

Pinchar con el botón auxiliar del ratón para que aparezca el menú local y elegir la opción Toggle BreakPoint.

-

Acceder, por medio del menú de Run, al ítem Add BreakPoint; lo cual hará que aparezca la ventana de la figura 6. Allí, como se puede ver, se le indica la unidad donde está la línea a añadir (por defecto en el que nos encontramos), el número de línea de programa (por defecto en la que nos encontramos), una condición y un número de ejecuciones.

6 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 6: Puntos de Ruptura desde la ventana de configuración Al especificar algo en Condition, el Punto de Ruptura sólo será válido cuando ese "algo" devuelva un valor verdadero; por ejemplo, se podría poner:

nCapitulo >= 2

Para que sólo entrara cuando se pinche sobre las pestañas 3 y 4. Al especificar un número en Pass Count, el Punto de Ruptura sólo será válido cuando la línea que lo tiene haya sido leída ese número de veces.; por ejemplo, se podría poner un 3, lo cual permitiría pasar dos veces por la línea definida sin que el Punto de Ruptura saltase. A la ventana de la figura 6 se puede llegar desde la ventana de la figura 7, en la que se muestran todos los Puntos de Ruptura definidos hasta el momento. Esta ventana, aparte de ofrecernos una relación detallada de ellos, nos permite modificar el contenido de todos o parte de ellos (haciendo doble clic sobre alguno o pinchando con el botón auxiliar del ratón) y variarlo de acuerdo a nuestros propósitos. A la ventana de la figura 7 se llega por medio del menú de View, a través del ítem BreakPoints.

Figura 7: Relación de los Puntos de Ruptura

Puntos de Observación Los Puntos de Observación son otras de las herramientas más usadas en la consulta de los datos con el depurador. Al contrario que los Punto de Ruptura, éstos no detienen la normal ejecución del programa, sino que sólo muestran el contenido de una variable, un conjunto de ellas o una expresión compleja. Para poner un Punto de Observación (o quitarlo) basta con acceder, por medio del menú de Run, al ítem Add Watch...; lo cual hará que aparezca la ventana de la figura 8. Allí, como se puede ver, se le indica la expresión que se desea ver, un número de ejecuciones a partir de la cual queremos que se haga efectiva la visualización, el número de dígitos que se van a usar en dicha visualización y si se puede ver o no (el valor por defecto de Enabled es estar marcado, o sea, que se pueda ver, pero en algún punto del programa nos puede interesar eliminar alguno de los Puntos de Observación definidos). Los distintos RadioButtons son autoexplicativos.

7 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 8: Puntos de Ruptura desde la ventana de configuración

Identif.

Tipo afectado

Descripción

,C

Caracteres

Muestra los caracteres correspondientes a los ASCCI comprendidos entre el 0 y el 31. De no especificarlo, dichos caracteres se interpretan como secuencia de escape del tipo \n, \t, etc.

,S

Caracteres

Muestra los caracteres ASCCI comprendidos entre el 0 y el 31 como secuencia de escape.

,D

Enteros

Muestra los valores enteros en formato decimal, incluidos los campos de las bases de datos tratadas.

,H o ,X

Enteros

Muestra los valores enteros en formato hexadecimal, añadiendo el prefijo 0x, incluidos los campos de las bases de datos tratadas.

,P

Punteros

Muestra información de la dirección ocupada por los punteros (segmento:offset). Esto permite identificar, de manera indirecta, por ejemplo, qué variable está causando un determinado problema.

,R

Registros, clases, objetos

Ofrece información específica de los distintos valores almacenados en las variables de una clase, para una instaciación concreta de un objeto concreto.

,nM

Cualquiera

Visualiza n bytes de memoria, comenzando en la dirección de la expresión reseñada. Una especificación del tipo 5M, mostrará información de los cinco primeros bytes de la variable en cuestión.

Tabla 1: Modificadores de formato.

Bueno, aquí me despido de la serie y de todo aquel que me quiera oír. En esta entrega no se acompaña con ningún tipo de ejemplo, pues provocar errores en un programa es la cosa más sencilla del mundo, lo que es más complicado es quitárselos de encima. Lo dicho, se cierra la serie, pero no la investigación en Delphi.

8 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS