How to debug NuGet packages using SourceLink

Using a NuGet package is a very convenient way to add a dependency to your project. However, when you have an issue with a NuGet package and you want to debug it, it's hard because you don't have the source code.

Lots of NuGet packages have their source code on GitHub, so it would be very convenient to automatically get the code from the GitHub repository. This is what SourceLink provides. It adds some metadata to the pdb file to remap the local files to the files on GitHub, so Visual Studio can download the files when needed.

The most downloaded NuGet package Newtonsoft.Json now uses SourceLink, so you can step into the package code from Visual Studio. Let's see how you can do the same for your projects!

Create a NuGet package with SourceLink enabled

SourceLink is very simple to enable in your build. All you have to do is to add a reference to the NuGet package Microsoft.SourceLink.GitHub. You can also add some optional properties to control SourceLink.

<Project>
  <PropertyGroup>
    <!-- Optional: Include PDB in the built .nupkg -->
    <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

    <!-- Optional: Declare that the Repository URL can be published to NuSpec -->
    <PublishRepositoryUrl>true</PublishRepositoryUrl>

    <!-- Optional: Embed source files that are not tracked by the source control manager to the PDB -->
    <!-- This is useful if you generate files during the build -->
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
  </PropertyGroup>

  <ItemGroup>
    <!-- Required -->
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-*" PrivateAssets="All"/>

    <!-- Required if your repository is on VSTS -->
    <!--<PackageReference Include="Microsoft.SourceLink.Vsts.Git" Version="1.0.0-*" PrivateAssets="All"/>-->

    <!-- Required if your repository is on GitLab -->
    <!--<PackageReference Include="Microsoft.SourceLink.GitLab" Version="1.0.0-*" PrivateAssets="All"/>-->
  </ItemGroup>
</Project>

You can find more information on the GitHub repository of SourceLink: https://github.com/dotnet/sourcelink/#using-sourcelink

In Visual Studio, you need to uncheck Enable Just My Code:

Then, when you want to step into a method from a dll with SourceLink enabled, you get the popup:

Then, you can continue debugging:

You can test you package is well configured using the SourceLink global tool. If you don't know what is a global tool, you can check my previous post about it. First, install the tool:

dotnet tool install --global sourcelink
C:\Users\mezia>sourcelink --help
Source Code On Demand

Usage:  [options] [command]

Options:
  -h|--help  Show help information

Commands:
  print-documents  print the documents stored in the pdb or dll
  print-json       print the Source Link JSON stored in the pdb or dll
  print-urls       print the URLs for each document based on the Source Link JSON
  test             test each URL and verify that the checksums match

Use " [command] --help" for more information about a command.
  • Print the mapping document in the pdb file:
PS> sourcelink print-json meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
{"documents":{"D:\\a\\1\\s\\*":"https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/*"}}
  • Test that the files are downloadable:
PS> sourcelink test meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
sourcelink test passed: meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
  • Print the sha1 of every files in the pdb:
PS> sourcelink print-documents meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
da677f59aa0a2b2e266e869ebebae275f49adfc0 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\AssemblyUtilities.cs
783e778429958369673e2a6baac5560af28c2c98 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ByteArrayExtensions.cs
753671f42091a5fe5dd71c2db4ae9bd34838c4a2 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\CultureInfoUtilities.cs
a2e7fcc4b5535b5f3861d74fce424088ab6c6001 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DateTimeUtilities.cs
bb19ae0d30831c17c0bcc709a74c07bb6b87ae7a sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DebounceExtensions.cs
3067c4d797a2fd9edae131a946ef584bf4c32760 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\EnumerableExtensions.cs
a9124ea95b058ac8d4f46fc8fff801d9082bcefa sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ExceptionExtensions.cs
...
  • Print the URLs of every files in the pdb:
PS> sourcelink print-urls meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
da677f59aa0a2b2e266e869ebebae275f49adfc0 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\AssemblyUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/AssemblyUtilities.cs
783e778429958369673e2a6baac5560af28c2c98 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ByteArrayExtensions.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/ByteArrayExtensions.cs
753671f42091a5fe5dd71c2db4ae9bd34838c4a2 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\CultureInfoUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/CultureInfoUtilities.cs
a2e7fcc4b5535b5f3861d74fce424088ab6c6001 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DateTimeUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/DateTimeUtilities.cs
bb19ae0d30831c17c0bcc709a74c07bb6b87ae7a sha1 csharp
d19483e16d67ae4da6e044d3fdeb35dd87480282 sha1 csharp C:\Users\buildguest\AppData\Local\Temp\.NETStandard,Version=v2.0.AssemblyAttributes.cs embedded
...

BTW, you can see that the file AssemblyAttributes.cs is embedded in the pdb because of the attribute <EmbedUntrackedSources>true</EmbedUntrackedSources>.

Conclusion

SourceLink is very useful if you publish a NuGet package where the code is hosted on GitHub, GitLab or VSTS. It allows the consumers of your package to step into the code from Visual Studio and start debugging the code. It's so easy to setup, that you have no reason to not add it to your project.