How we came across the bug
Today we were debugging some unit tests which passed on a local machine, but were failing on our build server. We decided to step through the code using the Visual Studio 2010 debugger. We had real trouble understanding how the test ever passed at all as the debugger was showing a variable having a value of 0 when, we knew it should have a value of 103.4, and the test should definitely fail if the value was 0.
This resulted in a great deal of head scratching, and eventually resorting to writing to the Debug window, which proved the variable did hold the value of 103.4 even though the debugger repeatedly showed the variable holding the value of 0. The initial tests used the ReSharper test runner, so we ran a check that debugging a standalone version of NUnit produced the same value of 0, and it did. So it wasn’t a tooling issue, and unlikely to be specific to NUnit.
As our code was part of a suite of unit tests, which in turn was part of a medium sized solution with many dependencies, we had to produce a repeatable test with the minimum number of lines of code. We created a new console application and created the three lines of codes with two methods that were the bare minimum to mirror the code in our unit test project.
However, we couldn’t make it reproduce the fault we’d seen. We nearly gave up, but as I had Visual Studio 2010 Ultimate installed on my machine I thought it was a good chance to use IntelliTrace to see if that changed how variables were handled in the debugger. However, we were surprised to see in the help files that IntelliTrace only worked when a project Platform Target is set to target a x86 CPU. This is when the penny dropped – our projects target Any CPU which on our systems means x64. The moment I set the Platform Target to x64 the error was reproduced.
Show me the code!
Here is how to reproduce the issue. In Visual Studio 2010, create a Visual C#, Windows application, using the Console Application template. In the code file Program.cs replace the code with the following code,
static void Main(string args)
Decimal result = GetNullableValue() ?? GetNonNullableValue();
static decimal? GetNullableValue()
static decimal GetNonNullableValue()
What does the code do?
The code has two methods. GetNullableValue, may return null or a decimal value. A second method, GetNonNullableValue, is guaranteed to return a decimal value every time.
The result variable in the Main method is assigned using the null-coalescing operator, ??, which will assign the return value of the first method call unless it has a null value, in which case it will assign the return value of the second method. This guarantees that there will always be a decimal value assigned to the variable result.
Reproducing the problem
To view the variable result with the debugger set a breakpoint on the line containing Console.Write. If you left the project with the default settings, the Platform Target should be set to x86. When you run the program, it will reach the breakpoint, and if you hover over the result variable it will show the value 200.22.
Now right click on the console project in the Solution Explorer and configure the Platform Target to x64. Now when you run the program, it will reach the breakpoint, and if you hover over the result variable it will show the value 0.
In either case the actual value held in the variable is 200.22. You can see this when the Platform Target is set to x64 by reaching the breakpoint and pressing F10 to step one line further in the code. If you switch to the console application window, there is the correct value of 200.22 even though the debugger showed the variable holding the value 0 when you hover over it in the code window.
Even weirder behaviour ...
During trying to reproduce the problem, we edited our original code to see if I could ‘fix’ the problem by using a different method of assigning the variable. This is when we discovered that if you add an additional variable assignment after the line making the assignment to result,
bool AnyOldVariableWeDontActuallUse = true;
this resolved the issue and the debugger displayed the correct value. This is true even if the variable is not actually used by any other code. This additional variable can be of any type but it must be initialised if it is to work. If you leave this additional variable with a default value it won’t resolve the issue with the debugger.
Our tests were performed in Windows 7 64-bit, with Visual Studio 2010 Professional RTM, Visual Studio 2010 Ultimate RTM and Visual Studio 2010 Ultimate SP1, and all exhibited the same fault in the debugger when the Platform Target was set to x64.
I haven’t delved into the IL code generated, as clearly the actual code and .Net Framework is functioning as expected. The fault appears only to lie in the x64 implementation of the Visual Studio 2010 debugger. As Visual Studio 2010 Professional doesn’t include IntelliTrace the x86 debugger is the standard debugger for Visual Studio, one would expect this to be identical to that in the x64 version.
So if you are debugging an Any CPU or x64 don’t necessarily believe the value that the debugger might tell you is stored in a variable. Not a comforting thought.
I have placed this issue on Microsoft Connect if you wish to provide additional feedback to Microsoft on this issue,
| posted on Thursday, March 31, 2011 9:30 AM