Why I cannot see local variables in the middle of the stack while debugging ASP.NET Core source code from Visual Studio

Viewed 1787

I am trying to explore ASP.NET Core pipeline by debugging "out-of-the-box" WeatherForecast application based the API project template.

Based on the various resources I setup the following configuration in VS:

  1. Tools->Options->Debugging->General

    • Enable .NET Framework source stepping - checked
    • Enable Source Link support - checked
    • Suppress JIT optimization on module load - checked
    • Use Managed Compatibility Mode (tried both combinations checked/unchecked, but it does seem to affect anything)
  2. Tools->Options->Debugging->Symbols

    • Load all modules, unless excluded - selected
    • As the location for the symbols I tried both Microsoft Symbol Servers and Symbol Server integrated into JetBrains dotPeek
  3. Project Properties->Debug->Environment Variables

    • COMPLUS_JITMinOpts = 1 [as per https://github.com/dotnet/runtime/issues/5767]. In fact I believe it is what 'Suppress JIT optimization on module load' does.
    • COMPLUS_ZapDisable = 1 [to avoid the load of native images]
  4. Project Properties->Build

    • Optimize Code - unchecked

When I start the debugger and go to Debug->Windows->Modules I can see that all of ASP.NET Core dlls have symbols loaded and Optimized column is set to 'No'. However when I hit a breakpoint in the controller and traverse through the call stack there are a lot of errors in Locals window:

'Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away'

I would think that the optimization wasn't turned off, but I can hit a breakpoint inside of a function in asp.net source code and observe the local variables. The problem comes into play when the execution pointer is moved to a different function. Once it is happened those local variables become unavailable.

In this thread https://github.com/aspnet/Mvc/issues/8375 someone recommends to build the debug version of the assemblies, however I cannot fully grasp why it would be necessary. My understanding that the compiler just makes IL code more clean by removing NOP instructions and the dead code blocks. The real optimization work is done by JIT and can be controlled by COMPLUS_JITMinOpts and COMPLUS_ZapDisable flags.

Is there a way to avoid these 'possibly because it has been optimized away' messages while traversing the stack? It seems I am missing some important concepts here, but I cannot figure out where to look at.

I got the same behavior in both VS2019 (core 3.1) and VS2017(core 2.2)

1 Answers

Try these:

1) No sure what other settings you have used. To give a better guidance for you, I suggest you could first try to reset all vs settings under Tools-->Import and Export Settings-->Reset All settings so that it will avoid other neglectful options.

2) If you use Release mode, please enter Project Properties-->Build-->Advanced--> change Debugging Information to Full. Use the same mode as Debug.

3) please uncheck Optimize Code checkbox under Project Properties-->Build.

Then, check Suppress JIT optimization on module load(Managed only) under Tools-->Debugging-->General.

4) delete bin and obj folder and then rebuild your project again.

5) you can try to convince the JIT compiler not to optimize the code is to use an INI file with the name of the assembly in the same folder the assembly is in with the contents.

Update 1

My solution will make Release mode closer to Debug mode and since it still cannot see local variable, I am afraid that Release mode cannot be changed as we want.

Let me explain it more detail:

Specifically, Release Mode is not produced for debugging purposes, it is the best release for the final product, and obviously there are all sorts of contingencies that can happen if you want to look at the stack and variables in your code, which is a real thing, so we don't recommend doing this.

In Debug mode, the pdb file is very large, it contains all the types of debugging you want, only in debug mode, you can enjoy all debugging types. The debug mode is used for debugging projects, and Release is to create a final optimized version when there are no problems with the debugging project, in order to reduce the release and make it easier for publishers to distribute programs.

As a result, when debugging in Release mode, you can't avoid problems.

My solution is to make Release closer to Debug mode and it seems that the root behavior of Release cannot be changed, so you have to use Debug mode to watch the local variables and this mode it designed for debugging officially. You should use Debug.

Have a try

Related