我认为我的团队在64位编译器中发现了一个bug,其他人可以确认或告诉我为什么这是正确的吗?

本文关键字:确认 告诉我 其他人 为什么 bug 一个 64位 团队 我的 编译器 发现 | 更新日期: 2023-09-27 18:12:06

我有一个简单的洁净室例子。

   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中编译(当prefer 32位在VS2012中设置为false时),如果在If块中放置一个断点,它总是被击中。

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

我们查看了32位和64位版本的IL,它们看起来是一样的。

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

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

非常奇怪的行为,但似乎实际上并没有在if块中运行任何代码。开发人员过早地认为这是他所看到的例外。

(所有的调试都是在调试模式-生产是在发布中)

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

我认为我的团队在64位编译器中发现了一个bug,其他人可以确认或告诉我为什么这是正确的吗?

好的,我看到了。这在64位调试器的Debug构建中确实会出错。关键是要准确地在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指令。您还可以假设抖动应该生成跳转到地址0089的JNE指令。这些都是猜测,你可以在connect.microsoft.com上得到真正的答案

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

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

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