我认为我的团队在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块中运行任何代码。开发人员过早地认为这是他所看到的例外。
(所有的调试都是在调试模式-生产是在发布中)
如果throw被注释掉- If块没有到达
好的,我看到了。这在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与本机机器码和源代码之间没有严格的相关性。这导致调试器有时会突出显示与正在执行的代码不同的代码,在单步执行时多次突出显示同一行,或者将断点放在与预期不同的位置。
这是调试优化代码的一个基本问题,并且表示调试信息格式的不足。在编译器或调试器中都没有错误。您可能不得不在反汇编视图中进行调试。