13

我有一个这个可能的错误的简单洁净室示例。

   static void Main(string[] args)
    {
        bool MyFalse = false;

        if (MyFalse)
        {
            throw new Exception();
        }
        try
        {
            int i = 0;
        }
        catch (Exception e)
        {
            Console.Write(e);
        }

        Console.Read();
    }

如果在 x64 或 AnyCPU 中编译(在 VS2012 中将首选 32 位设置为 false 时),如果您在 if 块中放置断点,它总是会被命中。

我们在 VS2012、VS2010 和 VS2008 中尝试过,它们在 64 位编译时都触发了 if 块,但在 32 位中它不会触发 if 块。

我们查看了 32 位和 64 位版本的 IL,它们看起来相同。

我们在生产代码中发现了这一点,因为 if 块正在运行并且无论布尔变量的值是什么都会抛出异常,尽管在简单的示例中我们似乎无法抛出异常,它发生在生产代码中。

由于它发生在生产代码中,因此不仅仅是调试器问题。

非常奇怪的行为,但似乎并没有在 if 块中实际运行任何代码。开发人员假设这是他所看到的例外情况,因此开枪了。

(所有调试都处于调试模式 - 生产正在发布中)

如果 throw 被注释掉 - 没有到达 if 块。

4

2 回答 2

20

好的,我明白了。这在 64 位调试器的调试版本中确实出错了。关键是在 if() 语句上准确设置断点,然后开始单步执行。看起来throw 语句正在被执行。但这实际上并没有发生,实际的代码执行是正确的。

要查看发生了什么,请让它进入 throw 语句行。然后使用 Debug + Disassembly 查看它的实际位置。在我的机器上看起来像这样:

       if (MyFalse)
00000040  movzx       ecx,byte ptr [rbp+8] 
00000044  xor         eax,eax 
00000046  test        ecx,ecx 
00000048  sete        al 
0000004b  mov         dword ptr [rbp+1Ch],eax 
0000004e  movzx       eax,byte ptr [rbp+1Ch] 
00000052  mov         byte ptr [rbp+18h],al 
00000055  movzx       eax,byte ptr [rbp+18h] 
00000059  test        eax,eax 
0000005b  jne         0000000000000088            // <=== Note this jump
        {
0000005d  nop 
            throw new Exception();
0000005e  lea         rcx,[5B848928h] 
00000065  call        000000005F65E9E0 
0000006a  mov         qword ptr [rbp+20h],rax 
0000006e  mov         rax,qword ptr [rbp+20h] 
00000072  mov         qword ptr [rbp+28h],rax 
00000076  mov         rcx,qword ptr [rbp+28h] 
0000007a  call        000000005BE4A5D0 
0000007f  mov         rcx,qword ptr [rbp+28h] 
00000083  call        000000005F73E36C 
00000088  nop                                     // <=== yellow arrow here
        }
        try
        {
00000089  nop 
            int i = 0;

您甚至可以从调试器将机器代码指令与 C# 语句分组的方式中看到它。请注意调试器如何对地址 0088 处的 NOP 感到困惑。它认为它属于复合 if() 语句。所以它将黄色突出显示在块内。但是程序实际上已经在地址 005b 处进行了跳转,并跳过了 throw 语句(地址 005e 到 0083)。

不太确定该归咎于何处,不能归咎于 C# 编译器或 PDB 文件,因为它在 32 位模式下正常运行。它闻起来像一个抖动问题,值得注意的是 x86 抖动不会生成 NOP 指令。您还可以假设抖动应该生成 JNE 指令以跳转到地址 0089。这些只是猜测,您可以在 connect.microsoft.com 获得真正的答案

请记住这个怪癖,直到您收到回复或我们都在服务包中获得更新。代码实际上是正确执行的,所以你只会遇到轻微的困惑。

于 2013-05-03T20:11:37.840 回答
10

在优化代码中,MSIL 与本机机器代码和源代码之间没有严格的关联。这会导致调试器有时会突出显示与正在执行的代码不同的代码,在单步执行时多次突出显示同一行,或者将断点放置在与您预期不同的位置。

这是调试优化代码的一个基本问题,代表了调试信息格式的不足。编译器或调试器中都没有错误。您可能不得不求助于反汇编视图中的调试。

于 2013-05-03T19:07:35.507 回答