2010-03-12 9 views
8

Título algo lo dice todo. El comando usual de SOS ! Bpmd no hace mucho bien sin un nombre.¿Cómo romper WinDbg de forma anónima?

Algunas ideas que tenía:

  • volcado todos los métodos, a continuación, utilizar bpmd md cuando encuentre el MethodDesc correspondiente
    • no es práctico en el uso del mundo real, por lo que puedo decir . Incluso si escribí una macro para limitar el volcado a tipos/métodos anónimos, no hay una manera obvia de diferenciarlos.
  • uso del reflector para volcar el nombre MSIL
    • no ayuda cuando se trata de montajes dinámicos y/o Reflection.Emit. incapacidad de Visual Studio para leer VARs locales dentro de estos escenarios es toda la razón por la que volví a WinDbg en el primer lugar ...
  • establecer el punto de interrupción en VS, esperar a que se golpeó, y luego change to Windbg using the noninvasive trick
    • intentar desconectarse de VS hace que se cuelgue (junto con la aplicación). Creo que esto se debe al hecho de que el depurador administrado es un "soft" debugger via thread injection en lugar de un depurador "duro" estándar. O tal vez solo sea un error de VS específico de Silverlight (difícilmente sería el primero I've encountered).
  • establecer un punto de interrupción en algún otro lugar conocido para llamar al método anónimo, entonces solo paso en su camino
    • mi plan de copia de seguridad, aunque yo prefiero no recurrir a él si esto Q & a revela una mejor manera
+0

Nota bene: Hice un descubrimiento accidental que hacía innecesario WinDbg. Si emites programáticamente DebuggableAttribute (http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggableattribute.aspx) durante la creación del ensamblaje dinámico, podrás ver vars locales en Visual Studio bien . –

Respuesta

9

El método anónimo no es realmente anónimo. Simplemente se esconde detrás de un nombre generado por el compilador.

consideran este pequeño ejemplo:

Func<int, int> a = (x) => x + 1; 

Console.WriteLine(a.Invoke(1)); 

Para hallar el valor de retorno, tenemos que encontrar el nombre de la implementación del método. Para hacer eso, necesitamos ubicar el MethodDesc del método circundante. En este ejemplo, es Main(), por lo que:

0:000> !name2ee * TestBench.Program.Main 
Module: 6db11000 (mscorlib.dll) 
-------------------------------------- 
Module: 00162c5c (TestBench.exe) 
Token: 0x06000001 
MethodDesc: 00163010 
Name: TestBench.Program.Main() 
JITTED Code Address: 001e0070 

A través de la MethodDesc podemos volcar la IL para Main()

0:000> !dumpil 00163010 
ilAddr = 003f2068 
IL_0000: nop 
IL_0001: ldstr "press enter" 
IL_0006: call System.Console::WriteLine  
IL_000b: nop 
IL_000c: call System.Console::ReadLine 
IL_0011: pop 
IL_0012: ldsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 
IL_0017: brtrue.s IL_002c 
IL_0019: ldnull 
IL_001a: ldftn TestBench.Program::<Main>b__0 
IL_0020: newobj class [System.Core]System.Func`2<int32,int32>::.ctor 
IL_0025: stsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 
IL_002a: br.s IL_002c 
IL_002c: ldsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 
IL_0031: stloc.0 
IL_0032: ldloc.0 
IL_0033: ldc.i4.1 
IL_0034: callvirt class [System.Core]System.Func`2<int32,int32>::Invoke 
IL_0039: call System.Console::WriteLine 
IL_003e: nop 
IL_003f: ret 

Aviso los nombres aspecto divertido. Son los nombres del tipo generar delegado y el método real. El método se llama <Main>b__0. Veamos el método:

0:000> !name2ee * TestBench.Program.<Main>b__0 
Module: 6db11000 (mscorlib.dll) 
-------------------------------------- 
Module: 00152c5c (TestBench.exe) 
Token: 0x06000003 
MethodDesc: 00153024 
Name: TestBench.Program.<Main>b__0(Int32) 
Not JITTED yet. Use !bpmd -md 00153024 to break on run. 

Ahí lo tienes. MethodDesc es 00153024 y, como dice el comentario, puede usar! Bpmd para establecer el punto de interrupción utilizando MethodDesc.

+2

+1 Gran respuesta, muy completa. –

0

Si encontrar el nombre "<> ...." es complicado para su escenario, ¿qué tal si lo hacemos de forma regular? Esto es usualmente muy simple; lo único complicado es capturado las variables, pero eso no es tan malo - por ejemplo, éstas hacen lo mismo:

static void Main() 
    { 
     List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
     int div = 2; 
     foreach (var item in list.Where(x => x % div == 0)) 
     { 
      Console.WriteLine(item); 
     } 

     ListSearcher ls = new ListSearcher(); 
     ls.div = 2; 
     foreach (var item in list.Where(ls.Test)) 
     { 
      Console.WriteLine(item); 
     } 
    } 
    class ListSearcher 
    { 
     public int div; 
     public bool Test(int x) 
     { 
      return x % div == 0; 
     } 
    } 
0

volcar el descriptor de método de los puntos de acción a. Instrucciones here.

+0

Hola, Igor, esto es como una respuesta de solo enlace, sin incluir las partes relevantes. –