UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

16.6. Puntos de parada condicionales

A pesar de ofrecer la posibilidad de ejecución paso a paso y visualización de cualquier variable en el programa, para localizar errores de ejecución de forma efectiva, a menudo es preciso detenerse en un lugar del programa, pero bajo ciertas condiciones. Por ejemplo, supongamos que el código contiene un bucle con un número elevado de iteraciones. Los comandos next y step permiten realizar una ejecución paso a paso de cada iteración. Pero si el error de ejecución requiere un número elevado de iteraciones antes de manifestarse, esta ejecución paso a paso es muy larga y tediosa. En esta situación, los comandos next y step no son tan útiles.

Un primer paso para el diagnóstico del problema consiste en ejecutar el programa sin ningún tipo de punto de parada. En cuanto la anomalía se manifiesta, el depurador suspende la ejecución en ese punto, y a través del comando print se pueden visualizar los valores de todas las variables visibles en ese instante. Combinando este comando con los comando up y down se pueden visualizar todas las variables activas en las diferentes funciones.

En un número elevado de casos, se detecta alguna variable que contiene un valor incorrecto y es la causa directa de la anomalía. Sin embargo, en general lo que se necesita saber no es qué variable ha causado la anomalía, sino cómo tal variable ha llegado a tener tal valor. En realidad, lo que se necesita es ejecutar el programa y detener su ejecución antes de que ejecute ciertas líneas de código en las que se produce el valor que acaba produciendo el error.

Para ilustrar esta situación se procede a desactivar todos los puntos de parada a la vez en el programa ejemplo mediante el comando disable sin argumentos. El comando info breakpoints muestra el valor n en el campo Enb (abreviatura de Enable) de todos los puntos.

(gdb) disable
(gdb) info breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x08048471 in main at gdb_use.c:41
        breakpoint already hit 1 time
2   breakpoint     keep n   0x08048486 in main at gdb_use.c:42
        breakpoint already hit 1 time
3   breakpoint     keep n   0x0804843d in check at gdb_use.c:27
(gdb) 

A continuación se ejecuta el programa y se puede observar cómo se produce un error en la línea 29 de código, más concretamente en la función check.

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/test/gdb_use 

Program received signal SIGSEGV, Segmentation fault.
0x08048441 in check (table=0x804a008) at gdb_use.c:29
29          if ((table[y].data + 1) != table[y].next->data)
(gdb) 

La línea del error está incluida dentro de un bucle, cuyo índice es la variable y. Para ver en qué iteración se ha producido el error, se imprime el valor de esta variable.

(gdb) p y
$6 = 999
(gdb) 

Se puede comprobar que el error se ha producido en la última de las iteraciones. Evidentemente, mediante los comandos next y step se puede llegar a detener la ejecución antes de que se produzca, pero tal tarea acarrearía introducir demasiados comandos todos idénticos.

Para facilitar esta labor gdb permite la definición de puntos de parada condicionales. Estos puntos se introducen añadiendo a un punto de parada definido anteriormente una condición booleana que si evalua a cierto hace que la ejecución se detenga, mientras que si evalua a falso, el punto de parada no tiene efecto alguno.

Para conseguir que el programa se detenga antes de ejecutar la línea 29 es necesario introducir dos comandos.

(gdb) b 29
Breakpoint 4 at 0x804844f: file gdb_use.c, line 29.
(gdb) condition 4 y == 999
(gdb) 

El primer comando crea un punto de parada incondicional en la línea 29 del fichero gdb_use.c. Esta línea corresponde a la comprobación de la condición en el bucle de la función check. El comando condition añade al punto de parada número 4 la condición y == 999. Nótese que la sintaxis de la condición es directamente código C. A partir de ese momento se puede comprobar la definición del punto de parada mediante el comando info breakpoints.

(gdb) info breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x080484a1 in main at gdb_use.c:41
2   breakpoint     keep n   0x080484b6 in main at gdb_use.c:42
3   breakpoint     keep n   0x0804843d in check at gdb_use.c:27
4   breakpoint     keep y   0x0804844f in check at gdb_use.c:29
	stop only if y == 999
(gdb) 

De los cuatro puntos de parada definidos, los tres primeros están desactivados tal y como indica el cuarto campo, y el último está activado pero se detiene sólo si y == 999.

La inserción de un punto de parada condicional se puede realizar mediante un único comando si se añade al comando breakpoint la palabra if seguida por una condición. En el ejemplo anterior, el punto de parada se puede insertar mediante el comando b 29 if (y == 999).

Si se ejecuta de nuevo el programa, esta vez la ejecución se detiene justo antes de producirse el error.

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/test/gdb_use 

Breakpoint 4, check (table=0x804a008) at gdb_use.c:29
29	    if ((table[y].data + 1) != table[y].next->data)
(gdb) 

Si el error se produce en esta línea, se procede a visualizar mediante el comando print el contenido de las variables y expresiones que forman parte de ella.

(gdb) p table[y].data + 1
$2 = 1000
(gdb) p table[y].next
$3 = (struct data_unit *) 0x0
(gdb) p table[y].next->data
Cannot access memory at address 0x0
(gdb) 

Se puede observar que se realiza una indirección a través del campo next del elemento de la tabla cuyo valor es 0 y por tanto una dirección incorrecta. En este punto se ha localizado el error. Falta descubrir si ese campo tiene el valor correcto, y si es así, por qué se utiliza como indirección (ejercicio que se deja para que lo resuelva el lector).

Si se desea borrar una condición de un punto de parada simplemente se añade una condición vacía (comando condition seguido únicamente del número de punto de parada).

(gdb) condition 4
Breakpoint 4 now unconditional.
(gdb) info breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x080484a1 in main at gdb_use.c:41
2   breakpoint     keep n   0x080484b6 in main at gdb_use.c:42
3   breakpoint     keep n   0x0804843d in check at gdb_use.c:27
4   breakpoint     keep y   0x0804844f in check at gdb_use.c:29
	breakpoint already hit 1 time
(gdb)