Cómo funciona el depurador de Rust bajo el capó: análisis paso a paso

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

Depurador de Rust: cómo mirar bajo el capó paso a paso

\n\n

¿Alguna vez te has preguntado: «Depurador interactivo paso a paso para Rust: ¿cómo funciona bajo el capó?» Si has escrito en Rust y has intentado atrapar un error difícil, seguramente te has topado con GDB, LLDB o incluso con herramientas como rust-gdb. Pero, ¿cómo logran exactamente detener la ejecución del programa, mostrar los valores de las variables y avanzar paso a paso por el código fuente?

\n\n

En este artículo no solo te contaremos cómo usar el depurador. Miraremos en su interior: desde el código máquina hasta los puntos de interrupción, desde los marcos de pila hasta los símbolos de depuración. Si te interesa no solo qué hacer, sino también por qué funciona, estás en el lugar correcto.

\n\n

¿Por qué Rust es un caso especial para la depuración?

\n\n

Rust se compila a código nativo a través de LLVM. Esto significa que para la depuración usamos las mismas herramientas que para C/C++: GDB y LLDB. Pero Rust añade sus propias complejidades:

\n\n
    \n
  • Propiedad y préstamo: las variables pueden ser movidas, y el depurador debe rastrear esto.
  • \n
  • Monomorfización de genéricos: cada T se convierte en un tipo concreto, y los nombres de las funciones se vuelven «encriptados».
  • \n
  • Optimizaciones: el compilador puede reordenar el código, y el depurador debe «desenredar» esto de vuelta.
  • \n
\n\n

Es por eso que el depurador interactivo paso a paso para Rust no es solo ejecutar un programa bajo GDB. Es todo un sistema que traduce las direcciones de máquina de vuelta a líneas de código fuente.

\n\n

¿Cómo funciona un punto de interrupción (breakpoint) bajo el capó?

\n\n

Imagina: colocas un punto de interrupción en la línea println!("Hello");. ¿Qué sucede en el procesador?

\n\n

Cuando ejecutas un programa bajo el depurador, este hace lo siguiente:

\n\n
    \n
  1. Lee la información de depuración (formato DWARF) del archivo ejecutable. Allí se almacena la correspondencia: dirección en memoria → línea en el código.
  2. \n
  3. Reemplaza la instrucción en esa dirección por un comando especial INT3 (en x86 — 0xCC). Esto es una «trampa» para el procesador.
  4. \n
  5. Cuando la ejecución llega a este punto, el procesador genera una señal SIGTRAP.
  6. \n
  7. El depurador intercepta la señal, guarda el estado de los registros y te muestra la línea actual.
  8. \n
\n\n

Aquí hay un ejemplo de un programa simple en Rust:

\n\n
fn main() {\n    let x = 42;\n    let y = x + 10; // <-- punto de interrupción\n    println!("y = {}", y);\n}
\n\n

Cuando colocas un breakpoint en let y = x + 10;, el depurador encuentra la dirección de esta instrucción en el binario y reemplaza el primer byte con 0xCC. Después de la parada, restaura el byte original, ejecuta un paso y vuelve a colocar la trampa, si deseas continuar.

\n\n

Step over, step into, step out: ¿cómo sabe el depurador dónde está la «siguiente línea»?

\n\n

Esta es, quizás, la parte más mágica. ¿Cómo sabe el depurador que después de let y = x + 10; viene println!?

\n\n

Todo está en las tablas de depuración. El compilador de Rust (a través de LLVM) genera la sección .debug_line en formato DWARF. Esta es una tabla comprimida que asigna direcciones de instrucciones a números de línea en el código fuente.

\n\n

Por ejemplo, para nuestro código, la tabla podría verse así:

\n\n
Dirección 0x1000 → línea 2 (let x = 42;)\nDirección 0x1010 → línea 3 (let y = x + 10;)\nDirección 0x1020 → línea 4 (println!)
\n\n

Cuando presionas step over, el depurador calcula la dirección de la siguiente instrucción que no pertenece a la línea actual y coloca un punto de interrupción temporal allí. La ejecución continúa hasta esa dirección, y pasas a la siguiente línea.

\n\n

Para step into, el depurador verifica si en la dirección actual hay una llamada a función. Si es así, busca la primera línea de esa fun

Blogs

Book Recommendations