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!