调试器引擎。方法重写,局部变量提升和变量解析

时间:2016-09-13 13:35:26

标签: c# debugging roslyn cil hoisting

我正在使用MDBG示例制作托管.NET调试器。它适用于简单的场景,但在发生方法重写时会出现问题。最关键的部分是yield方法和异步方法。

我已经就这些问题提出了更为笼统的问题question。 现在我想专注于局部变量解析。 请考虑代码:

    using System;
    using System.Threading.Tasks;

    class C
    {
        public static void Main() {

            var instance = new Instance(); 
            instance.Start().Wait();
        }
    }
    class Instance
    {
        public static async Task F() { for(var i=0; i<100; i++) { Console.WriteLine(i); await Task.Delay(100); } }
        public async Task Start() {
           var z = "test";<------- Breakpoint
           var x = 10;
           await F();
        }
    }

当调试器到达断点时,我查询调试器以获取局部变量,唯一的变量是this。变量xz在生成的结构中悬挂,无法直接解析Image

所以问题是:如何在yield方法和异步方法中调试局部变量期间解决?

在对我之前的问题的评论中,@ Brian Reichle给了我一些提示,告诉我如何在现有变量和提升变量之间进行映射。 在探索SymAttribute和Roslyn源代码时,我得出的结论是,它并没有直接存储它们之间的映射。 SymAttribute用于获取CustomDebugInfoRecord,它存储此信息的一部分(来自Roslyn的Used Pdb2Xml库来生成它):

    <method containingType="Instance+&lt;Start&gt;d__1" name="MoveNext">
      <customDebugInfo>
        <forward declaringType="C" methodName="Main" />
        <hoistedLocalScopes>
          <slot startOffset="0x0" endOffset="0xcc" />
          <slot startOffset="0x0" endOffset="0xcc" />
        </hoistedLocalScopes>
        <encLocalSlotMap>
          <slot kind="27" offset="0" />
          <slot kind="33" offset="161" />
          <slot kind="temp" />
          <slot kind="temp" />
        </encLocalSlotMap>
      </customDebugInfo>
      <sequencePoints>
        <entry offset="0x0" hidden="true" document="1" />
        <entry offset="0x7" hidden="true" document="1" />
        <entry offset="0xe" startLine="16" startColumn="37" endLine="16" endColumn="38" document="1" />
        <entry offset="0xf" startLine="17" startColumn="14" endLine="17" endColumn="29" document="1" />
        <entry offset="0x1a" startLine="18" startColumn="14" endLine="18" endColumn="35" document="1" />
        <entry offset="0x26" startLine="19" startColumn="14" endLine="19" endColumn="25" document="1" />
        <entry offset="0x2e" startLine="19" startColumn="25" endLine="19" endColumn="46" document="1" />
        <entry offset="0x3a" startLine="20" startColumn="14" endLine="20" endColumn="24" document="1" />
        <entry offset="0x45" hidden="true" document="1" />
        <entry offset="0xa0" hidden="true" document="1" />
        <entry offset="0xb8" startLine="21" startColumn="11" endLine="21" endColumn="12" document="1" />
        <entry offset="0xc0" hidden="true" document="1" />
      </sequencePoints>
      <asyncInfo>
        <kickoffMethod declaringType="Instance" methodName="Start" />
        <await yield="0x57" resume="0x72" declaringType="Instance+&lt;Start&gt;d__1" methodName="MoveNext" />
      </asyncInfo>
    </method>

因此,我现在可以看到解决提升变量的唯一方法是:

  1. 检查方法是否被重写。
  2. 对于这样的方法获取asyncInfo并找到它等待declaringType。它给出了生成的结构名称以及提升变量的位置。
  3. 解析this.generatedStructureName
  4. Roslyn源代码显示naming conventions for hoisted variables,可用于将x变量翻译为<x>5__2
  5. 这种做法看起来并不正确,我不确定它是否会成功,但这是我现在唯一能想到的。 还有其他可能解决这个问题吗? VisualStudio如何处理它?<​​/ p>

    我创建了一个小型仓库来重现问题here

1 个答案:

答案 0 :(得分:1)

好吧,我还没有在这里或在msdn论坛上发现任何人会告诉我VS算法是如何工作的,但是我发现 SharpDevelop 支持异步方法的变量解析。令人惊讶的是,它使用了与我在我的问题中描述的类似的算法:只解析提升的字段名称。

相关来源可以在gitHub上使用here,如果其他人遇到类似的问题而且会被卡住。不过,我不认为这是一个很好的解决方案,并希望有更好的方法来解决这个问题......