¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Depurar un fallo en C con gdb

Después de esta lección: Puedo encontrar con un depurador la línea donde se rompe un programa en C.

Lección de 60 min9 comandosLaboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

El programa de notas que se cierra solo

Un depurador es un programa que ejecuta otro paso a paso y enseña su estado. Con gdb encuentras la línea exacta donde un programa en C se rompe, en lugar de adivinarla.

Un programa de notas se cierra con el aviso "Segmentation fault" al calcular la media. Revisar cien líneas a ojo cuesta una tarde, y gdb señala la línea en un minuto.

Al final de esta lección

Nota

Puedo encontrar con un depurador la línea donde se rompe un programa en C.

Palabras clave

PalabraQué significaEjemplo
Depuradorun programa que ejecuta otro y deja mirarlo por dentrogdb
Fallo de segmentaciónel sistema para un programa que toca memoria que no es suya
Punto de interrupciónuna línea donde el depurador detiene el programabreak suma
Traza de pilala lista de funciones activas cuando algo se rompebt
Señalun aviso que el sistema envía a un procesoSIGSEGV
Símbolos de depuraciónlos nombres y líneas que gcc guarda con la opción -g

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un fallo de segmentación es una señal

Cuando un programa lee o escribe memoria que no es suya, el sistema le envía la señal SIGSEGV. El programa acaba y la terminal escribe "Segmentation fault".

El estado de salida de un proceso matado por una señal es 128 más el número de la señal. SIGSEGV es la 11, así que el estado es 139.

El programa de prueba usa un puntero nulo

El programa fallo.c suma tres números de un vector, pero main le pasa NULL en lugar del vector. La línea 8 crea el puntero nulo, la 9 llama a suma, y la 4 intenta leer v[i].

Leer a través de NULL es un acceso a la dirección 0, que nunca pertenece al programa. Compílalo con -g, para que gdb conozca las líneas.

Entra en la carpeta de trabajo

Comando
mkdir -p ~/c && cd ~/c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe fallo.c, con un puntero nulo

Comando
printf '#include <stdio.h>\nint suma(int *v, int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total = total + v[i];\n  return total;\n}\nint main(void) {\n  int *datos = NULL;\n  int r = suma(datos, 3);\n  printf("%%d\\n", r);\n  return 0;\n}\n' > fallo.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila con símbolos de depuración

Comando
gcc -g -O0 -o fallo fallo.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta fallo y mira el estado de salida

Comando
./fallo; echo $?

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

La traza de pila dice quién llamó

Para ejecutar el programa bajo gdb, escribes gdb ./fallo y la orden run. Cuando llega la señal, gdb detiene el programa y no lo mata.

La orden bt muestra la traza de pila: la función donde se rompió y las que la llamaron, cada una con su línea. Con la opción -batch y varias -ex, gdb ejecuta las órdenes y sale solo.

Ejemplo resuelto: leer la traza de fallo

MarcoFunciónLíneaQué significa
#0suma4aquí se rompió: leyó v[i]
#1main9main llamó a suma con datos

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pide la traza y deja solo las líneas

Comando
gdb -batch -ex run -ex bt ./fallo 2>&1 | grep -o 'fallo.c:[0-9]*'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cuenta cuántas veces aparece la señal

Comando
gdb -batch -ex run ./fallo 2>&1 | grep -c SIGSEGV

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

print muestra el valor de una variable

Detenido el programa, print enseña el valor de una variable. Un puntero nulo se muestra como 0x0.

Con break suma pides que gdb pare al entrar en esa función. frame 1 cambia al marco de main, para ver sus variables. Así encuentras la causa: datos valía NULL desde la línea 8.

Para en suma y mira n y v

Comando
gdb -batch -ex 'break suma' -ex run -ex 'print n' -ex 'print v' ./fallo 2>&1 | grep '^\$'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Sube al marco de main y mira datos

Comando
gdb -batch -ex run -ex 'frame 1' -ex 'print datos' ./fallo 2>&1 | grep '^\$'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Intenta leer lo que apunta v

Comando
gdb -batch -ex run -ex 'print *v' ./fallo 2>&1 | grep 'Cannot access'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cuenta los marcos de un informe

Código: python
# Un informe de fallo escrito a mano, como lo imprimiría gdb.
report = [
    "Program received signal SIGSEGV",
    "address 0x0",
    "#0 suma",
    "#1 main",
]
frames = 0
for line in report:
    if line[0] == "#":
        frames = frames + 1
print("marcos:", frames)
print("señal:", report[0])
# Añade la línea "#2 arranque" a la lista y mira cuántos marcos salen.

Un error frecuente

Advertencia

Mucha gente cree que la línea donde se rompe el programa es la línea con el error. Es solo donde se notó. Aquí se rompió en la línea 4, pero el error está en la 8, que creó el puntero nulo. Sube por la traza hasta encontrar el dato mal creado.

Sobre este laboratorio y su código: Laboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.