Random IRC quote :      <ggonzalez> porque no sabéis expresar vuestros entimientos

Los debuggers y el principio de incertidumbre

Hace casi ochenta años que fué enunciado uno de los principios más conocidos de la mecánica cuántica: el principio de incertidumbre o indeterminación de Heinsenberg. Para explicar este principio en lenguaje coloquial se puede decir que mientras más certeza se tenga sobre la posición de una partícula menos se conocerá su velocidad, y mientras más se conozca su velocidad, menos se sabrá acerca de su posición. Si se intenta medir de alguna forma la posición de la partícula, la propia medición alterará su velocidad, y lo mismo le ocurrirá a la posición si se intenta determinar la velocidad. El principio de incertidumbre de Heinsenberg habla de que todo sistema se altera al ser observado. A nivel atómico no existen observadores absolutamente pasivos. Pero no hay que profundizar tanto en la estructura de la materia para comprobar la validez del principio de incertidumbre, en el mundo de los ceros y los unos también se cumple esta ley universal.

Hace ya algunos añitos estuve escribiendo un driver para Windows 9X, los otrora populares y ahora casi extintos VXDs. El driver tenía una función «callback» que proporcionaba al sistema operativo, para que este le notificara acerca de un determinado evento. Hace tanto tiempo que he olvidado los detalles, pero recuerdo perfectamente que en cierto momento, luego de modificar la función «callback», el driver empezó a provocar pantallas azules apenas era cargado. Por aquel entonces (y también por este) mi debugger favorito era SoftICE, y mis sesiones de depuración del driver empezaban colocando una instrucción «__asm INT 3» en el lugar del código que me interesaba examinar. Una vez que SoftICE se detenía en la INT 3 insertada en el código, bien cargaba el fichero de símbolos generado por el compilador, o bien depuraba directamente en ensamblador, según me resultara más cómodo. Así que cuando el driver empezó a provocar las pantallas azules hice lo de siempre, puse la INT 3 para forzar el breakpoint al principio de la función «callback» y me puse a depurar. Llegé al RET y no había pasado nada malo, así que resumí la ejecución y para mi sorpresa el driver funcionaba bien. Cada vez que paraba en el breakpoint seguía el código paso a paso, pero nada, el bug se había esfumado. Quité la INT 3 y volví a compilar, cargué el driver… y otra vez la pantalla azul. Volví a poner la INT 3 para parar en la función y mirar más atentamente el código, compilé, cargué el driver, y SoftICE saltó como debía. Después de mirar el código en todos sus detalles llegué a la conclusión que no había nada raro, así que dejé que corriera, y otra vez arreglado, no saltaba la pantalla azul. Después de hacerlo tres o cuatro veces acabé por convencerme de que al poner la INT 3 para depurar con SoftICE el bug desaparecía, y que volvía a aparecer en cuanto la quitaba, ¡pero entonces no podía depurar!. Era un bug que aparecía justo cuando no podía verlo.

Entonces opté por un análisis estático. Compilé el driver con INT 3 y sin ella, y desensamblé ambas versiones con IDA Pro para ver las diferencias, así pude comprobar que el compilador generaba código radicalmente distinto por el simple hecho de poner o quitar la INT 3. Yo esperaba que fueran códigos muy similares, con la obvia diferencia que uno tendría la INT 3 embebida y el otro no, pero no resultó así. El código que tenía la INT 3 usaba además un marco de pila basado en EBP, accedía a las variables locales usando EBP como base, el otro usaba directamente el ESP para acceder a las variables locales, la mayoría de las cuales intentaba mantener en los registros para ganar en velocidad. En otras palabras, al colocar la INT 3 el compilador se comportaba como si estuviera en modo «debug», prescindiendo de las optimizaciones y generando todas las funciones con marcos de pila basados en EBP. Al quitar la INT 3, el driver se compilaba en modo «release», con las optimizaciones activadas, eliminando los marcos de pila basados en EBP.

Precisamente la causa del bug estaba relacionada con el registro EBP. El código de Windows que llamaba a la función «callback» dentro de mi driver asumía que al retornar de la función el registro EBP se mantendría con el mismo valor. Cuando la función era compilada con marcos de pila basados en EBP no ocurría nada malo, puesto que lo primero que hace una función con estas características es precisamente guardar el contenido de EBP en la pila para restaurarlo justo antes del retorno, pero al no usar EBP como marco de pila, este registro se usaba con otros propósitos y el compilador no se tomaba el trabajo de guardar su valor inicial y luego restaurarlo. Esto provocaba un fallo en el código de Windows al retornar mi función «callback» y encontrarse con que el valor de EBP había cambiado.

Por supuesto la solución fue usar un #pragma para obligar al compilador a que usara marcos de pilas basados en EBP, al menos para la función «callback», y asunto resuelto, el problema no volvió a ocurrir. Nunca antes había estado ante una situación en la que se viera tan claramente que el intento de depurar un programa puede modificar su comportamiento, e incluso hacer desaparecer el bug que se intenta encontrar. Principio de Heinsenberg en estado puro, y eso que todavía no tenemos ordenadores cuánticos.

11 Comentarios para “Los debuggers y el principio de incertidumbre”

  1. Comment por 12345 | 05/23/06 at 8:29 pm

    Pero estabas programando en ASM o en otro lenguaje?

  2. Comment por Johnny Foo | 05/24/06 at 12:49 pm

    Nunca podré saber dónde se encuentra
    cada átomo de mi cuerpo
    y qué está haciendo,
    pero ninguna incertidumbre
    como la duda de una sombra en el corazón.

    taken from http://cuaderno.bitacoras.com/archivos/2006/02/12/el-principio-de-heisenberg

  3. Comment por ruben | 05/24/06 at 10:24 pm

    aplicando otra famosa paradoja de la mecánica cuántica, también se podría sustituir al gato de schrödinger por tu driver 🙂 . Muy buena la historia.

  4. Comment por mballano | 05/25/06 at 2:16 am

    Muy buena la historia 😀

  5. Comment por vmalvarez | 05/25/06 at 11:37 am

    Vaya! Tengo comentarios! 🙂

    Respuesta para los primeros 5 elementos de N: programaba en C, pero insertaba una instrucción INT 3 en ensamblador dentro del código C para que me sirviera de breakpoint.

    Y para ruben: el driver se llamaba schrdngrcat32.vxd 😀

  6. Comment por 12345 | 05/25/06 at 6:00 pm

    Y que compilador usabas?

  7. Comment por vmalvarez | 05/25/06 at 11:14 pm

    Borland C++, creo recordar

  8. Comment por 12345 | 05/26/06 at 5:47 pm

    Y que tal el tiempo?

  9. Comment por mballano | 05/27/06 at 8:17 pm

    por aquí no hace mal.

  10. Comment por fran | 03/22/07 at 9:49 pm

    muy buena historia para comentar a mis alumnos de Física

  11. Comment por natalia lizbeht hinojosa gonzalez | 10/17/07 at 11:39 pm

    expliquenmelo no entiendo somos pendejas a y muy buena historia

Se han cerrado los comentarios