2011-04-21 7 views
16

Un caso curioso de Visual Studio 2010 depurador (no puede golpear a un punto de ruptura)Un caso curioso de Visual Studio 2010 depurador (no puede golpear a un punto de ruptura)

Este es el código que reproduce el problema:

class Program { 
    static void Main(string[] args) { 
    bool b = false; 

    if (b) { 
     List<string> list = new List<string>(); 
     foreach (var item in list) { 

     } 
    } else { 
     Console.WriteLine("1"); 
    } 
    Console.WriteLine("2");//add a break point here in VS2010 
    } 
    //1. configuration: release 
    //2. platform target: x64 or Any Cpu 
    //3. debug info: pdb only or full 
    //4. OS: Win7 x64 
    //5. optimize code: enabled 
} 

Añadir un punto de ruptura a la última declaración del código, a continuación, depurarlo en VS2010, se verá que el punto de equilibrio no puede ser golpeado.

para reproducir este curioso caso, tendrá que cumplir las siguientes condiciones: sistema de

  1. operación: Windows 7 x64;
  2. VS build configuration: release;
  3. Objetivo de plataforma de compilación VS: x64 o Cualquier CPU;
  4. VS build debug info: pdb solo o completo;
  5. VS build optimize code: enabled;

No estoy seguro si esas condiciones son suficientes para reproducirlo, pero así es como se configuró mi máquina cuando encontré este problema.

¿Por qué el depurador no puede alcanzar el punto de ruptura?

¡Gracias de antemano!

Y si se puede reproducir este problema, por favor considere la votación de this post.

+1

Tengo esta configuración y no puedo reproducir el comportamiento establecido. ¿Sería importante que estoy usando Win7 64 Bit SP 1? El resto de la configuración es la misma, pero llegué al punto de interrupción todo el tiempo. –

+0

funciona bien para mí - de cualquier manera, ¿por qué estás depurando en modo de lanzamiento? – BrokenGlass

+2

Imagino que la línea ha sido optimizada por el compilador si la pisa, ¿está ejecutando la consola.writeline ("1") dos veces, pero suministrando diferentes argumentos? – forsvarir

Respuesta

11

Cuando el ejemplo proporcionado se genera en modo de lanzamiento y luego JIT-ed en el código máquina de 64 bits, no contiene suficiente información para que el depurador correlacione el punto de interrupción con cualquier instrucción particular de la máquina. Es por eso que el depurador nunca se detiene en este punto de interrupción durante la ejecución de un código de máquina JIT-ed. Simplemente no sabe dónde parar. Probablemente es algún tipo de mal comportamiento o incluso un error en el depurador CLR de 64 bits porque solo se puede reproducir cuando está JIT-ed en código máquina de 64 bits pero no en código máquina de 32 bits.

Cuando el depurador ve un punto de interrupción en el código, trata de encontrar una instrucción de máquina en el código JIT-ed que corresponde a la ubicación marcada por el punto de interrupción. Primero, necesita encontrar una instrucción IL que corresponda a una ubicación de punto de interrupción en su código C#. Luego necesita encontrar una instrucción de máquina que corresponda al comando IL. Luego establece un punto de corte real en la instrucción de la máquina encontrada y comienza la ejecución del método. En su caso, parece que el depurador simplemente ignora un punto de interrupción porque no puede asignarlo a una instrucción de máquina particular.

El depurador no puede encontrar una dirección de una instrucción de máquina que sigue inmediatamente si ... else. La instrucción if ... else y el código dentro de ella de alguna manera provocan este comportamiento. No importa qué afirmación sigue al si ... else. Puede reemplazar la instrucción Console.WriteLine ("2") con alguna otra y todavía podrá reproducir el problema.

Verá que el compilador C# emite un intento ... atrapa bloque alrededor de la lógica que lee la lista si desmontará el ensamblaje resultante con Reflector. Es una característica documentada del compilador de C#. Puede leer más al respecto en The foreach statement

Un try ... catch ... finally block tiene un efecto bastante invasivo en un código JIT-ed. Utiliza el mecanismo de Windows SEH debajo del capó y reescribe mal tu código. No puedo encontrar un enlace a un buen artículo en este momento, pero estoy seguro de que puedes encontrar uno si estás interesado.

Es lo que sucede aquí. El intento ... finalmente bloquear dentro de la instrucción if ... else hace que el depurador tenga un problema. Puede reproducir su problema con un código muy simple.

bool b = false; 
if (b) 
{ 
    try 
    { 
     b = true; 
    } 
    finally 
    { 
     b = true; 
    } 
} 
else 
{ 
    b = true; 
} 
b = true; 

Este código no llama a las funciones externas (que elimina efecto de inlining método propuesto por una de las respuestas) y que compila directamente en IL sin ninguna información adicional codificada añadida por el compilador de C#.

Se puede reproducir solo en modo de lanzamiento porque en el modo de depuración el compilador emite la instrucción IL NOP para cada línea de su código C#. La instrucción IL NOP no hace nada y se compila directamente a la instrucción NOP de la CPU por el JITer que no hace nada también.La utilidad de esta instrucción es que puede ser utilizada por el depurador como un ancla para los puntos de corte, incluso si el resto del código está mal reescrito por el JITer.

Pude hacer que el depurador funcionara correctamente al poner una instrucción NOP justo antes de la instrucción que sigue al if ... else.

Puede leer más sobre las operaciones NOP y proceso de mapeo depurador aquí Debugging IL

Usted puede tratar de utilizar la extensión para WinDbg y SOS para que examine la versión JIT-ed del método. Puede intentar examinar el código de máquina que genera JIT-er e intentar comprender por qué no puede volver a mapear ese código de máquina a una línea particular de C#.

Aquí hay un par de enlaces sobre el uso de WinDbg para romper el código administrado y obtener una dirección de memoria de un método JIT-ed. Creo que deberías poder encontrar la forma de obtener el código JIT-ed para un método desde allí: Setting a breakpoint in WinDbg for Managed Code, SOS Cheat Sheet (.NET 2.0/3.0/3.5).

También puede intentar informar un problema a Microsoft. Probablemente este es un error del depurador CLR.

Gracias por la interesante pregunta.

+2

Hay una forma más simple de ver el código JIT-ed. Compruebe aquí para la descripción http://blogs.msdn.com/b/vancem/archive/2006/02/20/535807.aspx – Dennis

+0

gracias por la respuesta detallada. – CuiPengFei

2

cambiar su configuración de generación de "depuración", en lugar de "Release".

+1

Eso no es realmente una respuesta de por qué no está siendo golpeado, es cómo hacerlo funcionar como se espera ... – forsvarir

+0

Sí, sé que hará que el punto de quiebre sea aceptable. Pero solo quiero saber por qué no funciona en el modo de lanzamiento. – CuiPengFei

+0

Parece que su configuración del Modo de Liberación está configurada para no incluir información de depuración. Verifique las Opciones de compilación avanzada en la Configuración del proyecto. – DWRoelands

5

Al usar VS2010 SP1, se detiene en la última línea si establece un punto de interrupción en modo de liberación. Realmente debería instalarlo, menciona específicamente que soluciona los problemas del depurador donde a veces se salta los puntos de interrupción (aunque no en este caso específico).

Proof

+0

gracias, intentaré eso el lunes cuando regrese al trabajo (Mi PC en el hogar es Windows7 x86). – CuiPengFei

+1

@Blindy: estoy ejecutando VS2010 SP1 (Win 7, 64-bit) y omite este punto de interrupción. –

+1

Tengo VS2010 SP1 y aún puedo reproducirlo. – Dennis

0

creo que cuando se depura usando el modo de disparo puntos de interrupción pueden no corresponder a las líneas reales en el código debido a que el código de la máquina puede haber sido optimizado. En el caso de tu código, en realidad no estás haciendo nada partiendo de imprimir "1" y luego "2", por lo que sería seguro asumir que el compilador eliminará el código de (b == falso) ya que nunca será alcanzado.

+0

Lo descompilé y vi el código IL, no estoy muy familiarizado con el código MSIL, pero creo que la cláusula if todavía está allí. – CuiPengFei

+0

@CuiPengFei: La sentencia "if (false)" probablemente será optimizada por el compilador JIT. No todas las optimizaciones deben realizarse en el código IL. El compilador puede simplemente dejar que el compilador JIT realice las optimizaciones de JIT conocidas. –

+1

Cuando inicia la sesión de depuración desde VS, suprime la optimización de JIT. Puedo reproducir un problema incluso si suprimo la optimización de JIT en la carga del módulo (solo administrado) configuraciones habilitadas para mi VS. Significa que la optimización de JIT no tiene nada que ver con este comportamiento. – Dennis

0

La generación del lanzamiento omite la creación de los símbolos de depuración, que en sí mismo es obvio.

Puede anular esto yendo a las propiedades de su proyecto. seleccione la configuración de lanzamiento y presione 'Avanzado' en la pestaña de compilación. Establezca la información de depuración en lleno. enter image description here

+0

Intenté ambos, full y pdb-only y lo mencioné en la pregunta – CuiPengFei

1

El compilador JIT utiliza técnicas de optimización que pueden causar esto.

Una de estas optimizaciones se llama método que incluye, que puede ser responsable de este comportamiento.

No puedo decir exactamente ahora, porque estoy usando la computadora de otra persona ...pero se puede probar que usted mismo:

1) Crear el siguiente método:

[MethodImpl(MethodImplOptions.NoInlining)] 
public static void MyMethod(string str) 
{ 
    str += "-CONCAT-STRING"; 
} 

2) reemplazar las llamadas a "Console.WriteLine" con sólo "MiMetodo"

3) Establecer el punto de interrupción y pruébalo

4) Ahora, elimine el atributo "MethodImpl" del método "MyMethod".

//[MethodImpl(MethodImplOptions.NoInlining)] 
public static void MyMethod(string str) 
{ 
    str += "-CONCAT-STRING"; 
} 

5) Ejecutar nuevamente, con el punto de interrupción en el mismo lugar.

6) Si se detiene en la primera ejecución, pero no en la segunda ejecución ... entonces este es el motivo.

+0

Gracias, parece una buena sugerencia. Lo intentaré el lunes, ya que mi PC en casa es Win7 x86. – CuiPengFei

+0

Acabo de probarlo. Con o sin el atributo, no puede alcanzar el punto de ruptura. – CuiPengFei

Cuestiones relacionadas