首先,我为这个问题的冗长道歉。
我是 铁人计划的作者。最近我一直在努力发出体面的调试信息,以便我可以使用’原生’。NET 调试器。
虽然这种做法在一定程度上取得了成功,但我也遇到了一些初期的问题。
第一个问题与踏步有关。
由于 Scheme 是一种表达式语言,所以与 Major 不同,所有内容都包装在括号中。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 调试器可能只是一个普通的 bug: (
参考文献:
更新1:
Mdbg 不适用于64位程序集。所以这是出局了。我没有更多的32位机器来测试它。我肯定这不是什么大问题,有人能解决吗?编辑:是的,我真笨,只要在 x64命令提示符下启动 mdbg:)
更新2:
我已经创建了一个 C # 应用程序,并试图剖析行信息。
我的发现是:
brXXX
指令之后,您需要有一个序列点(如果不是有效的 aka’# line hide’,则发出一个 nop
)。brXXX
指令之前,发出一个“ # line hide”和一个 nop
。然而,应用这种方法并不能解决问题(单独解决?)。
但加上以下内容,就得到了预期的结果:)
ret
之后,发出一个“ # line hide”和一个 nop
。这是使用不应用 IgnoreSymbolStoreSequencePoints
的模式。当应用时,一些步骤仍然被跳过: (
以下是应用上述方法后的输出:
.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 报告由于 ret
之后的 nop
导致的所有方法的错误。我真的不明白这个问题。在 ret
之后,nop
如何能够中断验证。它就像死代码(除了它甚至不是代码) ... ... 哦,好吧,实验还在继续。
更新4:
现在回到家里,删除了“无法验证”的代码,运行在 VS2008上,情况要糟糕得多。也许为了正确的调试而运行无法验证的代码就是答案。在“发布”模式下,所有输出仍然是可验证的。
更新5:
我现在已经决定,我的上述想法是目前唯一可行的选择。虽然生成的代码无法验证,但我还没有找到任何 VerificationException
。我不知道这种情况对最终用户会有什么影响。
作为奖励,我的第二个问题也得到了解决。 :)
这是我最后得到的一点 视频。它会触发断点,执行正确的单步执行(in/out/over)等。总之,达到了预期效果。
然而,我仍然不能接受这样做的方式。我觉得有点过了。确认真正的问题是件好事。
更新6:
刚刚在 VS2010上测试了代码,似乎有一些问题:
在 VS2008下,这两种情况都能正常工作。主要区别在于,在 VS2010下,整个应用程序的编译目标是。NET 4和 VS2008下,编译为。NET 2.都是64位的。
更新7:
如前所述,我让 mdbg 在64位下运行。不幸的是,它还有一个断点问题,即如果我重新运行程序,它将无法中断(这意味着它将被重新编译,因此不使用相同的程序集,但仍然使用相同的源代码)。
更新8:
关于断点问题,我在 MS 连接站点上有 装了窃听器。
更新: 固定
更新9:
经过长时间的思考,使调试器高兴的唯一方法似乎是执行 SSA,因此每一步都可以是隔离的和连续的。不过我还没有证明这个观点。但这似乎是合乎逻辑的。显然,从 SSA 中清除临时数据会中断调试,但是这很容易切换,并且留下临时数据没有太大的开销。