How to debug .NET source code in Visual Studio

When you’re using the Visual Studio debugger to step through your code, sometimes it’s useful to be able to step into the .NET source code. I had to do this recently to figure out why my ASP.NET Core code wasn’t behaving as expected.

You can enable .NET source code debugging with the following steps:

  • In the Visual Studio menu, click Debug > Options…
  • In the Debug > General section:
    • Uncheck Enable Just My Code.
    • Check Suppress JIT optimization on module load (Managed only).
    • Check Enable Source Link support.
Visual Studio Debugging options window showing which options to check/uncheck
  • In the Debug > Symbols section:
    • Check Microsoft Symbol Servers
    • Check NuGet.org Symbol Server
Visual Studio Debugging options showing which symbol servers to pick
  • Click OK to apply the changes.

Now put a breakpoint in your code and start debugging.

The first time you run the debugger, it’ll download debug symbols for lots of .NET assemblies. It saves them to the symbol cache directory (this defaults to %TEMP%\SymbolCache, which is a user temp directory). This can take awhile, and it may seem like your program is frozen. I suggest opening the Output window (Debug > Windows > Debug) to see it downloading symbols. Just wait for it to finish. Here’s what this looks like:

Visual Studio Output window showing debug symbols loading for several .NET assemblies

Debugging example

Here’s an example of debugging .NET source code. In my case, I had to figure out something in ASP.NET Core. So I put a breakpoint in my code (this is my background service’s StopAsync() method):

public async override Task StopAsync(CancellationToken cancellationToken) { Log($"Start of StopAsync(). "); //details not shown Log("End of StopAsync()"); //I put a breakpoint here }
Code language: C# (cs)

When the debugger hits the breakpoint, you can step into the .NET source code. In my case, I stepped out of my StopAsync() method and into the .NET source code that was calling it. Here’s a snippet of the code:

//Snippet in Microsoft.Extensions.Hosting.Host.StopAsync() if (_hostedServices != null) { foreach (IHostedService hostedService in _hostedServices.Reverse()) { try { await hostedService.StopAsync(token).ConfigureAwait(false); } catch (Exception ex) { exceptions.Add(ex); } } }
Code language: C# (cs)

This allowed me to figure out exactly why ASP.NET Core was not behaving as I expected in this specific scenario. It turned out I had been reading outdated documentation, which referred to outdated source code, hence why the behavior was baffling.

Note: You’ll probably want to revert the settings after you’re done. Debugging .NET source code is usually a last resort.

Leave a Comment