¿Cómo funciona un depurador?

170

Me sigo preguntando cómo funciona un depurador. Particularmente el que se puede 'adjuntar' al ejecutable que ya se está ejecutando. Entiendo que el compilador traduce el código al lenguaje de máquina, pero ¿cómo "sabe" el depurador a qué se adjunta?

ya23
fuente
55
El artículo de Eli se trasladó a eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1
Oktalist el
@Oktalist Este artículo es interesante pero solo habla sobre la abstracción de nivel API para la depuración en Linux. Supongo que OP quiere saber más sobre debajo del capó.
smwikipedia

Respuestas:

96

Los detalles de cómo funciona un depurador dependerán de lo que esté depurando y del sistema operativo. Para la depuración nativa en Windows, puede encontrar algunos detalles sobre MSDN: API de depuración de Win32 .

El usuario le dice al depurador a qué proceso adjuntar, ya sea por nombre o por ID de proceso. Si es un nombre, el depurador buscará la ID del proceso e iniciará la sesión de depuración a través de una llamada al sistema; bajo Windows esto sería DebugActiveProcess .

Una vez conectado, el depurador entrará en un bucle de eventos similar a cualquier interfaz de usuario, pero en lugar de eventos que provienen del sistema de ventanas, el sistema operativo generará eventos en función de lo que sucede en el proceso que se está depurando, por ejemplo, una excepción. Ver WaitForDebugEvent .

El depurador puede leer y escribir la memoria virtual del proceso de destino, e incluso ajustar sus valores de registro a través de las API proporcionadas por el sistema operativo. Consulte la lista de funciones de depuración para Windows.

El depurador puede usar información de archivos de símbolos para traducir de direcciones a nombres de variables y ubicaciones en el código fuente. La información del archivo de símbolos es un conjunto separado de API y no es una parte central del sistema operativo como tal. En Windows, esto es a través del SDK de acceso a la interfaz de depuración .

Si está depurando un entorno administrado (.NET, Java, etc.), el proceso generalmente se verá similar, pero los detalles son diferentes, ya que el entorno de la máquina virtual proporciona la API de depuración en lugar del sistema operativo subyacente.

Rob Walker
fuente
55
Esta pregunta puede sonar estúpida, pero ¿cómo hace el seguimiento del sistema operativo si se alcanza una dirección específica dentro del programa? Por ejemplo, se establece un punto de ruptura en la dirección 0x7710cafe. A medida que el puntero de instrucciones cambia, el sistema operativo (o tal vez la CPU) tendrá que comparar el puntero de instrucciones con todas las direcciones de punto de corte, ¿o me equivoco? Como funciona esto ..?
mostrar el
3
@StefanFalk Escribí una respuesta que aborda algunos de los detalles de nivel inferior (en x86).
Jonathon Reinhart
¿Podría explicar cómo se asignan exactamente los nombres de las variables a las direcciones? ¿La aplicación usa las mismas direcciones de memoria para las variables cada vez que se ejecuta? Siempre supuse que acababa de mapearse desde la memoria disponible, pero nunca había pensado realmente si los bytes se mapearían directamente al mismo lugar en el espacio de memoria de la aplicación. Parece que sería un importante problema de seguridad.
James Joshua Street
@JamesJoshuaStreet Me imagino que ese es un detalle específico del depurador.
moonman239
Esta respuesta revela algo. Pero creo que op está más interesado en algunos detalles de bajo nivel que en algunas abstracciones de API.
smwikipedia
63

Según lo entiendo:

Para puntos de interrupción de software en x86, el depurador reemplaza el primer byte de la instrucción con CC( int3). Esto se hace con WriteProcessMemoryWindows. Cuando la CPU llega a esa instrucción y ejecuta la int3, esto hace que la CPU genere una excepción de depuración. El sistema operativo recibe esta interrupción, se da cuenta de que el proceso se está depurando y notifica al proceso del depurador que se alcanzó el punto de interrupción.

Después de alcanzar el punto de interrupción y detener el proceso, el depurador busca en su lista de puntos de interrupción y reemplaza el CCcon el byte que estaba allí originalmente. El depurador establece TF, el indicador de captura en EFLAGS(modificando el CONTEXT), y continúa el proceso. El indicador de captura hace que la CPU genere automáticamente una excepción de un solo paso ( INT 1) en la siguiente instrucción.

Cuando el proceso que se está depurando se detiene la próxima vez, el depurador reemplaza nuevamente el primer byte de la instrucción de punto de interrupción CCy el proceso continúa.

No estoy seguro de si esta es exactamente la forma en que lo implementan todos los depuradores, pero he escrito un programa Win32 que logra depurarse utilizando este mecanismo. Completamente inútil, pero educativo.

Jonathon Reinhart
fuente
25

En Linux, la depuración de un proceso comienza con la llamada al sistema ptrace (2) . Este artículo tiene un gran tutorial sobre cómo usar ptracepara implementar algunas construcciones de depuración simples.

Adam Rosenfield
fuente
1
¿ (2)Nos dice algo más (o menos) que "ptrace es una llamada al sistema"?
Lazer
55
@eSKay, no, no realmente. El (2)es el número de sección manual. Ver en.wikipedia.org/wiki/Man_page#Manual_sections para una descripción de las secciones del manual.
Adam Rosenfield
2
@AdamRosenfield Excepto por el hecho de que la sección 2 es específicamente "Llamadas del sistema". Entonces, indirectamente, sí, nos dice que ptracees una llamada al sistema.
Jonathon Reinhart
1
Más prácticamente, el (2)nos dice que podemos escribir man 2 ptracey obtener la página de manual correcta, no es importante aquí porque no hay otro ptracepara desambiguar, sino para comparar man printfcon man 3 printfLinux.
delgado
9

Si está en un sistema operativo Windows, un gran recurso para esto sería "Depuración de aplicaciones para Microsoft .NET y Microsoft Windows" de John Robbins:

(o incluso la edición anterior: "Depuración de aplicaciones" )

El libro tiene un capítulo sobre cómo funciona un depurador que incluye código para un par de depuradores simples (pero que funcionan).

Como no estoy familiarizado con los detalles de la depuración de Unix / Linux, es posible que esto no se aplique en absoluto a otros sistemas operativos. Pero supongo que, como introducción a un tema muy complejo, los conceptos, si no los detalles y las API, deberían 'portarse' a la mayoría de los sistemas operativos.

Michael Burr
fuente
3

Otra fuente valiosa para comprender la depuración es el manual de la CPU Intel (Intel® 64 y el Manual del desarrollador de software de arquitecturas IA-32). En el volumen 3A, capítulo 16, introdujo el soporte de hardware para la depuración, como excepciones especiales y registros de depuración de hardware. Lo siguiente es de ese capítulo:

Indicador T (trampa), TSS: genera una excepción de depuración (#DB) cuando se intenta cambiar a una tarea con el indicador T establecido en su TSS.

No estoy seguro de si Windows o Linux usan esta bandera o no, pero es muy interesante leer ese capítulo.

Espero que esto ayude a alguien.

Jiang
fuente
2

Creo que hay dos preguntas principales para responder aquí:

1. ¿Cómo sabe el depurador que ocurrió una excepción?

Cuando se produce una excepción en un proceso que se está depurando, el depurador recibe una notificación del sistema operativo antes de que cualquier controlador de excepciones de usuario definido en el proceso de destino tenga la oportunidad de responder a la excepción. Si el depurador elige no manejar esta notificación de excepción (de primera oportunidad), la secuencia de despacho de excepción continúa y el hilo objetivo tiene la oportunidad de manejar la excepción si así lo desea. Si el proceso de destino no maneja la excepción de SEH, se envía al depurador otro evento de depuración, llamado notificación de segunda oportunidad, para informarle que ocurrió una excepción no controlada en el proceso de destino. Fuente

ingrese la descripción de la imagen aquí


2. ¿Cómo sabe el depurador cómo detenerse en un punto de interrupción?

La respuesta simplificada es: cuando coloca un punto de interrupción en el programa, el depurador reemplaza su código en ese punto con una instrucción int3 que es una interrupción de software . Como efecto, el programa se suspende y se llama al depurador.

InTheNameOfScience
fuente
1

Entiendo que cuando compila una aplicación o un archivo DLL, lo que sea que compila contiene símbolos que representan las funciones y las variables.

Cuando tiene una compilación de depuración, estos símbolos son mucho más detallados que cuando se trata de una compilación de lanzamiento, lo que le permite al depurador brindarle más información. Cuando adjunta el depurador a un proceso, observa a qué funciones se está accediendo actualmente y resuelve todos los símbolos de depuración disponibles desde aquí (dado que sabe cómo son las partes internas del archivo compilado, puede determinar lo que podría estar en la memoria , con contenidos de int, flotadores, cadenas, etc.). Como decía el primer póster, esta información y cómo funcionan estos símbolos depende en gran medida del entorno y el idioma.

DavidG
fuente
2
Esto es solo sobre símbolos. Hay mucho, mucho más en la depuración que los símbolos.
Jonathon Reinhart