Depuración de código C++: 8 técnicas que te ahorrarán dolores de cabeza

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Depuración de código C++: cómo dejar de temer al segmentation fault y amar a GDB



¿Alguna vez has pasado tres horas buscando un error que al final resultó ser un break olvidado en un switch o un ; extra después de un if? Si es así, bienvenido al club. La depuración de C++ es un arte que roza el masoquismo. Pero, como cualquier arte, se puede dominar.



En este artículo, no solo veremos "cómo ejecutar GDB", sino técnicas reales que transforman la depuración de una adivinanza en un trabajo detectivesco digno de Sherlock Holmes. Hablaremos sobre sanitizadores modernos, depuración inversa e incluso cómo atrapar un error que solo se reproduce "los viernes por la tarde".



1. Clásico del género: GDB y LLDB



Empecemos por lo fundamental. GNU Debugger (GDB) y su competidor LLDB son las navajas suizas del depurador. Pero la mayoría los usa al 10% de su capacidad. Vamos a solucionarlo.



Puntos de interrupción con condiciones



¿Pones un punto de interrupción en un bucle de 10,000 iteraciones y presionas continue 9,999 veces? Detente. Usa breakpoints condicionales:



(gdb) break main.cpp:42 if i == 5000


Esto te ahorrará horas de vida. En LLDB la sintaxis es similar:



(lldb) breakpoint set --file main.cpp --line 42 --condition 'i == 5000'


Watchpoints: detectando cambios en variables



Imagina: tienes una variable que cambia sin que sepas dónde. Coloca un watchpoint:



(gdb) watch my_variable(gdb) rwatch my_variable   # se activa al leer(gdb) awatch my_variable   # se activa tanto al leer como al escribir


Ahora el programa se detendrá justo en el momento en que alguien toque tu variable. Magia, ¿verdad?



2. Depuración inversa: viaje en el tiempo



La peor pesadilla de un programador: el programa falló, pero no sabes cómo llegó a ese estado. La depuración inversa (reverse debugging) permite "rebobinar" la ejecución hacia atrás.



En GDB, necesitas soporte para record:



(gdb) record(gdb) continue... el programa falló ...(gdb) reverse-step   # paso atrás(gdb) reverse-continue # continuar hacia atrás


En herramientas comerciales (como UndoDB o rr de Mozilla) esto funciona aún mejor. rr (Record and Replay) es una herramienta gratuita que graba la ejecución del programa y luego permite reproducirla una y otra vez:



$ rr record ./my_program$ rr replay(gdb) continue... encontramos el lugar del fallo ...(gdb) reverse-step


Consejo: usa rr para buscar condiciones de carrera (data races): reproduce cada ejecución de forma determinista.



3. Sanitizadores: vacuna contra el comportamiento indefinido



El C++ moderno (a partir de Clang y GCC 4.8+) tiene herramientas integradas que nuestros padres ni soñaban. AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan) y ThreadSanitizer (TSan) son imprescindibles.



Ejemplo: detectando una fuga de memoria



// compilar con: g++ -fsanitize=address -g main.cpp -o main#include <iostream>

void leak() { int* p = new int(42); // olvidamos delete}

int main() { leak(); return 0;}


Lo ejecutamos y vemos:



==12345==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0x7f... in operator new(unsigned long) #1 0x400... in leak() main.cpp:5 #2 0x400... in main main.cpp:10


ASan mostrará la pila de llamadas exacta, incluyendo el número de línea donde ocurrió la asignación. Se acabó el "en algún lugar se fuga la memoria".



UBSan contra el comportamiento indefinido



// compilar con: g++ -fsanitize=undefined -g main.cppint main() {    int x = INT_M                

Blogs

Book Recommendations