让你的.NET语言在debugging器中正确的步骤

首先,我对这个问题的长度表示抱歉。

我是IronScheme的作者。 最近,我一直在努力发布体面的debugging信息,以便我可以使用“本地”.NETdebugging器。

虽然这部分取得了成功,但我却遇到了一些小问题。

第一个问题与步进有关。

由于Scheme是一种expression式语言,所有东西都倾向于用括号包装,而不像似乎是基于语句(或行)的主要.NET语言。

原始代码(Scheme)如下所示:

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

我有目的地在换行符上列出每个expression。

发出的代码转换为C#(通过ILSpy)看起来像:

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

正如你所看到的,非常简单。

注意:如果代码在C#中被转换成条件expression式(?:),整个事情只是一个debugging步骤,请记住。

这里是IL输出与源和行号码:

  .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' 

注:为了防止debugging器简单地突出显示整个方法,我使方法入口点只有1列宽。

正如你所看到的,每个expression式正确映射到一行。

现在步进的问题(VS2010testing,但VS2008相同/相似的问题):

这些与IgnoreSymbolStoreSequencePoints不适用。

  1. 用null参数调用baz,它工作正常。 (null?x)后跟x。
  2. 用Cons arg调用baz,它工作正常。 (null?x)then(pair?x)then(car x)。
  3. 与其他参数调用baz,它失败。 (null?x)then(pair?x)then(car x)then(assertion-violation …)。

当应用IgnoreSymbolStoreSequencePoints (推荐)时:

  1. 用null参数调用baz,它工作正常。 (null?x)后跟x。
  2. 用Cons arg调用baz,它失败。 (null?x)then(pair?x)。
  3. 与其他参数调用baz,它失败。 (null?x)then(pair?x)then(car x)then(assertion-violation …)。

我也发现在这种模式下,某些行(这里没有显示)被错误地突出显示,它们被closures了1。

以下是一些可能的原因:

  • Tailcalls混淆了debugging器
  • 重叠的位置(这里没有显示)混淆了debugging器(当设置一个断点时,它非常好)
  • ????

第二,也是严重的问题是debugging器在某些情况下没有打破/命中断点。

唯一可以让debugging器正确地(并且一致地)中断的地方就是方法入口点。

IgnoreSymbolStoreSequencePoints未应用时情况会好一些。

结论

这可能是VSdebugging器只是普通的错误:

参考文献:

  1. 使CLR / .NET语言可debugging

更新1:

Mdbg不适用于64位程序集。 所以这是出来的。 我没有更多的32位机器来testing它。 更新:我相信这不是一个大问题,有没有人有一个修复? 编辑:是的,傻,我只需在x64命令提示符下启动mdbg 🙂

更新2:

我创build了一个C#应用程序,并试图剖析行信息。

我的发现:

  • 任何brXXX指令后,你需要有一个序列点(如果不是有效的,也就是'#line hidden',发出一个nop )。
  • 在任何brXXX指令之前,发出一个'#line hidden'和一个nop

应用这个,并不能解决问题(单独?)。

但添加以下,给出了预期的结果:)

  • ret ,发出一个'#line hidden'和一个nop

这是使用IgnoreSymbolStoreSequencePoints未应用的模式。 应用时,一些步骤仍然跳过:(

以上是应用上面的IL输出:

  .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' 

更新3:

上述“半定”的问题。 由于ret之后的nop ,Peverify会报告所有方法的错误。 我真的不明白这个问题。 一个nop如何在ret之后破解validation。 这就像死了的代码(除了它甚至不是代码)…哦,实验还在继续。

更新4:

现在回到家中,删除了“无法validation”的代码,在VS2008上运行,事情变得更糟。 也许为了正确的debugging运行不可validation的代码可能是答案。 在“释放”模式下,所有输出仍然是可validation的。

更新5:

我现在已经决定,我的上述想法是现在唯一可行的select。 虽然生成的代码是无法validation的,但我还没有find任何VerificationException的。 我不知道这种情况下最终用户会产生什么样的影响。

作为奖励,我的第二个问题也解决了。 🙂

这里是我最后的一个小屏幕录像 。 它击中断点,做适当的步进(进/出/结束)等。总而言之,期望的效果。

但是,我仍然不接受这个做法。 对我来说,这感觉太过分了。 在真正的问题上确认会很好。

更新6:

刚刚在VS2010上testing代码的变化,似乎有一些问题:

  1. 现在的第一个电话不能正确地执行。 (断言违反…)被击中。 其他情况下工作正常。 一些旧的代码发出不必要的位置 删除了代码,按预期工作。 🙂
  2. 更严重的是,在程序的第二次调用中断点失败(使用内存中的汇编,将汇编文件转储到文件似乎使断点重新开始)。

这两种情况在VS2008下都能正常工作。 主要区别在于,在VS2010下,整个应用程序都是为.NET 4编译的,在VS2008下编译为.NET 2.两个都运行64位。

更新7:

就像上面提到的,我得到了mdbg在64位下的运行。 不幸的是,如果我重新运行程序(这意味着它被重新编译,所以不使用相同的程序集,但仍然使用相同的源程序),它也有断点问题。

更新8:

我已经在MS Connect网站上提交了一个关于断点问题的错误。

更新:修正

更新9:

经过漫长的思考,让debugging器开心的唯一方法似乎是做SSA,所以每一步都可以被隔离和顺序执行。 尽pipe我还没有certificate这个观点。 但是这似乎合乎逻辑。 显然,从SSA清理临时数据将会破坏debugging,但这很容易切换,而且不会有太多开销。

我是Visual Studio Debugger团队的一名工程师。

纠正我,如果我错了,但它听起来像剩下的唯一问题是,从PDB切换到.NET 4dynamic编译符号格式时,一些断点正在错过。

我们可能需要一个repro来准确地诊断问题,但是这里有一些笔记可能有帮助。

  1. VS(2008+)可以作为非pipe理员运行
  2. 第二次做任何符号吗? 您可以通过中断来testing(通过exception或调用System.Diagnostic.Debugger.Break())
  3. 假设符号加载,是否有一个repro,你可以发送给我们?
  4. 可能的区别是dynamic编译代码的符号格式在.NET 2(PDBstream)和.NET 4(IL DB我认为他们称之为?)之间是100%不同的。
  5. 'nop的声音是正确的。 请参阅下面的生成隐式序列点的规则。
  6. 你不需要在不同的线路上发射东西。 默认情况下,VS将会在符号语句的步骤中作为编译器编写器来定义“符号语句”的含义。 所以,如果你想要每个expression式在符号文件中是一个单独的东西,那将工作得很好。

JIT基于以下规则创build隐式序列点:1. IL nop指令2. IL堆栈空点3.紧随呼叫指令的IL指令

如果事实certificate,我们确实需要repro来解决您的问题,您可以提交连接错误并通过该介质安全地上传文件。

更新:

我们鼓励遇到此问题的其他用户尝试从http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27543获取Dev11的开发人员预览版,并发表任何反馈意见。; (必须指定4.5)

更新2:

Leppie已经通过http://www.microsoft.com/visualstudio/11/en-us/downloadsvalidation了在Dev11 Beta版本上为他工作的修正,正如连接错误https://connect.microsoft中所述。; com / VisualStudio / feedback / details / 684089 / 。

谢谢,

卢克

我是SharpDevelop Debugger团队的一名工程师:-)

你解决了这个问题吗?

你有没有尝试在SharpDevelop中进行debugging? 如果在.NET中有一个错误,我不知道是否需要实施一些解决方法。 我不知道这个问题。

你有没有尝试在ILSpy中进行debugging? 特别是没有debugging符号。 它会debuggingC#代码,但它会告诉我们IL指令是否可以很好地debugging。 (请注意,ILSpydebugging器虽然是testing版)

关于原始IL代码的简要说明:

  • .line 19,19:6,15“会出现两次?
  • .line 20,20:7,14''不在隐式序列点上启动(堆栈不为空)。 我很担心
  • .line 20,20:7,14''包含“car x”(good)以及“#f nooo x”(坏?)的代码
  • 关于ret之后的nop。 那么stloc,ldloc,ret呢? 我认为C#使用这个技巧来使ret成为一个独特的序列点。

大卫