【问题标题】:How to break WinDbg in an anonymous method?如何以匿名方法破坏 WinDbg?
【发布时间】:2010-03-12 06:33:15
【问题描述】:

标题有点说明一切。通常的 SOS 命令 !bpmd 没有名字就没有多大用处。

我的一些想法:

  • 转储每个方法,然后在找到相应的 MethodDesc 时使用 !bpmd -md
    • 据我所知,在实际使用中并不实用。即使我编写了一个宏来将转储限制为匿名类型/方法,也没有明显的方法可以区分它们。
  • 使用 Reflector 转储 MSIL 名称
    • 在处理动态程序集和/或 Reflection.Emit 时没有帮助。 Visual Studio 无法在此类场景中读取本地变量是我首先转向 Windbg 的全部原因......
  • 在VS中设置断点,等待命中,然后change to Windbg using the noninvasive trick
    • 尝试从 VS 分离会导致它挂起(与应用程序一起)。我认为这是因为托管调试器是"soft" debugger via thread injection 而不是标准的“硬”调试器。或者这可能只是 Silverlight 特有的一个 VS 错误(不会是第一个 I've encountered)。
  • 在其他已知调用匿名方法的位置设置断点,然后单步执行
    • 我的备用计划,但如果此问答揭示了更好的方法,我宁愿不采用它

【问题讨论】:

  • 注意事项:我偶然发现了一个不必要的 WinDbg。如果您在动态程序集创建期间以编程方式发出 DebuggableAttribute (msdn.microsoft.com/en-us/library/…),您将能够在 Visual Studio 中看到本地变量。

标签: c# debugging windbg anonymous-methods sos


【解决方案1】:

匿名方法并不是真正的匿名方法。它只是隐藏在编译器生成的名称后面。

考虑这个小例子:

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

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

要找到返回值,我们需要找到方法实现的名称。为此,我们需要定位周围方法的 MethodDesc。在这个例子中是Main(),所以:

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

通过 MethodDesc,我们可以将 IL 转储为 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 

注意看起来很有趣的名字。它们是生成委托类型和实际方法的名称。该方法称为&lt;Main&gt;b__0。我们来看看方法:

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. 

你有它。 MethodDesc 是 00153024,正如评论所说,您可以使用 !bpmd 使用 MethodDesc 设置断点。

【讨论】:

    【解决方案2】:

    如果在您的场景中查找“....”名称很棘手,那么将其设为常规方法怎么样?这通常很简单;唯一棘手的事情是捕获的变量,但这还不错 - 例如,它们做同样的事情:

        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;
            }
        }
    

    【讨论】:

      【解决方案3】:

      转储操作指向的方法描述符。路线here.

      【讨论】:

      • 嗨,Igor,这是一个仅链接的答案,不包括相关部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-21
      • 2020-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多