1

I have a situation where the c# compiler is doing some strange stuff when optimisation is turned on (ie in release mode) and you start stepping through the code (with Enable just my code turned off)

I have code similar to the following, what it does is basically see if the settings provider has an application environment that it wants to set, otherwise it leaves the environment alone.

var environmentValue = settingsProvider.ApplicationEnvironment;
if (!string.IsNullOrWhiteSpace(environmentValue))
{
    switch (environmentValue.ToLower())
    {
        case "p":
            _connectionSettings.Production();
            break;
        case "t":
            _connectionSettings.Test();
            break;
        case "d":
            _connectionSettings.Development();
            break;
        case "l":
            _connectionSettings.Local();
            break;
    }
}

However when optimisations is turned on it is always hitting the last case statement even when the value of environmentValue is null i.e., shouldn't be getting into the switch in the first place.

I've had to change the code so that it is like this instead, and it now hits the default: case instead.

var environmentValue = settingsProvider.ApplicationEnvironment;
if (!string.IsNullOrWhiteSpace(environmentValue))
{
    switch (environmentValue.ToLower())
    {
        case "p":
            _connectionSettings.Production();
            break;
        case "t":
            _connectionSettings.Test();
            break;
        case "d":
            _connectionSettings.Development();
            break;
        case "l":
            _connectionSettings.Local();
            break;
        default:
            Console.WriteLine(environmentValue);
            break;
    }
}

I'm running this through Visual Studio 2015, Framework version 4.5.1, Release Mode, Any CPU (prefer 32 bit), and Enable Just My Code turned off.

When I first started this post I didn't realise that it was only when stepping through the code. It does seem to have some strange behaviour though since I wasn't stepping through the code and was still hitting the last case statement.

I've also created a gist project which illustrates the issue.

https://gist.github.com/matthewvukomanovic/899d595cd0b787116b95ddb5f2b128d3#file-program-cs

With the test application I've written it only needs another call afterwards which will force it to behave correctly, however in the real application it already has other calls which use that same object, however it doesn't work quite the same.

Does anyone know how to avoid this issue when stepping through optimised code, apart from adding a dummy default case like I have?

Matt Vukomanovic
  • 1,392
  • 1
  • 15
  • 23

1 Answers1

2

That is in general an issue with Release built code, the optimizer reorders code and that makes the mapping from executable machine code back to the source lines in your program fuzzy. It is the basic reason why a Debug configuration exists in the first place. Single-stepping and breakpoints are not the only debugging features that misbehave, inspecting local variables stops working as well.

There is a debugger option that forces the optimizer to be disabled when you use the debugger, even for Release built code. Use Tools > Options > Debugging > General > tick the "Suppress JIT optimization" checkbox. Turned on by default. Your sample program behaves properly with that option turned on. So you probably have it turned off.

That option cannot always be effective, won't work when you attach the debugger to a running program or break in with Debugger.Break(). Do keep in mind that modifying your code was quite unnecessary. You can tell from your repro snippet that it does not actually call the Local() method. It just highlighted the wrong source code line, that's all. Fuzzy mapping. Just keep in mind that debugging will be wonky when you debug a Release build of your code and you'll be ahead. And turn the option back on.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • It's strange since it was actually calling the method since it was connecting to the local service instead of the one it was supposed to. However I haven't been able to get it to reproduce that now. With that option turned on it shows correctly though as well. Thank you – Matt Vukomanovic Jul 19 '16 at 01:42