GNU DEBUGGER (GDB) CI-4835: Redes de Computadores Ene-Mar 2013 Depuración ● Limpiar, purificar ● Proceso de identificar y corregir errores ● El propósito de GDB es permitir que se vea lo que ocurre “dentro” de otro programa mientras este se ejecuta, o lo que estaba ocurriendo con otro programa, cuando este último falló ● Orden “~$ man gdb” ● http://sourceware.org/gdb/current/onlinedocs/gdb toc.html (manual en línea) ● ● Para usarlo hay que compilar los programas con la opción “-g” (~$ gcc -g <nombre_prog.c>) Otra información en: http://ldc.usb.ve/~figueira/Cursos/ci3825/taller/material/gdb.html Ejemplo #1 /* uno.c v1.0 - cuenta la cantidad de "3" y "7" en una serie numérica que lee de un archivo */ #include <stdio.h> #include <stdlib.h> char line[100]; int data[5]; void get_data (int *data); int main () { int seven_count; int three_count; int index; seven_count=0; three_count=0; get_data (data); for (index=0; index<5; ++index) { if (data[index] == 3) ++three_count; if (data[index] == 7) ++seven_count; } } fprintf (stdout, "\nLa secuencia contiene %d sietes y %d tres.\n", seven_count, three_count); exit (0); void get_data (int *data) { fprintf (stdout, "\nIntroduzca cinco numeros enteros: "); fgets (line, sizeof(line), stdin); sscanf (line, "%d %d %d %d %d", &data[1], &data[2], &data[3], &data[4], &data[5]); } Anomalía en el Ejemplo 1 ● El código no cuenta apropiadamente la cantidad de números “3” que contiene la secuencia Shell GDB ● ● ● Posee un Shell interactivo con funciones de historial (teclas de flechas) y completar la orden, usando la tecla “TAB” Para ayuda desde el shell se puede usar “help” (gdb) help [<comando>]) La orden “list” (alias “l”) muestra el contenido del programa que se ejecuta. (gdb) list [archivo:]funcion (gdb) list [archivo:]linea[,linea] (gdb) list (gdb) list - Puntos de Interrupción Un “Breakpoint” es un punto en el cual se interrumpirá la ejecución del programa y se devolverá el control al “shell” para poder examinar variables u otras operaciones ● La orden “break” (alias “b”) se usa de las siguientes formas: (gdb) break [archivo:]función (gdb) break [archivo:]linea ● La orden “tbreak” se usa para colocar un punto de interrupción antes de la posición indicada (gdb) tbreak [archivo:]linea (gdb) tbreak [archivo:]función ● Un “Watchpoint” es una orden que actúa sobre variables y detiene la ejecución cuando algún contenido cambia (gdb) watch nombre_de_la_variable ● Puntos de Interrupción (cont...) ● Un “Catchpoint” es un punto de interrupción que aplica sobre señales (gdb) catch ● La orden “info” permite visualizar los puntos existentes (gdb) info breakpoints|watchpoints|catch ● ● Se pueden usar las órdenes “delete”, “disable”, “enable” e “ignore” sobre los puntos de interrupción Un punto de interrupción se puede condicionar Corriendo el Programa ● La orden “run” pone a correr el programa (gdb) run [argumentos] ● ● Si se desea ejecutar el código desde el principio se puede usar “bt”, lo cual es una traza hacia atrás (backtrace) Si el programa termina por alguna falla particular, entonces se obtendrá en pantalla algo como: Corriendo el Programa (cont...) Después de que el programa se ha detenido en algún punto de interrupción, para continuar su ejecución se debe usar la orden “continue” (alias “c”) ● La orden “print” (alias “p”) se usa para visualizar el contenido en memoria de alguna variable y puede emplear un formato específico (gdb) print [/fmt] expresión donde /fmt puede estar compuesto por una letra que cambie el formato original de la variable ● La letra de formato puede ser: o octal x hexadecimal d decimal u unsigned decimal f float a address t binary s string c char ● Corriendo el Programa ● La orden “step” restaura la ejecución del programa un paso a la vez. Es decir, ejecuta una única orden (gdb) step ● La orden “next” restaura la ejecución del programa un paso a la vez, pero si dicha orden es la invocación de una llamada a una función, ejecuta a la misma como si fuese una sola orden (gdb) next ● La orden “jump” restaura la ejecución del programa a partir de la línea que se le indica. Es decir, la corrida salta a la línea señalada (gdb) jump nro_linea Resultado del Ejemplo 1 ● ● Después de aplicar los comandos que se muestran en las imágenes, se observa que data[0] contiene 0 y la secuencia es capturada a partir de data[1]. De modo que se cae en cuenta de que se cometió un error al trabajar el índice del arreglo data a partir de “1” y no “0” en la instrucción “sscanf” Es decir, se direccionó un espacio por afuera del arreglo. Por lo tanto la cuenta de los contadores no podía haber sido correcta Algunas órdenes más ● La orden “set” sirve para alterar la ejecución de una variable en medio de la corrida x set variable=expresión ● La orden “display” se emplea para permitir que cada vez que el programa se detenga muestre lo que se le indica display [/fmt] expresión donde /fmt puede estar compuesto por un número -contador-, una letra de formato y una letra de tamaño. ● La letra del tamaño puede ser: b byte h halfword w word g giant (8 bytes) ● Así por ejemplo “(gdb) display /10xb arreglo” indica que se debe mostrar, en notación hexadecimal, el contenido de los siguientes 10 bytes a partir de arreglo Ejemplo #2 /* Segundo ejemplo */ #include <stdio.h> #include <stdlib.h> #include <time.h> int main () { int i, j; long ahora; /* Tomando la semilla desde el reloj del computador. */ srand (time(&ahora) % 37); printf ("\nGenerando un valor pseudoaleatorio:"); /* Se usa el generador de números pseudoaleatorios "rand". Este generador es del tipo congruencial multiplicativo, con un período de 2 elevado a la 32 (4294967296). Es decir, al término de esos intentos vuelve a repetir valores. Los números que produce oscilan entre 0 y 2 elevado a la 15 menos una unidad */ } i=rand (); j=rand(); j-=j; i=i/j; fprintf (stdout, "\ni=%d y j=%d\n", i, j); exit (0); Anomalía en el Ejemplo 2 ● El código no termina de ejecutarse, ya que se presenta una excepción, se aborta la corrida y se genera un archivo de vaciado de la memoria Lectura del archivo Core ● La orden para leer el “core” es: ~$ gdb -c <archivo_core> <archivo_ejecutable> Resultado del Ejemplo 2 ● ● Al leer el archivo “core” GDB indica que el programa terminó con una señal tipo “8”. Al ejecutar la orden “~$ man 7 signal” se puede constatar que se trata de SIGFPE, una anomalía del tipo expresión aritmética real El depurador muestra además que la operación es una división. Cuando se examinan los valores de las variables, se observa que el problema es que se está dividiendo por cero Algunas órdenes más ● La pila de ejecución del proceso puede examinarse con el comando “backtrace”, alias “bt” (gdb) backtrace #0 func2 (x=30) at test.c:5 #1 0x80483e6 in func1 (a=30) at test.c:10 #2 0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19 #3 0x40037f5c in __libc_start_main () from /lib/libc.so.6 (gdb) El ejemplo muestra que func2() fue invocada por func1(), que a su vez fue invocada por main(). Cada frame tiene un número a la izquierda que lo identifica ● Existen órdenes para examinar y manipular el “stack”, incluso con aquellas variables que se encuentran en otros contextos de ejecución que no son el local. La orden “frame” permite cambiar a algún marco de ejecución almacenado en la pila frame [N] Algunas órdenes más (cont...) ● (gdb) backtrace #0 func2 (x=30) at test.c:5 #1 0x80483e6 in func1 (a=30) at test.c:10 #2 0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19 #3 0x40037f5c in __libc_start_main () from /lib/libc.so.6 (gdb) frame 2 #2 0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19 19 x = func1(x); (gdb) El ejemplo muestra como, a pesar de haber ocurrido un punto de interrupción en func2(), se pasa a operar en el “marco” apilado para main() ● La orden “info frame” se usa para mostrar la información vinculada con el “marco corriente” Algunas órdenes más (cont...) ● ● La orden “info locals” se usa para mostrar la información de las variables guardadas en el marco La orden “info args” se emplea para visualizar los argumentos registrados en el marco (gdb) info frame Stack level 2, frame at 0xbffffa8c: eip = 0x8048414 in main (test.c:19); saved eip 0x40037f5c called by frame at 0xbffffac8, caller of frame at 0xbffffa5c source language c. Arglist at 0xbffffa8c, args: argc=1, argv=0xbffffaf4 Locals at 0xbffffa8c, Previous frame's sp is 0x0 Saved registers: ebp at 0xbffffa8c, eip at 0xbffffa90 (gdb) info locals x = 30 s = 0x8048484 "Hello World!\n" (gdb) info args argc = 1 argv = (char **) 0xbffffaf4 Algunas órdenes más (cont...) ● ● El ejemplo anterior muestra el contenido de los registros Instruction Pointer (EIP) y Base Pointer (EBP) de un procesador del tipo Intel familia 8088. Conociendo el código que se procesa, comprendiendo los principios básicos de manejo de bajo nivel de un procesador -ensamblador- y con práctica suficiente, se puede identificar y corregir los errores Hay más alternativas de órdenes en GDB, pero esta presentación es únicamente una introducción Mayor información en: http://www.dirac.org/linux/gdb/ http://gnu.org/software/gdb/docume ntation/