首先,我为这个问题的长度道歉。
我是IronScheme的作者.最近我一直在努力发布不错的调试信息,以便我可以使用“ native ”.NET 调试器。
虽然这部分成功,但我遇到了一些初期问题。
第一个问题与步进有关。
由于 Scheme 是一种表达式语言,所以一切都倾向于用括号括起来,这与主要的 .NET 语言似乎是基于语句(或行)不同。
原始代码(方案)如下所示:
(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 调试器只是简单的错误:(
引用文献:
更新 1:
Mdbg 不适用于 64 位程序集。所以就这样了。我没有更多的 32 位机器来测试它。更新:我确定这不是大问题,有没有人有解决办法?编辑:是的,愚蠢的我,只需在 x64 命令提示符下启动 mdbg :)
更新 2:
我创建了一个 C# 应用程序,并试图剖析行信息。
我的发现:
brXXX
指令你需要有一个序列点(如果无效,又名'#line hidden',发出一个 nop
)。 brXXX
指令,发出 '#line hidden' 和 nop
. 然而,应用这个并不能解决问题(单独?)。
但是添加以下内容,可以得到所需的结果:)
ret
, 发出一个 '#line hidden' 和一个 nop
. 这是使用模式 where
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:
上述“半修复”的问题。由于
nop
,Peverify 报告所有方法的错误之后 ret
.我真的不明白这个问题。怎么可以nop
ret
后中断验证.它就像死代码(除了它甚至不是代码)......哦,实验还在继续。更新 4:
现在回到家里,删除了“无法验证”的代码,在 VS2008 上运行,事情变得更糟了。也许为了正确调试而运行无法验证的代码可能是答案。在“发布”模式下,所有输出仍可验证。
更新 5:
我现在决定我的上述想法是目前唯一可行的选择。虽然生成的代码无法验证,但我还没有找到任何
VerificationException
的。我不知道这种情况会对最终用户产生什么影响。作为奖励,我的第二个问题也已解决。 :)
这里有一点screencast我最终得到了什么。它击中断点,进行适当的步进(进/出/越过)等。总而言之,达到了预期的效果。
然而,我仍然不接受这是这样做的方式。对我来说,这感觉太过分了。对真正的问题进行确认会很好。
更新 6:
刚刚在VS2010上改了测试代码,好像有一些问题:
这两种情况在 VS2008 下都可以正常工作。主要区别在于,在 VS2010 下,整个应用程序针对 .NET 4 编译,而在 VS2008 下,则编译为 .NET 2。两者都运行 64 位。
更新 7:
如前所述,我让 mdbg 在 64 位下运行。不幸的是,它也有断点问题,如果我重新运行程序,它就无法中断(这意味着它被重新编译,所以不使用相同的程序集,但仍然使用相同的源代码)。
更新 8:
我有 filed a bug在 MS Connect 站点上关于断点问题。
更新:固定
更新 9:
经过长时间的思考,让调试器满意的唯一方法似乎是做 SSA,所以每一步都可以隔离和顺序执行。我还没有证明这个概念。但这似乎是合乎逻辑的。显然,从 SSA 中清除临时文件会破坏调试,但这很容易切换,并且保留它们不会有太多开销。
最佳答案
我是 Visual Studio Debugger 团队的一名工程师。
如果我错了,请纠正我,但听起来剩下的唯一问题是,当从 PDB 切换到 .NET 4 动态编译符号格式时,会遗漏一些断点。
我们可能需要一个重现来准确诊断问题,但是这里有一些可能会有所帮助的说明。
JIT 根据以下规则创建一个隐式序列点:
1. IL nop 指令
2.IL栈空点
3. 紧跟在 call 指令之后的 IL 指令
如果事实证明我们确实需要重现来解决您的问题,您可以提交连接错误并通过该媒体安全地上传文件。
更新:
我们鼓励遇到此问题的其他用户尝试来自 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/feedback/details/684089/ 中所述.
谢谢,
卢克
关于.net - 在调试器中正确执行 .NET 语言步骤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6937198/