visual studio que programacion net lenguaje historia framework ejemplos desde cero aprender .net visual-studio visual-studio-2010 debugging clr

.net - programacion - que es visual studio



Haciendo que su lenguaje.NET avance correctamente en el depurador (2)

Soy ingeniero en el equipo SharpDevelop Debugger :-)

¿Has resuelto el problema?

¿Intentó depurarlo en SharpDevelop? Si hay un error en .NET, me pregunto si debemos implementar alguna solución. No estoy enterado de este problema.

¿Intentó depurarlo en ILSpy? Especialmente sin símbolos de depuración. Depuraría el código C #, pero nos diría si las instrucciones IL son muy fáciles de debbieble. (Tenga en cuenta que el depurador ILSpy es beta)

Notas rápidas sobre el código IL original:

  • .line 19,19: 6,15 '''' ocurre dos veces?
  • .line 20,20: 7,14 '''' no comienza en el punto de secuencia implícita (la pila no está vacía). Estoy preocupado
  • .line 20,20: 7,14 '''' incluye el código para "car x" (bueno) así como también "#f nooo x" (¿malo?)
  • con respecto al nop después de ret. ¿Qué pasa con stloc, ldloc, ret? Creo que C # usa este truco para hacer que ret sea un punto de secuencia distinto.

David

En primer lugar, me disculpo por la longitud de esta pregunta.

Soy el autor de IronScheme . Recientemente he estado trabajando arduamente para emitir información de depuración decente, de modo que pueda usar el depurador .NET nativo.

Aunque esto ha sido parcialmente exitoso, me estoy encontrando con algunos problemas iniciales.

El primer problema está relacionado con el paso.

Debido a que Scheme es un lenguaje de expresión, todo tiende a estar entre paréntesis, a diferencia de los principales lenguajes .NET que parecen estar basados ​​en declaraciones (o líneas).

El código original (Scheme) se ve así:

(define (baz x) (cond [(null? x) x] [(pair? x) (car x)] [else (assertion-violation #f "nooo" x)]))

He diseñado a propósito cada expresión en una nueva línea.

El código emitido se transforma en C # (a través de ILSpy) se ve así:

public static object ::baz(object x) { if (x == null) { return x; } if (x is Cons) { return Builtins.Car(x); } return #.ironscheme.exceptions::assertion-violation+( RuntimeHelpers.False, "nooo", Builtins.List(x)); }

Como puedes ver, bastante simple.

Nota: Si el código se transformó en una expresión condicional (? :) en C #, todo sería solo un paso de depuración, téngalo en cuenta.

Aquí está la salida IL con números de fuente y línea:

.method public static object ''::baz''(object x) cil managed { // Code size 56 (0x38) .maxstack 6 .line 15,15 : 1,2 '''' //000014: //000015: (define (baz x) IL_0000: nop .line 17,17 : 6,15 '''' //000016: (cond //000017: [(null? x) IL_0001: ldarg.0 IL_0002: brtrue IL_0009 .line 18,18 : 7,8 '''' //000018: x] IL_0007: ldarg.0 IL_0008: ret .line 19,19 : 6,15 '''' //000019: [(pair? x) .line 19,19 : 6,15 '''' IL_0009: ldarg.0 IL_000a: isinst [IronScheme]IronScheme.Runtime.Cons IL_000f: ldnull IL_0010: cgt.un IL_0012: brfalse IL_0020 IL_0017: ldarg.0 .line 20,20 : 7,14 '''' //000020: (car x)] IL_0018: tail. IL_001a: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object) IL_001f: ret IL_0020: ldsfld object [Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False IL_0025: ldstr "nooo" IL_002a: ldarg.0 IL_002b: call object [IronScheme]IronScheme.Runtime.Builtins::List(object) .line 22,22 : 7,40 '''' //000021: [else //000022: (assertion-violation #f "nooo" x)])) IL_0030: tail. IL_0032: call object [ironscheme.boot]#:: ''ironscheme.exceptions::assertion-violation+''(object,object,object) IL_0037: ret } // end of method ''eval-core(033)''::''::baz''

Nota: Para evitar que el depurador simplemente resalte todo el método, hago que el punto de entrada del método tenga solo 1 columna de ancho.

Como puede ver, cada expresión se correlaciona correctamente con una línea.

Ahora el problema con el paso (probado en VS2010, pero el mismo / problema similar en VS2008):

Estos son con IgnoreSymbolStoreSequencePoints no aplicados.

  1. Call baz con null arg, funciona correctamente. (null? x) seguido de x.
  2. Call baz con Cons arg, funciona correctamente. (null? x) luego (pair? x) luego (carro x).
  3. Llama a baz con otra arg, falla. (null? x) luego (pair? x) luego (carro x) luego (assertion-violation ...).

Al aplicar IgnoreSymbolStoreSequencePoints (como se recomienda):

  1. Call baz con null arg, funciona correctamente. (null? x) seguido de x.
  2. Call baz con Cons arg, falla. (null? x) luego (pair? x).
  3. Llama a baz con otra arg, falla. (null? x) luego (pair? x) luego (carro x) luego (assertion-violation ...).

También encuentro en este modo que algunas líneas (que no se muestran aquí) están resaltadas incorrectamente, están desactivadas por 1.

Aquí hay algunas ideas sobre cuáles podrían ser las causas:

  • Tailcalls confunde el depurador
  • Las ubicaciones superpuestas (que no se muestran aquí) confunden al depurador (lo hace muy bien al establecer un punto de interrupción)
  • ????

El segundo problema, pero también grave, es que el depurador no logra romper / llegar a los puntos de interrupción en algunos casos.

El único lugar donde puedo hacer que el depurador se rompa correctamente (y constantemente) está en el punto de entrada del método.

La situación mejora un poco cuando IgnoreSymbolStoreSequencePoints no se aplica.

Conclusión

Es posible que el depurador VS sea simplemente defectuoso :(

Referencias

  1. Hacer un depurador de lenguaje CLR / .NET

Actualización 1:

Mdbg no funciona para ensamblajes de 64 bits. Entonces eso está fuera. No tengo más máquinas de 32 bits para probarlo. Actualización: estoy seguro de que este no es un gran problema, ¿alguien tiene una solución? Editar: Sí, me parece tonto, simplemente inicie mdbg bajo el símbolo del sistema x64 :)

Actualización 2:

Creé una aplicación de C # y traté de diseccionar la información de la línea.

Mis hallazgos:

  • Después de cualquier instrucción brXXX , necesitas tener un punto de secuencia (si no es válido, también conocido como ''# línea oculta''), emite un nop ).
  • Antes de cualquier instrucción brXXX , emita un ''#line hidden'' y un nop .

Sin embargo, aplicar esto no soluciona los problemas (¿solo?).

Pero agregando lo siguiente, da el resultado deseado :)

  • Después de ret , emite un ''#line hidden'' y un nop .

Esto está usando el modo donde no se aplica IgnoreSymbolStoreSequencePoints . Cuando se aplica, algunos pasos todavía se saltan :(

Aquí está la salida IL cuando arriba se ha aplicado:

.method public static object ''::baz''(object x) cil managed { // Code size 63 (0x3f) .maxstack 6 .line 15,15 : 1,2 '''' IL_0000: nop .line 17,17 : 6,15 '''' IL_0001: ldarg.0 .line 16707566,16707566 : 0,0 '''' IL_0002: nop IL_0003: brtrue IL_000c .line 16707566,16707566 : 0,0 '''' IL_0008: nop .line 18,18 : 7,8 '''' IL_0009: ldarg.0 IL_000a: ret .line 16707566,16707566 : 0,0 '''' IL_000b: nop .line 19,19 : 6,15 '''' .line 19,19 : 6,15 '''' IL_000c: ldarg.0 IL_000d: isinst [IronScheme]IronScheme.Runtime.Cons IL_0012: ldnull IL_0013: cgt.un .line 16707566,16707566 : 0,0 '''' IL_0015: nop IL_0016: brfalse IL_0026 .line 16707566,16707566 : 0,0 '''' IL_001b: nop IL_001c: ldarg.0 .line 20,20 : 7,14 '''' IL_001d: tail. IL_001f: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object) IL_0024: ret .line 16707566,16707566 : 0,0 '''' IL_0025: nop IL_0026: ldsfld object [Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False IL_002b: ldstr "nooo" IL_0030: ldarg.0 IL_0031: call object [IronScheme]IronScheme.Runtime.Builtins::List(object) .line 22,22 : 7,40 '''' IL_0036: tail. IL_0038: call object [ironscheme.boot]#:: ''ironscheme.exceptions::assertion-violation+''(object,object,object) IL_003d: ret .line 16707566,16707566 : 0,0 '''' IL_003e: nop } // end of method ''eval-core(033)''::''::baz''

Actualización 3:

Problema con el ''semi-arreglo'' anterior. Peverify informa errores en todos los métodos debido al nop después de ret . No entiendo el problema realmente. ¿Cómo puede un nop romper la verificación después de un ret . Es como un código muerto (excepto que NO es ni siquiera código) ... Bueno, la experimentación continúa.

Actualización 4:

De vuelta en casa ahora, eliminó el código "no verificable", se ejecuta en VS2008 y las cosas son mucho peores. Tal vez ejecutar la clave no verificable por una correcta depuración podría ser la respuesta. En el modo ''liberación'', toda la salida aún sería verificable.

Actualización 5:

Ahora he decidido que mi idea anterior es la única opción viable por ahora. Aunque el código generado no se puede verificar, todavía no he encontrado ninguna VerificationException . No sé cuál será el impacto en el usuario final con este escenario.

Como beneficio adicional, mi segundo problema también se ha resuelto. :)

Aquí hay un pequeño screencast de lo que terminé. Llega a los puntos de interrupción, realiza un paso adecuado (entrada / salida / finalización), etc. Con todo, el efecto deseado.

Yo, sin embargo, todavía no estoy aceptando esto como la manera de hacerlo. Se siente demasiado hacky para mí. Tener una confirmación sobre el problema real sería agradable.

Actualización 6:

Acabo de tener el cambio para probar el código en VS2010, parece que hay algunos problemas:

  1. La primera llamada ahora no se ejecuta correctamente. (afirmación-violación ...) es golpeado. Otros casos funcionan bien. Algunos códigos antiguos emitían posiciones innecesarias. Se eliminó el código, funciona como se esperaba. :)
  2. Más en serio, los puntos de interrupción fallan en la segunda invocación del programa (al usar la compilación en memoria, volcar el ensamblaje al archivo parece hacer felices a los puntos de interrupción).

Ambos casos funcionan correctamente en VS2008. La diferencia principal es que en VS2010, toda la aplicación se compila para .NET 4 y en VS2008, compila en .NET 2. Ambos ejecutan 64 bits.

Actualización 7:

Como se mencionó, obtuve mdbg corriendo por debajo de 64 bits. Desafortunadamente, también tiene el problema del punto de interrupción en el que no se puede romper si vuelvo a ejecutar el programa (esto implica que se vuelve a compilar, por lo que no utiliza el mismo ensamblado, pero sigue usando la misma fuente).

Actualización 8:

He archivado un error en el sitio de MS Connect con respecto al problema del punto de interrupción.

Actualización: arreglado

Actualización 9:

Después de pensarlo mucho, la única forma de hacer que el depurador sea feliz parece estar haciendo SSA, por lo que cada paso puede ser aislado y secuencial. Todavía estoy por probar esta noción. Pero parece lógico. Obviamente, limpiar temps de SSA romperá la depuración, pero eso es fácil de alternar, y dejarlos no tiene demasiados gastos generales.


Soy un ingeniero en el equipo de Visual Studio Debugger.

Corrígeme si estoy equivocado, pero parece que el único problema que queda es que, al cambiar de PDB al formato de símbolo de compilación dinámico .NET 4, se pierden algunos puntos de interrupción.

Probablemente necesitemos una repro para diagnosticar exactamente el problema, sin embargo aquí hay algunas notas que pueden ayudar.

  1. VS (2008+) puede ejecutarse como no administrador
  2. ¿Se carga algún símbolo por segunda vez? Puede probar ingresando (a través de una excepción o llamando a System.Diagnostic.Debugger.Break ())
  3. Suponiendo que los símbolos se carguen, ¿hay alguna reprografía que nos puedas enviar?
  4. La diferencia probable es que el formato de símbolo para el código compilado dinámicamente es 100% diferente entre .NET 2 (secuencia PDB) y .NET 4 (IL DB ¿Creo que lo llamaron?)
  5. El sonido de ''nop es correcto. Vea las reglas para generar puntos de secuencia implícitos a continuación.
  6. En realidad no necesita emitir cosas en diferentes líneas. Por defecto, VS pasará ''declaraciones de símbolos'' donde, como escritor del compilador, usted puede definir lo que significa ''declaración-símbolo''. Entonces, si quieres que cada expresión sea una cosa separada en el archivo de símbolos, funcionará bien.

El JIT crea un punto de secuencia implícito basado en las siguientes reglas: 1. IL nop instructions 2. IL stack empty points 3. La instrucción IL inmediatamente después de una instrucción de llamada

Si resulta que necesitamos una repro para resolver su problema, puede archivar un error de conexión y cargar archivos de forma segura a través de ese medio.

Actualizar:

Alentamos a otros usuarios que experimenten este problema a que prueben la Vista previa del desarrollador de Dev11 desde http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27543 y comenten sus comentarios. (Debe apuntar a 4.5)

Actualización 2:

Leppie ha verificado la solución para trabajar para él en la versión Beta de Dev11 disponible en http://www.microsoft.com/visualstudio/11/en-us/downloads como se indica en el error de conexión https://connect.microsoft. com / VisualStudio / feedback / details / 684089 / .

Gracias,

Luke