Depuración - Revision : 1,9 - Departamento de Lenguajes y

Anuncio
Depuración - Revision : 1,9
Herramientas de Programación— Cuadernos de Prácticas
Soluciones
6 de octubre de 2005
Resumen
Ejercicios sobre el uso de un depurador a nivel de código fuente. Caracterı́sticas de gdb.
Dpto. Lenguajes y Sistemas Informáticos
Universidad de Alicante
Índice
1. Depuración
2
1
1.
Depuración
1.
Compilad con gcc -Wall -ggdb depuracion1.c -o depuracion1;
Entrad en el depurador con gdb depuracion1;
Ejecutad el programa con r;
El programa aborta en la lı́nea 11 ejecutando vec[i] = drand48();
Imprimid el valor de i con p i;
Observad que su valor es 629 (este valor puede ser otro);
Observad en el fuente que vec solo tiene espacio para 10 enteros;
Corregid el bucle de la lı́nea 10 de forma que ponga:
for( i = 0; i <dim; i++ )
2.
Compilad con gcc -Wall -ggdb depuracion2.c -o depuracion2;
Entrad en el depurador con gdb depuracion2;
Ejecutad el programa con r;
Dejad que el programa se ejecute un tiempo y cuando creáis que el programa está en el bucle infinito, paradlo con ctrl-C.
Imprimid los valores de las variables i y j.
Continuad ejecutando el programa con c.
Volvedlo a parar y consultad los valores de i y j.
Repetid estas acciones varias veces y observad que el valor de i es siempre
0.
Observad que el problema está en la lı́nea 18.
3.
Compilad con gcc -Wall -ggdb depuracion3.c -o depuracion3;
Entrad en el depurador con gdb depuracion3;
El programa escribe bien los elementos, por tanto supondremos que el
problema está en la función ordenar.
Para examinar como funciona pondremos un punto de parada en la primera lı́nea de la función ordenar con b ordenar, y ejecutaremos el programa
con r.
Cuando pare el programa, lo ejecutaremos paso a paso con n.
El sı́ntoma del programa es que hay valores de vec que cambian; por tanto vamos a observar que pasa cuando se modifican sus elementos. Para
esto ponemos un punto de parada en la lı́nea 21 con b 21 y ejecutamos
el programa hasta este punto con c. El programa para antes de ejecutar la lı́nea aux = vec[i]. En ese momento, miramos el contenido de la
variable vec[i] con p vec[i], ($1 = 0.1766426425429159 en mi caso),
ejecutamos la lı́nea con n y consultamos lo que ha almacenado en aux con
p aux ($4 = 0 en mi caso). Como se ve no se está almacenando en aux el
contenido de vec[i]; esto se debe a que aux está declarada como int en
lugar de double (el tipo de vec[]).
4.
Como se dice en el enunciado, supondremos que el programa ha sido compilado de la siguiente manera:
gcc -Wall -g depuracion4.c -o depuracion4
y que al ejecutarlo se ha producido un volcado de memoria (fichero core
2
en mi caso, en otros sistemas se puede llamar de otra forma).
Para depurarlo ejecutaremos:
gdb depuracion4 core
El gdb se quedará en un estado similar al que estarı́a si hubiésemos ejecutado el programa depuracion4 en su interior.
Ahora, con bt, podemos examinar como estaba la pila de llamadas a funciones en el momento del fallo.
Las primeras llamadas a función que aparecen (las últimas en haberse llamado) corresponden a funciones de la librerı́a de C. Ası́ podemos saber
que la función fact se llamó con el parámetro n = -4.
Para ver porqué se llamó a esta función con este valor, inspeccionamos los
valores de las variables cuando se llamó en el cuerpo de la función comb
(lı́nea 37 del programa). Para esto ejecutaremos frame 4.
Ahora podremos inspeccionar los valores de las variables n y m con las
órdenes p n y p m, respectivamente; ası́ comprobamos que el valor de n m es -4 (con p n - m si es necesario).
Para ver porqué m y n toman esos valores podemos subir un frame con
up y ver que estamos llamando a comb, en el programa principal como
comb(i,5), con i = 1. Esto no es correcto, ya que no tiene sentido calcular el número de formas de tomar un elemento de cinco en cinco.
Ha de cambiarse comb(i,5) a comb(5,i).
5.
Suponemos que el proceso depuracion5 está ejecutándose. Para averiguar su PID utilizamos la orden ps -a. Suponed que el PID del proceso
depuracion5 es 919. Para inspeccionar el estado de la memoria sin parar el programa habrá que ejecutar gdb depuracion5 919. Esta orden
parará momentáneamente el programa y dará el control a gdb. Si inspeccionamos el valor de la variable val (con p val), veremos que tiene un
valor negativo, cosa imposible según la definición del problema.
6.
Primero buscaremos que valor tiene la variable i cuando se queda en el
bucle infinito. Para esto entramos en el depurador con gdb depuracion5
y lo ejecutamos con r. Al cabo de un rato paramos la ejecución del programa con Ctr-C y comprobamos si estamos en el bucle infinito; para ello
miramos si el valor de la variable val (con p val) es negativo. A continuación miramos el contenido de la variable i con p i (en mi caso 113383), y
ponemos un punto de parada condicional al principio del bucle para que
pare la ejecución del programa cuando lleguemos a esta iteración (b 11
if i==113383).
Ahora volvemos a ejecutar el programa (con r) para situarnos al principio
de dicha iteración y poder inspeccionar los cambios en la variable val hasta ver en que punto se vuelve negativa. Para ello ponemos un “watchpoint”
sobre la variable val con la condición de que pare si cambia a un valor negativo (watch val if val <0) y continuamos la ejecución del programa
(con c). El programa para en la lı́nea 16, al ejecutar val = 3 * val + 1,
diciendo que la variable val ha cambiado de 827370449 a -1812855948.
Esto sucede porque estamos ante un problema de desbordamiento en la
3
variable entera val. La forma más directa de arreglarlo es aumentando el
rango de la variable val. Con el compilador gcc se puede hacer fácilmente
definiéndola como long long var. (ATENCIÓN: este es un tipo de datos
propio del compilador gcc; esta no es una solución portable). Notad que
poner el punto de parada inicial no es estrictamente necesario, solo se ha
hecho porque los puntos “watch” son de ejecución muy lenta.
4
Descargar