Docsity
Docsity

Prepara tus exámenes
Prepara tus exámenes

Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity


Consigue puntos base para descargar
Consigue puntos base para descargar

Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium


Orientación Universidad
Orientación Universidad


Modolu 2 De software Practicas, Ejercicios de Ingeniería Infórmatica

Asignatura: Fundamentos del Software, Profesor: Garvi, Eladio, Carrera: Ingeniería Informática, Universidad: UGR

Tipo: Ejercicios

2014/2015

Subido el 16/01/2015

franaguilera
franaguilera 🇪🇸

1 documento

1 / 24

Toggle sidebar

Esta página no es visible en la vista previa

¡No te pierdas las partes importantes!

bg1
Prácticas de Fundamentos del Software Módulo II. Compilación y depuración de programas
Fundamentos del Software
Prácticas
Módulo II
Compilación y depuración de programas
7-oct-2014
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18

Vista previa parcial del texto

¡Descarga Modolu 2 De software Practicas y más Ejercicios en PDF de Ingeniería Infórmatica solo en Docsity!

Prácticas de Fundamentos del Software Módulo II. Compilación y depuración de programas

Fundamentos del Software

Prácticas

Módulo II

Compilación y depuración de programas

7 - oct- 2014

Lenguajes y Sistemas Informáticos Universidad de Granada

Grado en Ingeniería Informática y

Doble Grado en Ingeniería Informática y Matemáticas

Departamento de Lenguajes y Sistemas Informáticos

E.T.S. Ingenierías Informática y de Telecomunicación

Universidad de Granada

Lenguajes y Sistemas Informáticos Universidad de Granada

Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas

Práctica 8: Compilación de programas

1 Objetivos principales

 Conocer cómo la utilidad gcc/g++ realiza las distintas etapas del proceso de generación de un archivo

ejecutable a partir de distintos archivos de código fuente.

 Conocer las dependencias que se producen entre los distintos archivos implicados en el proceso de

generación de un archivo ejecutable.

 Saber construir un archivo makefile sencillo que permita mantener las dependencias entre los distintos

módulos de un pequeño proyecto software.

Además, se verán las siguientes órdenes:

Utilidades

gcc/g++ ar make

Tabla 8. 1. Órdenes de la sesión.

2 Introducción a la compilación de programas con gcc/g++

GCC es un compilador integrado del proyecto GNU para los lenguajes C, C++, Objective C y Fortran. A partir de un

archivo que contiene un programa escrito en cualquiera de estos lenguajes puede generar un programa ejecutable

binario en el lenguaje de la máquina donde queremos que se ejecute el programa. En la figura 1 podemos ver el

proceso normal de compilación.

El preprocesador (del inglés, preprocessor) acepta como

entrada un archivo que contiene código fuente ( archivo.c o

archivo.cpp ) y se encarga de eliminar los comentarios e

interpretar las directivas de preprocesamiento. Estas directivas

siempre comienzan por el símbolo # (sí, el mismo que precede

a los comentarios en Bash). La más interesante por ahora de

cara a nuestra práctica es **#include ** o

#include ”archivoinc.h” que sustituye la línea por el

contenido del archivo archivoinc o archivoinc.h.

El compilador (del inglés, compiler) analiza el código fuente

preprocesado y lo traduce a un código objeto que se almacena

en un archivo archivoinc.o , conocido como módulo

objeto. En el proceso de compilación se realiza la traducción

del código fuente a código objeto pero no se resuelven las

posibles referencias a elementos externos al archivo. Las

referencias externas se refieren a variables y funciones que,

aunque se utilizan en el archivo, y por tanto deben estar

declaradas, no se encuentran definidas en éste, sino en otro

archivo distinto.

El enlazador (del inglés, linker) se encarga de resolver las

referencias externas y generar un archivo ejecutable. Estas Figura 8.1. Proceso normal de compilación en C/C++.

Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas

módulos objeto podemos generar una biblioteca utilizando la orden ar. Puede comprobar lo que significan las

diferentes opciones utilizadas mediante el manual en línea.

En este caso, los siguientes archivos se encuentran en el subdirectorio dirprograma2.

$ g++ - c sin.cpp

$ g++ - c cos.cpp

$ g++ - c tan.cpp

$ ar - rvs libmates.a sin.o cos.o tan.o

Para comprobar el uso que podemos hacer de nuestra biblioteca vamos a generar un programa ejecutable,

programa2, a partir de los siguientes módulos de código objeto: main2.o, factorial.o, hello.o que fueron

compilados cada uno mediante g++ - c.

$ g++ - o programa 2 main 2 .o factorial.o hello.o

main 2 .o: In function 'main':

main 2 .cpp:(.text+ 0 x 58 ): undefined reference to 'print_sin(float)'

main2.cpp:(.text+0x65): undefined reference to 'print_cos(float)'

main 2 .cpp:(.text+ 0 x 72 ): undefined reference to 'print_tan(float)'

collect 2 : ld returned 1 exit status

Ejercicio 8. 2. Explique por qué el enlazador no ha podido generar el programa archivo ejecutable programa2 del

ejemplo anterior y, sin embargo, ¿por qué sí hemos podido generar el módulo main2.o?

Para generar el programa ejecutable hay que especificar explícitamente la(s) biblioteca(s) que se utilizan (vamos,

las definiciones de las funciones).

$ g++ - L./ - o programa2 main2.o factorial.o hello.o - lmates

La opción - L permite especificar directorios en donde g++ puede buscar las bibliotecas necesarias. Por omisión

g++ las busca en los directorios /lib y /usr/lib. Además, en el caso de – lmates, esa opción – l busca la

biblioteca cuya raíz es mates, con prefijo lib y sufijo .a, es decir, busca la biblioteca libmates.a.

Pruebe lo siguiente.

$ mkdir includes

$ mv *.h includes

$ rm *.o programa 2

$ g++ - L./ - o programa 2 main 2 .cpp factorial.cpp hello.cpp - lmates

main2.cpp:2:23: error: functions.h: No such file or directory

main 2 .cpp:3:19: error: mates.h: No such file or directory

main 2 .cpp: In function ‘int main()’:

main 2 .cpp: 8 : error: ‘print_hello’ was not declared in this scope

main 2 .cpp: 10 : error: ‘factorial’ was not declared in this scope

main 2 .cpp: 11 : error: ‘print_sin’ was not declared in this scope

main 2 .cpp: 12 : error: ‘print_cos’ was not declared in this scope

main 2 .cpp: 13 : error: ‘print_tan’ was not declared in this scope

factorial.cpp:1:23: error: functions.h: No such file or directory

hello.cpp:2:23: error: functions.h: No such file or directory

Ejercicio 8. 3. Explique por qué la orden g++ previa ha fallado. Explique los tipos de errores que ha encontrado.

De forma análoga a la opción - L, la opción - I permite especificar directorios en donde g++ puede buscar los

archivos de cabecera (por omisión, se buscan en /usr/include). Esta opción no tiene sentido si todos los

archivos de cabecera se encuentran en el directorio donde estamos ejecutando todas estas órdenes. En ese tipo de

circunstancias, NO es necesario definir tal opción e indicar directorios concretos.

Lenguajes y Sistemas Informáticos Universidad de Granada

$ g++ - I./includes - L./ - o programa 2 main 2 .cpp factorial.cpp hello.cpp - lmates

Como puede comprobar, cuando se trabaja con varios módulos de código fuente se van generando una serie de

dependencias entre ellos. En nuestro caso, el módulo main.cpp depende de los módulos factorial.cpp y

hello.cpp. Además, el módulo main2.cpp depende de los anteriores y de la biblioteca libmates.a. Si en

algún momento es necesario modificar algún módulo, se hace necesario conocer estas dependencias para generar

un nuevo programa ejecutable. Por ejemplo si suponemos que el módulo hello.cpp cambia la línea: cout <<

"Hello World!"; por cout << "Hello happy World!"; sería necesario generar de nuevo el módulo

objeto correspondiente y generar de nuevo el programa1 y el programa2.

4 Uso de archivos de tipo makefile

Afortunadamente existe una utilidad make que permite gestionar las dependencias (y otras muchas cosas),

comprobando qué archivos se han modificado desde la última vez que se ejecutó para construir el archivo

ejecutable y, en su caso, vuelve a construirlo haciendo de nuevo sólo lo que sea necesario, es decir, compilando

exclusivamente aquellos archivos que hubieran sido modificados.

La idea es especificar en un documento de texto las dependencias entre los archivos y las acciones que deben

llevarse a cabo si se produce alguna modificación. Por ejemplo, si sólo hemos modificado uno de los archivos

fuente o hemos perdido el archivo objeto correspondiente, la utilidad make solamente volverá a generar ese

archivo objeto y realizará la fase de enlazado para construir el archivo ejecutable.

Por ejemplo, como se ha visto en la parte introductoria de esta sesión, si deseamos compilar y enlazar de forma

manual una serie de archivos fuente como son main.cpp, factorial.cpp y hello.cpp para obtener el

archivo ejecutable ejemplo, habría que ejecutar lo siguiente:

$ g++ - o programa1 main.cpp factorial.cpp hello.cpp

Esta ejecución compila cada archivo fuente y genera sus correspondientes archivos objeto main.o, factorial.o

y hello.o, respectivamente, y a continuación los enlaza para obtener el archivo ejecutable denominado

programa1.

Es posible automatizar el proceso de compilación y enlazado construyendo un archivo de tipo makefile en el que se

especifiquen las acciones a realizar. En las siguientes secciones se describirá cómo hacerlo.

4.1 Ejecución de la utilidad make

La utilidad make admite entre sus opciones la especificación del nombre de un archivo de tipo makefile. Esa opción

es – f y, a continuación, el nombre del archivo. Si el nombre elegido para el archivo es makefileGNU, makefile

o Makefile, en ese caso, no es necesario especificar la opción anterior junto al nombre del achivo, es decir,

bastará con ejecutar la utilidad make sin argumentos.

Por ejemplo, suponiendo que el único archivo makefile se denomina makefileA, se observa el siguiente resultado

tras la ejecución de la orden make.

$ make

make: *** No se especificó ningún objetivo y no se encontró ningún makefile. Alto.

Para que admita el ese archivo makefile, se ha de especificar su nombre mediante la opción – f.

$ make - f makefileA

En cambio, si el archivo se denominase makefile, entonces la ejecución de la orden make no necesitaría tal

opción.

Lenguajes y Sistemas Informáticos Universidad de Granada

main.o: main.cpp

g++ - I./includes - c main.cpp

factorial.o: factorial.cpp

g++ - I./includes - c factorial.cpp

hello.o: hello.cpp

g++ - I./includes - c hello.cpp

Nótese que para el objetivo programa1 existen tres dependencias que son las de la existencia de los archivos

objeto main.o, factorial.o y hello.o. Dichas dependencias son, a su vez, otros objetivos descritos en el

mismo archivo makefile.

Suponiendo que no existen los archivos objeto ni el ejecutable, la ejecución de la utilidad make con este archivo

arrojaría el siguiente resultado:

$ make – f makefileB

g++ - I./includes - c main.cpp

g++ - I./includes - c factorial.cpp

g++ - I./includes - c hello.cpp

g++ - o programa 1 main.o factorial.o hello.o

Si ahora borramos uno de los archivos objeto, por ejemplo factorial.o, el resultado de ejecutar la utilidad

make sería el siguiente:

$ make – f makefileB

g++ - I./includes - c factorial.cpp

g++ - o programa 1 main.o factorial.o hello.o

En esta ejecución podemos observar que sólo será necesaria la obtención del archivo objeto factorial.o y el

posterior enlazado dado que los otros dos archivos objeto ya existían y no han sido modificados sus archivos

fuente.

Basándonos en este mismo ejemplo, el archivo functions.h está incluido en los archivos fuente main.cpp,

factorial.cpp y hello.cpp. ¿Qué ocurriría si modificásemos el archivo functions.h? En ese caso, no se

recompilaría absolutamente nada porque en el archivo makefileB no existe ninguna dependencia en la que

intervenga. Para solucionarlo, sólo hemos de incluir este archivo de cabecera en las dependencias de aquellos

objetivos donde su archivo fuente lo incluya. Si el archivo functions.h estuviera ubicado en el mismo directorio

que el resto de archivos fuente, el nuevo archivo makefile, que denominaremos makefileC, quedaría de la

siguiente forma:

programa 1 : main.o factorial.o hello.o

g++ - o programa1 main.o factorial.o hello.o

main.o: main.cpp functions.h

g++ - c main.cpp

factorial.o: factorial.cpp functions.h

g++ - c factorial.cpp

hello.o: hello.cpp functions.h

g++ - c hello.cpp

Suponiendo que se modifica el archivo functions.h, se deberían recompilar los archivos main.cpp,

factorial.cpp y hello.cpp. Pruébelo y compruebe que se realiza tal y como se ha explicado.

Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas

Es importante reseñar que este ejemplo, descrito según el makefile anterior, funciona perfectamente pues

detectaría posibles cambios realizados al archivo functions.h al encontrarse éste en el mismo directorio que el

resto de los archivos fuente.

Si el archivo functions.h estuviese en otro directorio, habría que indicar la ruta para acceder a él tanto en la

dependencia como en la orden donde interviene. En el siguiente ejemplo, suponiendo que se encuentra en un

subdirectorio denominado includes, el archivo makefileC, ahora denominado makefileD, pasaría a ser el

siguiente:

programa 1 : main.o factorial.o hello.o

g++ - o programa1 main.o factorial.o hello.o

main.o: main.cpp ./includes/functions.h

g++ - I./includes - c main.cpp

factorial.o: factorial.cpp ./includes/functions.h

g++ - I./includes - c factorial.cpp

hello.o: hello.cpp ./includes/functions.h

g++ - I./includes - c hello.cpp

Es posible construir una regla que no tenga órdenes asociadas. En este caso, cuando make trate de construir el

objetivo de esta regla simplemente comprobará que los archivos de la lista de dependencias están actualizados. Si

es necesario construir alguno, pasa a ejecutar la regla que tiene este archivo como objetivo. Este tipo de reglas se

conocen como objetivos simbólicos o destinos simbólicos. Si no se especifica ninguna regla en la ejecución de la

utilidad make, se procesan las reglas desde la primera que se encuentre en adelante. Para aprender más sobre la

ejecución de la orden make puede consultar el manual en línea.

Además, se puede invocar una regla sin dependencias a la hora de ejecutar la utilidad make. A ese tipo de reglas

se las denomina reglas virtuales. Por ejemplo, podemos crear una regla virtual cuyo objetivo denominaremos

clean y cuya funcionalidad sea la de eliminar los archivos objeto generados. Recuerde que, al no ser un objetivo

que intervenga en la consecución del objetivo principal, generalmente se sitúan al final de las demás reglas.

A continuación se puede ver cómo quedaría dentro de un archivo makefile cualquiera:

clean:

rm *.o

Para ejecutar esta última regla es necesario especificar el nombre del objetivo a la hora de invocar a la utilidad

make, tal y como se muestra a continuación:

$ make - f makefile clean

Busque en internet el problema que se puede dar en las reglas virtuales cuando se crea un archivo en el directorio

con el mismo nombre de la regla (por ejemplo, clean). ¿Cómo se soluciona?

En general, la utilidad make, permite la ejecución de una regla cualquiera invocando el nombre del objetivo de la

regla como argumento del make.

En un archivo de tipo makefile, también se pueden añadir comentarios anteponiendo el símbolo # en su primera

columna y se pueden extender a lo largo de toda una línea de texto. Si deseamos varias líneas de comentario cada

una de ellas deberá comenzar por #.

Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas

Variable que indica el compilador que se va a utilizar

CC=g++

Variable que indica el directorio en donde se encuentran los archivos de cabecera

INCLUDE_DIR= ./includes

Variable que indica el directorio en donde se encuentran las bibliotecas

LIB_DIR= ./

programa 2 : main 2 .o factorial.o hello.o libmates.a

$(CC) - L$(LIB_DIR) - o programa 2 main 2 .o factorial.o hello.o - lmates

main 2 .o: main 2 .cpp

$(CC) - I$(INCLUDE_DIR) - c main2.cpp

factorial.o: factorial.cpp

$(CC) - I$(INCLUDE_DIR) - c factorial.cpp

hello.o: hello.cpp

$(CC) - I$(INCLUDE_DIR) - c hello.cpp

libmates.a: sin.o cos.o tan.o

ar - rvs libmates.a sin.o cos.o tan.o

sin.o: sin.cpp

$(CC) - I$(INCLUDE_DIR) - c sin.cpp

cos.o: cos.cpp

$(CC) - I$(INCLUDE_DIR) - c cos.cpp

tan.o: tan.cpp

$(CC) - I$(INCLUDE_DIR) - c tan.cpp

En concreto, se han definido las siguientes variables: CC , INCLUDE_DIR y LIB_DIR. Estas variables se pueden

usar posteriormente en las declaraciones de las reglas simplemente incluyéndolas entre paréntesis o llaves y

anteponiéndoles el signo $. Además existen variables especiales que actúan cuando make procesa cada regla. A

continuación se muestran algunas de ellas:

Variable Significado

Representa el nombre del objetivo de la regla en la que nos encontramos

Representa la primera dependencia de la regla en la que nos encontramos

Representa las dependencias de la presente regla que hayan sido actualizadas (modificadas)

dentro del objetivo de la regla y separadas por un espacio en blanco

$^

Representa todas las dependencias separadas por un espacio en blanco

Tabla 8.2. Variables especiales de la orden make.

En los siguientes epígrafes se mostrarán ejemplos de uso de estas variables. Para ello vamos a suponer las

siguientes declaraciones en un archivo makefile:

CC = g++

CPPFLAGS = - Wall

SRCS = main.cpp factorial.cpp hello.cpp

OBJS = main.o factorial.o hello.o

HDRS = functions.h

En la variable CPPFLAGS se indican las opciones del compilador. En este caso, la opción – Wall sirve para mostrar

todos los warnings que pudieran aparecer durante el proceso de compilación.

Lenguajes y Sistemas Informáticos Universidad de Granada

4.3.1 Uso de $@

El caso que se muestra a continuación sirve para representar el nombre que se le asociará al programa ejecutable

usando para ello el mismo nombre asignado al objetivo:

programa1: $(OBJS)

$(CC) - o $@ $(OBJS)

Como puede verse, el valor de $@ en la regla se sustituirá por programa1.

4.3.2 Uso de $<

Esta variable se utiliza para representar el archivo aportado como primera dependencia en una regla. De este

modo, por ejemplo, en un proceso de compilación, a la hora de indicar la orden para la obtención del archivo

objeto, en lugar de referenciar el nombre de ese primer archivo, se puede representar con esta variable.

hello.o: hello.cpp

$(CC) - c $(CPPFLAGS) $<

Como puede verse en esta ocasión, el valor de $< en la regla se sustituirá por hello.cpp, es decir, la primera

dependencia (y única en esta ocasión) de la regla.

4.3.3 Uso de $?

Cuando se desea hacer referencia a varias de las dependencias a la hora de actuar en consecuencia con las

órdenes de una regla, es posible usar esta variable para indicarlas y, en concreto, referenciaría aquellas

dependencias que se hubieran actualizado:

print: $(SRCS)

lpr - p $?

Con esta regla, si se ejecutase la utilidad make sobre este archivo makefile junto con el argumento print, la

orden imprimiría aquellos archivos que hubieran sido modificados hasta ese momento. Esta opción es muy útil

cuando se desea disponer de una copia impresa de los archivos fuente. Si en un momento anterior se hubieran

imprimido todos los archivos fuente y, posteriormente, se hubiera modificado sólo uno de ellos, la acción se llevaría

a cabo imprimiendo únicamente ese archivo y no los demás.

4.3.4 Uso de $^

Cuando se desea referenciar a todos los archivos indicados en las dependencias de una regla, lo cual supondría el

ahorro de tener que escribirlos en varios lugares del archivo makefile, se utiliza la variable $^.

programa1: $(OBJS)

$(CC) - o $@ $^

Como puede verse en esta ocasión, el valor de $^ en la regla se sustituirá por los nombres de todas sus

dependencias, es decir, por la secuencia main.o factorial.o hello.o. Además, tal y como se ha mostrado

anteriormente, se usa la variable $@ para indicar el nombre del objetivo como nombre de archivo ejecutable.

4.3.5 Otras opciones y variables

Si la utilidad make se ejecuta con la opción – p nos muestra las variables predefinidas que se pueden usar dentro

de la especificación de un archivo makefile.

Lenguajes y Sistemas Informáticos Universidad de Granada

Ejercicio 8. 10. Con la siguiente especificación de módulos escriba un archivo denominado makefilePolaca que

automatice el proceso de compilación del programa final de acuerdo a la siguiente descripción:

Compilador: gcc o g++

Archivos cabecera: calc.h (ubicado en un subdirectorio denominado cabeceras)

Archivos fuente: main.c stack.c getop.c getch.c

Nombre del programa ejecutable: calculadoraPolaca

Además, debe incluir una regla denominada borrar, sin dependencias, cuya funcionalidad sea la de eliminar los

archivos objeto y el programa ejecutable.

Lenguajes y Sistemas Informáticos Práctica 9. Depuración de programas

Práctica 9 : Depuración de programas

9.1 Objetivos principales

 Conocer cómo la utilidad gdb es capaz de seguir la traza de ejecución en ejecutables compilados con código

fuente en un lenguaje admitido por gcc (C++).

 Conocer las herramientas básicas de depurado y obtención de información de gdb.

 Saber construir guiones para gdb para automatizar la depuración.

 Conocer el manejo de marcos (frames) en gdb.

 Saber utilizar las órdenes de gdb para modificar la ejecución de un programa y sus datos.

 Conocer las órdenes avanzadas de depuración de procesos en gdb.

Además, se verán las siguientes órdenes:

Utilidades

g++ gdb make

Tabla 9. 1. Órdenes de la sesión.

9.2 Introducción a la depuración de programas con gdb

La utilidad gdb o GNU Debugger es el depurador estándar para el sistema operativo GNU. Es un depurador

portable que se puede utilizar en varias plataformas Unix y funciona para varios lenguajes de programación como

ensamblador, C, C++ o Fortran.

Esta utilidad ofrece la posibilidad de trazar y modificar la ejecución de un programa. El usuario puede controlar y

alterar los valores de las variables internas del programa.

Además, la utilidad no contiene su propia interfaz gráfica de usuario y por defecto se controla mediante una

interfaz de línea de órdenes. Existen diversos front-end que han sido diseñados para gdb, como DDD,

GDBtk/Insight y el "modo GUD" en Emacs.

Por tanto, gdb, permite ver qué pasa dentro de un programa cuando éste se ejecuta, o qué pasó cuando el

programa dio un fallo y abortó.

El esquema normal de funcionamiento es el siguiente:

  1. Compilar el programa con g++ (o gcc) con la opción - g que añade información necesaria para gdb de cara

a poder depurar el programa. Esta opción – g se ha de situar en la fase de compilación, aunque si con una

sola orden se efectúa la compilación y el enlazado, entonces se deberá incluir igualmente en esa orden (ver

ejemplo más abajo).

  1. Ejecutar el depurador gdb.
  2. Dentro del intérprete del depurador, ejecutar el programa con la orden run.
  3. Mostrar el resultado que aparece tras la ejecución.

Tomando los archivos de esta sesión de prácticas, procederemos a generar un archivo ejecutable a partir de

algunos de los archivos fuentes y luego ejecutaremos el depurador añadiendo, como argumento, el archivo

ejecutable obtenido fruto del proceso de compilación.

$ g++ - g main.cpp hello.cpp factorial.cpp - o ejemplo 1

Lenguajes y Sistemas Informáticos Práctica 9. Depuración de programas

9.5 Puntos de ruptura simples

En gdb se pueden añadir puntos de ruptura simple que permiten examinar qué hace el programa en un

determinado lugar. Para ello se puede utilizar la orden break. Esta orden puede tomar como parámetro el nombre

de una función, la dirección lógica donde parar, o un número de línea. Para continuar el programa hasta el final o

hasta el próximo punto de ruptura (lo que llegue antes), se puede utilizar la orden continue.

Ejercicio 9. 3. Ponga un punto de ruptura asociado a cada línea del programa fuente mainsesion09a.cpp

donde aparezca el comentario /* break */. Muestre información de todas las variables que se estén usando

cada vez que en la depuración se detenga la ejecución. Muestre la información del contador de programa mediante

$pc y el de la pila con $sp.

Una vez detenidos a causa a un punto de ruptura, podremos avanzar a la siguiente instrucción del programa con la

orden next o con step. Ambas órdenes se verán en la siguiente sesión.

Los puntos de ruptura activos pueden verse con info breakpoints. Podemos eliminar un punto de ruptura con

la orden delete (para mayor detalle, vea la ayuda mediante la orden help delete).

Ejercicio 9. 4. Indique las órdenes necesarias para ver el valor de las variables final1 y final2 del programa

generado en el ejercicio anterior en los puntos de ruptura correspondientes tras un par de iteraciones en el bucle

for. Indique la orden para obtener el código ensamblador de la zona depurada.

Ejercicio 9. 5. Considerando la depuración de los ejercicios anteriores, elimine todos los puntos de ruptura salvo el

primero.

9.6 Ejecución de guiones

Para no tener que escribir las acciones en la propia interfaz de gdb, podemos hacer uso de los guiones de gdb.

Un guion de este tipo es un archivo de texto con diversas líneas de órdenes. Por ejemplo, el siguiente cuadro

indica un guion denominado guion.gdb con órdenes para la depuración de un programa denominado ejemplo

y, justo debajo, cómo invocar a dicho guion cuando se desee aplicar la depuración establecida en el guion al

programa en cuestión:

break multiplica

run

display x

display y

display final

continue

continue

delete display 1

delete display 2

continue

$ gdb - x guion.gdb ejemplo 1

Ejercicio 9. 6. Realice las acciones del ejercicio 9.3 y las del ejercicio 9.5 en un guion y ejecútelas de nuevo

mediante la opción - x de gdb. ¿Sabría decir qué hace este programa con la variable final2?

Ejercicio 9. 7. Realice la depuración del programa ejecutable obtenido a partir del archivo fuente

ejsesion09a.cpp. Utilizando gdb, trate de averiguar qué sucede y por qué no funciona. Intente arreglar el

programa.

Lenguajes y Sistemas Informáticos Universidad de Granada

9.7 Depuración avanzada de programas con gdb: marcos(frames)

Un programa contiene información acerca de las direcciones donde se van a ejecutar determinadas funciones del

mismo. A esta información se le denomina la pila de llamadas (call stack). Esta clase de pila también se conoce

como una pila de ejecución, pila de control, pila de función, o pila de tiempo de ejecución, y a menudo se describe

en forma abreviada como “la pila”.

La pila de llamadas se divide en secciones contiguas llamadas pila de marcos ( stack frames) o simplemente

marcos ( frames). Cada marco es el conjunto de datos asociados con una llamada a una función. El marco contiene

los argumentos que se le da a la función, sus variables locales, y la dirección en la cual dicha función se ejecuta.

Cuando el programa comienza, la pila contiene solamente un único marco (en C/C++ el marco contiene solamente

la función main). Cada vez que se llama a una función, se crea un nuevo marco. Cada vez que la función devuelve

algo, el marco asignado a dicha función se elimina. El marco de la función que se está ejecutando actualmente se

denomina marco más interno ( innermost frame).

En el programa, el marco se identifica por su dirección. Un marco consta de muchos bytes, cada uno de los cuales

tiene su propia dirección. Normalmente esta dirección se almacena en un registro llamado registro puntero al

marco ( frame pointer register) mientras se ejecuta el siguiente marco.

La utilidad gdb emplea marcos en la depuración de un programa. La instrucción info frame muestra información

acerca del marco actual. Mientras que backtrace full nos muestra la información referente a las variables

locales y el resto de información asociada al marco.

Usando los archivos de esta sesión, compile con opciones de depuración el archivo mainsesion09b.cpp y

genere su programa ejecutable llamado ejemplo09b.

$ g++ - g – o ejemplo09b mainsesion 0 9b.cpp

Usando el archivo ejecutable obtenido tras la compilación, un ejemplo de información del marco de este ejecutable

sería:

$ gdb ejemplo09b

GNU gdb (Ubuntu/Linaro 7.3- 0 ubuntu 2 ) 7.3-2011.

Copyright (C) 2011 Free Software Foundation, Inc.

License GPLv 3 +: GNU GPL version 3 or later

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type "show copying"

and "show warranty" for details.

This GDB was configured as "i 686 - linux-gnu".

Para las instrucciones de informe de errores, vea:

...

Leyendo símbolos desde /home/usuario/ejemplo09b...hecho.

(gdb) break cuenta

Punto de interrupción 1 at 0x80485aa: file mainsesion09b.cpp, line 13.

(gdb ) run

Starting program: /home/usuario/ejemplo09b

Breakpoint 1, cuenta (y=0) at mainsesion09b.cpp: 13

13 tmp = y + 2;

(gdb) info frame

Stack level 0 , frame at 0 xbffff 2 b 0 :