使您的.NET语言在调试器中正确执行 [英] Making your .NET language step correctly in the debugger

查看:89
本文介绍了使您的.NET语言在调试器中正确执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,对于这个问题的冗长,我深表歉意.

Firstly, I apologize for the length of this question.

我是 IronScheme 的作者.最近,我一直在努力发布体面的调试信息,以便可以使用本机" .NET调试器.

I am the author of IronScheme. Recently I have been working hard on emitting decent debug info, so that I can use the 'native' .NET debugger.

虽然这部分取得了成功,但我遇到了一些麻烦的问题.

While this has been partly successful, I am running into some teething problems.

第一个问题与步进有关.

The first problem is related to stepping.

由于Scheme是一种表达语言,因此所有内容都倾向于用括号括起来,这与主要的.NET语言似乎是基于语句(或行)的语言不同.

Due to Scheme being an expression language, everything tends to be wrapped in parenthesis, unlike the major .NET languages which seems to be statement (or line) based.

原始代码(方案)如下:

The original code (Scheme) looks like:

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

我故意将每个表达式放在换行符上.

I have on purpose laid out each expression on a newline.

发出的代码通过ILSpy转换为C#,如下所示:

The emitted code transforms to C# (via ILSpy) looks like:

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

如您所见,非常简单.

As you can see, pretty simple.

注意:如果在C#中将代码转换为条件表达式(?:),则整个过程仅是调试步骤之一,请记住这一点.

Note: If the code was transformed into a conditional expression (?:) in C#, the whole thing would just be one debug step, keep that in mind.

以下是带有源代码和行号的IL输出:

Here is IL output with source and line numbers:

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

注意:为了防止调试器仅突出显示整个方法,我将方法入口点设置为仅一列宽.

Note: To prevent the debugger from simply highlighting the entire method, I make the method entry point just 1 column wide.

如您所见,每个表达式正确地映射到一行.

As you can see, each expression maps correctly to a line.

现在存在步进问题(已在VS2010上测试,但在VS2008上存在相同/相似的问题):

Now the problem with stepping (tested on VS2010, but same/similar issue on VS2008):

这些没有应用IgnoreSymbolStoreSequencePoints.

  1. 使用空arg调用baz,它可以正常工作. (null?x),然后是x.
  2. 使用Cons arg调用baz,它可以正常工作. (null?x)然后(pair?x)然后(car x).
  3. 与其他arg一起调用baz,但失败. (null?x)然后(pair?x)然后(car x)然后(assertion-violation ...).

应用IgnoreSymbolStoreSequencePoints时(建议):

  1. 使用空arg调用baz,它可以正常工作. (null?x),然后是x.
  2. 使用Cons arg调用baz,但失败. (null?x)然后(pair?x).
  3. 与其他arg一起调用baz,但失败. (null?x)然后(pair?x)然后(car x)然后(assertion-violation ...).

我还发现,在此模式下,某些行(此处未显示)未正确突出显示,它们之间的距离为1.

I also find in this mode that some lines (not shown here) are incorrectly highlighted, they are off by 1.

有些想法可能是什么原因:

Here are some ideas what could be the causes:

  • 通话失败会混淆调试器
  • 重叠的位置(此处未显示)会混淆调试器(在设置断点时效果很好)
  • ????

第二个也是很严重的问题是,调试器在某些情况下无法中断/命中断点.

The second, but also serious, issue is the debugger failing to break/hit breakpoints in some cases.

使调试器正确(一致地)中断的唯一地方是方法入口点.

The only place where I can get the debugger to break correctly (and consistantly), is at the method entry point.

不应用IgnoreSymbolStoreSequencePoints时情况会好一些.

The situation gets a bit better when IgnoreSymbolStoreSequencePoints is not applied.

结论

可能是VS调试器只是普通的越野车:(

It might be that the VS debugger is just plain buggy :(

参考:

  1. 使CLR/.NET语言可调试

更新1:

Mdbg不适用于64位程序集.这样就可以了.我没有更多的32位计算机可以对其进行测试. 更新:我确信这不是大问题,有人可以解决吗? 是的,愚蠢的我,只需在x64命令提示符下启动mdbg:)

Mdbg does not work for 64-bit assemblies. So that is out. I have no more 32-bit machines to test it on. Update: I am sure this is no big problem, does anyone have a fix? Yes, silly me, just start mdbg under the x64 command prompt :)

更新2:

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

I have created a C# app, and tried to dissect the line info.

我的发现:

  • 在执行任何brXXX指令后,您需要有一个序列点(如果无效,又名"#line hidden",则发出nop).
  • 在任何brXXX指令之前,发出一个'#line hidden'和nop.
  • After any brXXX instruction you need to have a sequence point (if not valid aka '#line hidden', emit a nop).
  • Before any brXXX instruction, emit a '#line hidden' and a nop.

应用此功能后,并不能解决问题(单独吗?).

Applying this, does not however fix the issues (alone?).

但是添加以下内容,可以得到所需的结果:)

But adding the following, gives the desired result :)

  • ret之后,发出隐藏的#line"和nop.
  • After ret, emit a '#line hidden' and a nop.

这是在不应用IgnoreSymbolStoreSequencePoints的模式下进行的.应用后,仍会跳过某些步骤:(

This is using the mode where IgnoreSymbolStoreSequencePoints is not applied. When applied, some steps are still skipped :(

以下是应用了上述内容时的IL输出:

Here is the IL output when above has been applied:

  .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报告所有方法的错误.我真的不明白这个问题. ret之后,nop如何中断验证.就像死代码一样(除了它甚至不是代码)...哦,好的,实验还在继续.

Problem with above 'semi-fix'. Peverify reports errors on all methods due to the nop after ret. I dont understand the problem really. How can a nop break verification after a ret. It is like dead code (except that it is NOT even code) ... Oh well, experimentation continues.

更新4:

现在回到家中,删除了无法验证的"代码,该代码在VS2008上运行,情况变得更糟.答案可能是为了进行适当的调试而运行无法验证的代码.在发布"模式下,所有输出仍将是可验证的.

Back at home now, removed the 'unverifiable' code, running on VS2008 and things are a lot worse. Perhaps running unverifiable code for the sake of proper debugging might be the answer. In 'release' mode, all output would still be verifiable.

更新5:

我现在已经确定上述想法是目前唯一可行的选择.尽管生成的代码不可验证,但我尚未找到任何VerificationException.我不知道在这种情况下会对最终用户产生什么影响.

I have now decided my above idea is the only viable option for now. Although the generated code is unverifiable, I have yet to find any VerificationException's. I dont know what the impact will be on the end user with this scenario.

作为奖励,我的第二个问题也已解决. :)

As a bonus, my second issue has also be solved. :)

以下是我最后的一些截屏.它会达到断点,进行适当的步进(进/出/越过)等.总而言之,可以达到预期的效果.

Here is a little screencast of what I ended up with. It hits breakpoints, does proper stepping (in/out/over), etc. All in all, the desired effect.

但是,我仍然不接受这样做.对我来说感觉太过分了.确认真实问题会很好.

I, however, am still not accepting this as the way to do it. It feel overly-hacky to me. Having a confirmation on the real issue would be nice.

更新6:

只是进行了更改以在VS2010上测试代码,所以似乎存在一些问题:

Just had the change to test the code on VS2010, there seems to be some problems:

  1. 第一个呼叫现在无法正确执行. (断言违反...)被击中.其他情况也可以.一些旧代码发出了不必要的位置.删除了代码,按预期方式工作. :)
  2. 更严重的是,断点在程序的第二次调用时失败(使用内存内编译,将程序集转储到文件中似乎使断点再次令人满意).
  1. The first call now does not step correctly. (assertion-violation ...) is hit. Other cases works fine. Some old code emitted unnecessary positions. Removed the code, works as expected. :)
  2. More seriously, breakpoints fail on the second invocation of the program (using in-memory compilation, dumping assembly to file seems to make breakpoints happy again).

这两种情况在VS2008下均可正常工作.主要区别在于,在VS2010下,整个应用程序都是针对.NET 4编译的,而在VS2008下,则是针对.NET 2编译的.两者都运行64位.

Both these cases work correctly under VS2008. The main difference is that under VS2010, the entire application is compiled for .NET 4 and under VS2008, compiles to .NET 2. Both running 64-bit.

更新7:

就像提到的那样,我让mdbg在64位下运行.不幸的是,它还有一个断点问题,如果我重新运行该程序,它将无法中断(这意味着它会重新编译,因此不使用相同的程序集,而是使用相同的源代码).

Like mentioned, I got mdbg running under 64-bit. Unfortunately, it also have the breakpoint issue where it fails to break if I rerun the program (this implies it gets recompiled, so not using the same assembly, but still using the same source).

更新8:

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

I have filed a bug at the MS Connect site regarding the breakpoint issue.

更新:固定

更新9:

经过深思熟虑,使调试器满意的唯一方法似乎是执行SSA,因此每个步骤都可以隔离和顺序执行.我还没有证明这个概念.但这似乎合乎逻辑.显然,从SSA清除临时文件会中断调试,但是切换起来很容易,并且没有太多开销.

After some long thinking, the only way to make the debugger happy seems to be doing SSA, so every step can be isolated and sequential. I am yet to prove this notion though. But it seems logical. Obviously, cleaning up temps from SSA will break debugging, but that is easy to toggle, and leaving them does not have much overhead.

推荐答案

我是Visual Studio调试器团队的工程师.

I am an engineer on the Visual Studio Debugger team.

如果我错了,请纠正我,但这听起来像是剩下的唯一问题是,当从PDB切换到.NET 4动态编译符号格式时,会丢失一些断点.

Correct me if I am wrong, but it sounds like the only issue left is that when switching from PDBs to the .NET 4 dynamic compile symbol format some breakpoints are being missed.

我们可能需要一名专家来准确诊断问题,但是这里有一些说明可能会有所帮助.

We would probably need a repro to exactly diagnose the issue, however here are some notes that might help.

  1. VS(2008+)可以以非管理员身份运行
  2. 第二次是否都加载了任何符号?您可以通过闯入(通过异常或调用System.Diagnostic.Debugger.Break())进行测试
  3. 假设符号已加载,您是否可以向我们发送一份复制品?
  4. 可能的区别在于,.NET 2(PDB流)和.NET 4(IL DB我认为他们称之为它)之间,动态编译代码的符号格式有100%的差异.
  5. 点点滴滴"的声音是正确的.请参阅下面的用于生成隐式序列点的规则.
  6. 您实际上不需要在不同的行上发出信号.默认情况下,VS将步进符号声明",在此,作为编译器编写者,您将定义符号声明"的含义.因此,如果您希望每个表达式在符号文件中都是独立的东西,那将很好用.

JIT根据以下规则创建隐式序列点: 1. IL nop指令 2. IL堆栈空点 3.调用指令之后紧跟着IL指令

The JIT creates an implicit sequence point based on the following rules: 1. IL nop instructions 2. IL stack empty points 3. The IL instruction immediately following a call instruction

如果事实证明我们确实需要解决您的问题,则可以提交连接错误并通过该媒体安全地上传文件.

If it turns out we do need a repro to solve your issue, you can file a connect bug and upload files securely through that medium.

更新:

我们鼓励遇到此问题的其他用户尝试从 http://www.microsoft.com/download/zh-CN/details.aspx?displaylang=zh-CN&id=27543 并发表评论并提供任何反馈. (必须定位4.5)

We are encouraging other users experiencing this issue to try the Developer Preview of Dev11 from http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27543 and comment with any feedback. (Must target 4.5)

更新2:

Leppie已验证修补程序,可以在 http://上获得的Dev11 Beta版对他有效.如连接错误中所述,www.microsoft.com/visualstudio/11/en-us/downloads https://connect.microsoft.com/VisualStudio/feedback/details/684089/.

Leppie has verified the fix to work for him on the Beta version of Dev11 available at http://www.microsoft.com/visualstudio/11/en-us/downloads as noted in the connect bug https://connect.microsoft.com/VisualStudio/feedback/details/684089/.

谢谢

卢克

这篇关于使您的.NET语言在调试器中正确执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆