在调试器中正确执行.NET语言步骤
首先,我为这个问题的长度道歉 我是这本书的作者。最近,我一直在努力发出体面的调试信息,以便我可以使用“本机”.NET调试器 虽然这在一定程度上取得了成功,但我在出牙时遇到了一些问题 第一个问题与步进有关 由于Scheme是一种表达式语言,所以所有内容都倾向于用括号括起来,不像主要的.NET语言那样似乎是基于语句(或行)的 原始代码(方案)如下所示:在调试器中正确执行.NET语言步骤,.net,visual-studio,visual-studio-2010,debugging,clr,.net,Visual Studio,Visual Studio 2010,Debugging,Clr,首先,我为这个问题的长度道歉 我是这本书的作者。最近,我一直在努力发出体面的调试信息,以便我可以使用“本机”.NET调试器 虽然这在一定程度上取得了成功,但我在出牙时遇到了一些问题 第一个问题与步进有关 由于Scheme是一种表达式语言,所以所有内容都倾向于用括号括起来,不像主要的.NET语言那样似乎是基于语句(或行)的 原始代码(方案)如下所示: (define (baz x) (cond [(null? x) x] [(pair? x) (ca
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
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#(通过ILSpy),如下所示:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
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上存在相同/类似的问题):
这些都是未应用的ignoresymbolstoresquencepoints
点
忽略符号存储序列点时(推荐):
使用null arg调用baz,它可以正常工作。(null?x)后跟x
用Cons arg调用baz,失败。(空?x)然后(对?x)
用其他参数调用baz,失败。(空?x)然后(对?x)然后(车x)然后(断言冲突…)
我还发现在这种模式下,一些行(此处未显示)被错误地高亮显示,它们被1关闭
以下是一些可能的原因:
- Tailcalls会混淆调试器
- 重叠位置(此处未显示)会混淆调试器(设置断点时,调试器的效果非常好)
第二个也是严重的问题是调试器在某些情况下无法中断/命中断点
唯一可以让调试器正确(且一致)中断的地方是方法入口点
如果不应用ignoresymbolstoresquencepoints
,情况会有所好转
结论
VS调试器可能只是一个简单的错误:(
参考文献:
更新1:
Mdbg不适用于64位程序集。因此,这已过时。我没有更多的32位计算机可对其进行测试。更新:我确信这不是大问题,有人有解决方案吗?编辑:是的,我很傻,只需在x64命令提示符下启动Mdbg:)
更新2:
我已经创建了一个C#应用程序,并试图剖析行信息
我的调查结果:
- 在任何
brXXX
指令之后,您需要有一个序列点(如果无效,即“隐藏线”,则发出nop
)
- 在任何
brXXX
指令之前,发出一个“#行隐藏”和一个nop
然而,应用这一点并不能(单独地)解决问题
但添加以下内容会得到所需的结果:)
- 在
ret
之后,发出一个“#行隐藏”和一个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:
上述“半修复”的问题。Peverify报告所有方法的错误,这是由于nop
在ret
之后出现的。我真的不理解这个问题。一个nop
如何在ret
之后破坏验证。这就像死代码(除了它甚至不是代码)…哦,实验还在继续
更新4:
现在回到家里,删除了在VS2008上运行的“无法验证”代码,情况更糟。也许为了正确调试而运行无法验证的代码可能是答案。在“发布”模式下,所有输出仍然可以验证
更新5:
我现在已经决定我的上述想法是目前唯一可行的选择。虽然生成的代码无法验证,但我还没有找到任何验证异常
。我不知道这种情况会对最终用户产生什么影响
作为奖励,我的第二个问题也解决了。:)
下面是我最后得到的一些东西。它命中断点,执行正确的步进(in/out/over),等等。总之,达到了预期的效果
一、 然而,我仍然不接受这样做的方式。我觉得太讨厌了。对真正的问题有一个确认会很好
更新6:
刚刚更改在VS2010上测试代码,似乎存在一些问题:
现在的第一个呼叫步骤不正确。(断言冲突…)被命中。其他情况也可以。一些旧代码发出不必要的位置。已删除代码,按预期工作。:)李>
更严重的是,断点在程序的第二次调用时失败(使用内存编译,将程序集转储到文件中似乎使断点再次感到高兴)
这两种情况在VS2008下都能正常工作。主要区别在于,在VS2010下,整个应用程序都是针对.NET 4编译的,而在VS2008下,则是针对.NET 2编译的。两者都运行64位
更新7:
如前所述,我让mdbg在64位下运行。不幸的是,它也有breakpo