Debugging .NET Core via Symbol Server on OSX in VSCode

Debugging .NET Core via Symbol Server on OSX in VSCode

In a previous life, I was a heavy user of Visual Studio on Windows. But for the last few years I have been working exclusively on Mac OSX and using VSCode as my primary editor.

One feature of the full Visual Studio that I miss is integrating with a Symbol Server for debugging code that does not belong to me.

If you are not familiar with how a debugger works (at least in .NET), debugging information is stored in a symbol file separate from the executable that is being built by a compiler. All modern version of Microsoft compilers store that information in  a program database file, or .pdb.

You might recognize those files from building a debug version of your applications. But did you know that Microsoft also produces symbol files for the .NET Core framework libraries as well?

And they make those symbols available via a Symbol Server. A symbol server enables debuggers to automatically retrieve the correct symbol files for a given library.

Micorsoft also makes their source code available via a Source Server. This allows a client to retrieve the exact version of the source code that were used to build an assembly.

Combining a Symbol and Source Server it is possible to debug right into any library you are using just like you do with your own code.

In full Visual Studio there is a deeply nested configration setting that you enable to make all this work. It is a bit more involved to get working in VSCode, let me outline the steps for you.

First, install the dotnet cli global tool for symbols.

dotnet tool install -g \
    --add-source https://api.nuget.org/v3/index.json \
    dotnet-symbol

This will add everything that is needed to fetch symbols from the primary NuGet source. This happens to be where we get all of the .NET libraries from.

We can use this tool now by issuing the command dotnet symbol from our terminals.

Next, we need to pull the symbols for the meta package we are using to build the application.

dotnet symbol --symbols \
  --output /tmp/symbols \
  /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/3.1.2/*

This will use the symbol tool to download the pdb symbol  files to a temporary directory for all the dlls found in the AspNetCore meta package version 3.1.2 that is located on my local machine.

Once downloaded, we can copy them into the meta package directory.

sudo cp /tmp/symbols/* \
  /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/3.1.2

Now that the symbols are in the right place, the debugger can use them to fetch source code and debug into the .NET Core framework. These steps only need to be completed once per .NET Core SDK you have installed. Any application you build from here out on that SDK will have the symbols available to it.

But if we jump into VSCode and attempt to debug an application, we will start seeing messages like this in the Debug Console.

Loaded '/src/sample/src/server/bin/Debug/netcoreapp3.1/Microsoft.IdentityModel.Protocols.dll'.
Skipped loading symbols.
Module is optimized and the debugger option 'Just My Code' is enabled.

This message is telling us that the debugger skipped loading external symbols because the "Just My Code" debugger option is enabled.

This is a feature of the debugger that allows us to focus our debugging session only on code that we have written. And that is probably the normal state we want to use.

But we can easily tell the debugger that we want to debug all the code we have symbols for by setting a flag in the launch.json file.

{
   "version": "0.2.0",
   "configurations": [
        {
            "name": ".NET Core Launch (web)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceFolder}/src/server/bin/Debug/netcoreapp3.1/server.dll",
            "args": [],
            "cwd": "${workspaceFolder}/src/server",
            "stopAtEntry": false,
            "justMyCode": false,
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        }
    ]
}

In this example, we have added the justMyCode property to the .NET Core Launch configuration and set it's value to false.

And just like that, we are debugging the .NET Core framework libraires on our local machine.

Follow me on Mastodon!