I have a simple clean room example of this possible bug.
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();
}
If compiled in x64 or AnyCPU (when prefer 32bit is set to false in VS2012) the if you put a breakpoint in the if block, it is always hit.
We tried it in VS2012, VS2010 and VS2008 and they all fired the if block when compiled in 64bit, yet in 32bit it does not fire the if block.
We looked at the IL for 32 bit and 64 bit versions and they look the same.
We found this in production code because the if block was being ran and the exception was being thrown no matter what the value of the boolean variable, though in the simple example we cannot seem to throw the exception, it is happening in production code.
Since it is happening in production code, it is not just a debugger issue.
Very strange behavior, but seems to not be acutally running any code in the if block. Developer jumped the gun in assuming that was the exception he was seeing.
(All debugging is in debug mode - production is in release)
If the throw is commented out - the if block is not reached.
Okay, I see it. This indeed goes wrong in the Debug build for the 64-bit debugger. The key is to set the breakpoint exactly on the if() statement and then to start stepping. It will look like the throw statement is getting executed. But it that doesn't actually happen, actual code execution is proper.
To see what is happening, have it step into the throw statement line. Then use Debug + Disassembly to see where it is actually located. On my machine it looks like this:
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;
You can even see it from the way the debugger groups the machine code instructions with the C# statements. Note how the debugger is confused about the NOP at address 0088. It thinks it belongs the compound if() statement. So it puts the yellow highlight inside the block. But the program has actually taken the jump at address 005b and has skipped the throw statement (addresses 005e through 0083).
Not so sure where to lay the blame on this one, can't blame the C# compiler or the PDB file since this operates correctly in 32-bit mode. It smells like a jitter problem, notable is that the x86 jitter doesn't generate the NOP instruction. You could also make the case that the jitter should have generated the JNE instruction to jump to address 0089. These are but guesses, you can get a real answer at connect.microsoft.com
Just keep this quirk in mind until you hear back or we all get an update in a service pack. The code is actually executing correctly so you only suffer a mild case of bewilderment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With