在调试器中正确地使你的.NET语言步骤正确
首先,我对这个问题的长度表示歉意。
我是IronScheme的作者。 最近我一直在努力发布体面的调试信息,以便我可以使用“本地”.NET调试器。
虽然这部分取得了成功,但我遇到了一些初期问题。
第一个问题与步进有关。
由于Scheme是一种表达式语言,所有东西都倾向于包装在括号中,而不像主要的.NET语言,它似乎是基于语句(或行)。
原始代码(Scheme)看起来像:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
我故意在换行符上列出每个表达式。
发出的代码转换为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#中转换为条件表达式(?:),则整个事情只是一个调试步骤,请记住这一点。
这里是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'
注:为了防止调试器简单地突出显示整个方法,我使方法入口点只有1列宽。
正如你所看到的,每个表达式都正确地映射到一行。
现在步进问题(在VS2010上测试,但在VS2008上有相同/相似的问题):
这些与IgnoreSymbolStoreSequencePoints
不适用。
当应用IgnoreSymbolStoreSequencePoints
(按照建议)时:
我还发现,在这种模式下,某些行(此处未显示)被错误地突出显示,它们被关闭1。
以下是一些想法,原因可能是:
第二,也是严重的问题是调试器在某些情况下无法打破/命中断点。
唯一可以让调试器正确(并且一致)破解的地方是方法入口点。
IgnoreSymbolStoreSequencePoints
未应用时情况会好一些。
结论
这可能是VS调试器只是普通的buggy :(
参考文献:
更新1:
Mdbg不适用于64位程序集。 所以那已经结束了。 我没有更多的32位机器来测试它。 更新:我相信这不是一个大问题,有没有人有修复? 编辑:是的,愚蠢的我,只需在x64命令提示符下启动mdbg :)
更新2:
我创建了一个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
之后破解验证。 这就像死了的代码(除了它甚至不是代码)......哦,实验仍在继续。
更新4:
现在回到家中,删除了“无法验证”的代码,运行在VS2008上,情况变得更糟。 也许为了正确的调试运行无法验证的代码可能是答案。 在'释放'模式下,所有输出仍然是可验证的。
更新5:
我现在已经决定我的上述想法是现在唯一可行的选择。 虽然生成的代码是无法验证的,但我还没有找到任何VerificationException
的。 我不知道这种情况下最终用户会产生什么样的影响。
作为奖励,我的第二个问题也解决了。 :)
这里是我最终结束的一个小屏幕录像。 它击中断点,做适当的步进(进/出/结束)等。总而言之,期望的效果。
但是,我仍然不接受这种做法。 对我来说,这感觉很过分。 对实际问题进行确认会很好。
更新6:
只是在VS2010上测试代码的变化,似乎有一些问题:
这两种情况在VS2008下都能正常工作。 主要区别在于,在VS2010下,整个应用程序是针对.NET 4编译的,并且在VS2008下编译为.NET 2.两者均运行64位。
更新7:
就像上面提到的,我得到了mdbg在64位下运行。 不幸的是,如果我重新运行程序(这意味着它被重新编译,所以不使用相同的程序集,但仍然使用相同的源代码),它也有断点问题。
更新8:
我已经在MS Connect网站上提交了一个关于断点问题的错误。
更新:修正
更新9:
经过长时间的思考,让调试器开心的唯一方法似乎是做SSA,所以每一步都可以是独立的和顺序的。 尽管我还没有证明这个观点。 但这似乎合乎逻辑。 显然,清理SSA的临时数据将会破坏调试,但很容易切换,并且不会有太多开销。
我是Visual Studio Debugger团队的一名工程师。
纠正我,如果我错了,但它听起来像剩下的唯一问题是,从PDB切换到.NET 4动态编译符号格式时,某些断点正在错过。
我们可能需要一个repro来准确诊断问题,但是这里有一些注释可能会有所帮助。
JIT基于以下规则创建隐式序列点:1. IL nop指令2. IL堆栈空点3.紧随呼叫指令后的IL指令
如果事实证明,我们确实需要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/downloads上验证了Dev11 Beta版本中的修复方法,如连接错误https://connect.microsoft中所述。 COM / VisualStudio中/反馈/信息/ 684089 /。
谢谢,
卢克
我是SharpDevelop Debugger团队的一名工程师:-)
你解决了这个问题吗?
你有没有尝试在SharpDevelop中进行调试? 如果.NET中存在错误,我不知道是否需要实施一些解决方法。 我不知道这个问题。
你有没有尝试在ILSpy中进行调试? 特别是没有调试符号。 它会调试C#代码,但它会告诉我们IL指令是否可以很好地调试。 (请注意,ILSpy调试器虽然是beta版本)
关于原始IL代码的简要说明:
大卫
链接地址: http://www.djcxy.com/p/59987.html上一篇: Making your .NET language step correctly in the debugger