一个好奇的Visual Studio 2010调试器(它不能打破点) [英] A curious case of Visual Studio 2010 debugger(it can not hit a break point)

查看:114
本文介绍了一个好奇的Visual Studio 2010调试器(它不能打破点)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个好奇的Visual Studio 2010调试器(它不能打破点)的案例

A curious case of Visual Studio 2010 debugger(it can not hit a break point)

这是重现问题的代码:

class Program {
  static void Main(string[] args) {
    bool b = false;

    if (b) {
        List<string> list = new List<string>();
        foreach (var item in list) {

        }
    } else {
        Console.WriteLine("1");
    }
    Console.WriteLine("2");//add a break point here in VS2010
  }
  //1. configuration: release
  //2. platform target: x64 or Any Cpu
  //3. debug info: pdb only or full
  //4. OS: Win7 x64
  //5. optimize code: enabled
}

在代码的最后一个语句中添加一个断点,然后在vs2010中调试它,你会发现断点不能被击中。

Add a break point to the last statement of the code, then debug it in vs2010, you'll see that the break point can not be hit.

要重现这个好奇的情况,你需要满足以下条件:

To reproduce this curious case, you'll need to meet the following conditions:


  1. 操作系统:windows 7 x64;

  2. VS构建配置:release;

  3. VS构建平台目标:x64或任何Cpu;

  4. VS构建调试信息:仅限pdb或完整;

  5. VS构建优化代码:启用;

  1. Operation system: windows 7 x64;
  2. VS build configuration: release;
  3. VS build platform target: x64 or Any Cpu;
  4. VS build debug info: pdb only or full;
  5. VS build optimize code: enabled;

我不确定这些条件是否足以重现,但这是我的机器配置时发现这个问题。

I am not sure those conditions are sufficient to reproduce it, but it's how my machine was configured when I found this issue.

为什么调试器不能打破断点?

Why is the debugger not able to hit the break point?

提前感谢!

如果可以重现此问题,请考虑投票这篇文章

And if you can reproduce this issue, please consider voting on this post.

推荐答案

当所提供的示例内置于发行模式,然后JIT-ed为64位机器代码时,它不包含足够的信息用于调试器将断点与任何特定关联机器指令。这就是为什么调试器在执行JIT-ed机器代码时永远不会停止在这个断点上。它只是不知道停在哪里。可能是64位CLR调试器中的某种错误行为甚至是一个错误,因为只有当它被JIT编入64位机器代码而不是32位机器代码时才可重现。

When the provided example is built in release mode and then JIT-ed into 64-bit machine code, it does not contain enough information for the debugger to correlate the breakpoint with any particular machine instruction. That’s why debugger never stops at this breakpoint during execution of a JIT-ed machine code. It just does not know where to stop. Probably it is some kind of misbehavior or even a bug in 64-bit CLR debugger because it is reproducible only when it is JIT-ed into 64-bit machine code but not into 32-bit machine code.

当调试器在代码中看到一个断点时,它尝试在JIT-ed代码中找到与断点标记的位置相对应的机器指令。首先,需要找到一个对应于C#代码中的断点位置的IL指令。那么它需要找到一个对应于IL命令的机器指令。然后在找到的机器指令上设置一个真正的断点,并开始执行该方法。在你的情况下,看起来调试器只是忽略断点,因为它不能映射到特定的机器指令。

When the debugger sees a breakpoint in your code it tries to find out a machine instruction in the JIT-ed code that corresponds to the location marked by the breakpoint. First, it needs to find an IL instruction that corresponds to a breakpoint location in your C# code. Then it needs to find a machine instruction that corresponds to the IL command. Then it sets a real breakpoint on the found machine instruction and starts execution of the method. In your case, it looks like that the debugger just ignores a breakpoint because it cannot map it to a particular machine instruction.

调试器找不到机器指令的地址如果... else语句就立即执行。 if ... else语句和其中的代码会以某种方式导致此行为。没有什么声明遵循if ... else。您可以用其他一个替换Console.WriteLine(2)语句,您仍然可以重现此问题。

The debugger cannot find an address of a machine instruction that immediately follows if…else statement. The if…else statement and the code inside it somehow causes this behavior. It does not matter what statement follows the if…else. You can replace the Console.WriteLine("2") statement with some other one and you will be still able to reproduce the issue.

您将看到C#编译器在读取列表的逻辑周围发出一个try ... catch块,如果您将使用Reflector反汇编生成的程序集。它是C#编译器的一个文档特征。您可以在 foreach语句

You will see that the C# compiler emits a try…catch block around the logic that reads the list if you will disassemble the resulting assembly with Reflector. It is a documented feature of the C# compiler. You can read more about it at The foreach statement

尝试... catch ... finally块对JIT-ed代码有一个很好的侵入性影响。它使用Windows SEH机制,并重写您的代码。我现在找不到一篇很好的文章的链接,但是如果你有兴趣,我相信你可以找到一个。

A try…catch…finally block has a pretty invasive effect on a JIT-ed code. It uses the Windows SEH mechanism under the hood and rewrites your code badly. I cannot find a link to a good article right now but I’m sure that you can find one out there if you are interested.

这是在这里发生的。 try ... finally阻止if ... else语句导致调试器打嗝。您可以使用简单的代码重现您的问题。

It is what happens here. The try…finally block inside of if…else statement causes the debugger to hiccup. You can reproduce your issue with a much simple code.

bool b = false;
if (b)
{
    try
    {
        b = true;
    }
    finally
    {
        b = true;
    }
}
else
{
    b = true;
}
b = true;

此代码不调用任何外部函数(它消除了其中一个答案提出的方法内联的影响),它直接编译成IL,而不用C#编译器添加任何附加编码。

This code does not call any external functions (it eliminates effect of method inlining proposed by one of the answers) and it compiles directly into IL without any additional coded added by the C# compiler.

只有在释放模式下才可重现,因为在调试模式下,编译器会发出IL NOP指令您的C#代码的每一行。 IL NOP指令什么也不做,它直接编译到没有任何东西的JITER的CPU NOP指令。该指令的有用性是调试器可以使用它作为断点的锚点,即使其余的代码被JITer重写。

It is reproducible only in release mode because in the debug mode the compiler emits the IL NOP instruction for every line of your C# code. The IL NOP instruction does nothing and it is directly compiled to the CPU NOP instruction by the JITer that does nothing too. The usefulness of this instruction is that it can be used by the debugger as an anchor for breakpoints even if the rest of the code is badly rewritten by the JITer.

我是能够使调试器正常工作,将一个NOP指令放在if ... else之后的语句之前。

I was able to make the debugger to work correctly by putting one NOP instruction right before the statement that follows the if…else.

您可以在这里阅读有关NOP操作和调试器映射过程的更多信息调试IL

You can read more about NOP operations and debugger mapping process here Debugging IL

您可以尝试使用WinDbg和SOS扩展来检查JIT- ed版本的方法。您可以尝试检查JIT生成的机器代码,并尝试了解为什么无法将机器代码映射到C#的特定行。

You can try to use WinDbg and SOS extension for it to examine JIT-ed version of the method. You can try to examine machine code that JIT-er generates and try to understand why it cannot map back that machine code to particular line of C#.

这里是几个链接使用WinDbg打破托管代码并获取JIT-ed方法的内存地址。我相信你应该可以找到一种方法来获取JIT-ed代码,从中获取方法:在WinDbg中设置断点用于托管代码 SOS作弊表(.NET 2.0 / 3.0 / 3.5)

Here are couple link about using WinDbg for breaking in managed code and getting a memory address of a JIT-ed method. I believe that you should be able to find a way to get JIT-ed code for a method from there: Setting a breakpoint in WinDbg for Managed Code, SOS Cheat Sheet (.NET 2.0/3.0/3.5).

您还可以尝试向Microsoft报告问题。可能这是一个CLR调试器错误。

You can also try to report an issue to Microsoft. Probably this is a CLR debugger bug.

感谢您的有趣的问题。

这篇关于一个好奇的Visual Studio 2010调试器(它不能打破点)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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