05. Depuración: breakpoints II

Publicado por

A estas alturas ya estamos familiarizados con los breakpoints, sabemos que siempre que nuestro programa alcance uno de estos, su ejecución se detendrá. Pero hay escenarios en los que no necesitamos que el programa se detenga siempre que el breakpoint sea alcanzado, por ejemplo, cuando el bug que buscamos corregir se reproduce en las últimas iteraciones de un bucle. Es aquí donde se vuelve útil decidir cuándo quieres que el breakpoint detenga el programa y cuándo no.

Breakpoints condicionales

El siguiente programa es un ejemplo sencillo que ilustra dicha situación:

#include <stdio.h>

int main()
{
  int i;

  for (i = 0; i < 10; i++)
    printf("i vale %d", i);

  return 0;
}

Para decidir cuándo detenernos y cuándo no con el mismo breakpoint, necesitamos agregar una condición al momento de declararlo: break if .

➜ cc -o bucle -g bucle.c
➜ gdb bucle 
...
Leyendo símbolos desde bucle...hecho.
(gdb) break 8 if i > 8
Punto de interrupción 1 at 0x400537: file bucle.c, line 8.
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400537 in main at bucle.c:8
    stop only if i > 8
(gdb) run
Starting program: /home/user/bucle 

Breakpoint 1, main () at bucle.c:8
8       printf("i vale %d", i);
(gdb) print i
$1 = 9
(gdb)

Y listo, de ese modo podemos controlar dónde, y sobre todo cuándo detener la ejecución de nuestro programa.

Asociando comandos a breakpoints

Generalmente cuando la ejecución se detiene en un breakpoint solemos ejecutar los mismos comandos. Bien, GDB nos permite asociar una lista de comandos para que se ejecuten justo después de alcanzar el breakpoint. Para ilustrar esto, te presento una versión recursiva de la función maximo() que aparece en el programa de ejemplo que utilicé en la tercera nota:

int maximo(int *numeros)
{
  int maxv = 1;
  int tam;
  int i;

  /* El primer elemento indica el número de argumentos. */
  tam = *numeros++;

  if (1 == tam)
     maxv = numeros[0]; /* Caso base: el arreglo contiene un solo elemento */
  else
  {
     int a;
     int b;
     
     a = numeros[0];
     numeros[0] = tam - 1;

     b = maximo(numeros);

     maxv = a > b? a : b ;
  }

  return maxv;
}

Al iniciar una sesión del depurador, creamos un breakpoint en la función maximo() y luego asociamos los comandos mediante la instrucción commands, seguida del número de breakpoint al que se van a asociar los comandos, uno por línea. Cuando termines de escribir los comandos, escribe la palabra end para terminar el bloque de instrucciones. Los comandos que indicaste, se ejecutarán inmediatamente después de alcanzar el breakpoint indicado.

➜ gdb maximo
...
Leyendo símbolos desde maximo...hecho.
(gdb) break maximo
Punto de interrupción 1 at 0x4007b8: file maximo.c, line 36.
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004007b8 in maximo at maximo.c:36
(gdb) commands 1
Teclee órdenes para puntos de ruptura 1, una por línea.
End with a line saying just "end".
>printf "Buscando en %d elementos ", numeros[0]
>print *(numeros+1)@numeros[0]
>end
(gdb) run 1 5 3
Starting program: /home/user/maximo 1 5 3

Breakpoint 1, maximo (numeros=0x602010) at maximo.c:36
36    int maxv = 1;
Buscando en 3 elementos $1 = {1, 5, 3}
(gdb) continue
Continuando.

Breakpoint 1, maximo (numeros=0x602014) at maximo.c:36
36    int maxv = 1;
Buscando en 2 elementos $2 = {5, 3}
(gdb) continue
Continuando.

Breakpoint 1, maximo (numeros=0x602018) at maximo.c:36
36    int maxv = 1;
Buscando en 1 elementos $3 = {3}
(gdb) continue
Continuando.
El valor mas grande es 5
[Inferior 1 (process 6434) exited normally]
(gdb)

Espero que estas características de los breakpoints hagan que tus sesiones con el depurador sean más eficientes y te ayuden a encontrar defectos en tus programas con mayor rapidez. Nos vemos en la siguiente nota. ¡Hasta entonces!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *