In .NET development Product Database (PDB) files are artifacts that allow us to debug. But vanilla PDBs are unwieldy because you still need to find the relevant source files for debugging. Here's how you can upgrade your PDBs to debugging powerhouses by including (references to) those sources.
The arcane PDB and its missing sources
A developer's first encounter with PDB files often happens after a swift online search when the "Source not found" page pops up while they’re debugging.
That search teaches you that PDB files, built alongside their binaries, are what allow you to debug applications. They contain information required for sticking a debugger to your running application, such as source file paths, line numbers and symbols. However, you'll still need to collect the relevant source files yourself, and that can be a time-consuming process. Luckily, it’s one that we can remove entirely. (Note that this only applies to PDB files you produce yourself.)
One way is embedding sources directly into the PDB file, which Visual Studio can then extract and use. The other is source indexing, i.e. embedding instructions into your PDB file stating where and how to fetch the relevant sources, which Visual Studio can then follow when loading the symbols. Both are outlined below.
It's possible to embed source files directly into the PDBs at compilation time (see Roslyn's GitHub issue #12625).
To have your sources embedded at compile time, run msbuild or csc with the /embed option. Alternatively, you can modify your csproj files to include the EmbedAllFiles property as shown below:
The compiler will take it from here. You'll notice your PDB files will be larger, and a lot more powerful. When debugging, Visual Studio will extract the embedded sources and store them in a temporary directory during debugging:
Extracted embedded document “C:\thi-lib\Thi.Lib\Math.cs to “C:\Users\THI\AppData\Local\Temp\.vsdbgsrc\a8518cddc50\Math.CS”
While this increases the size of your PDBs, they’re no longer dependent on the availability of your sources. I definitely suggest using this approach because the benefits far outweigh the cost of a larger PDB file.
Instead of embedding sources, source indexing embeds instructions on where and how to fetch those sources. While more unwieldy than embedding the sources, it can be used if the PDB size increase makes embedding prohibitive.
If your sources and builds are all in TFS/VSTS, then source indexing is just a matter of setting the Enable Source Indexing property of your build to true. If you are storing your sources or running your builds elsewhere, it becomes more difficult.
I recommend embedding sources instead. Setting up source indexing is a tedious process and the results might not survive the test of time. Source locations, authentication, and the tools for fetching are all subject to change.
In the srcsrv block, you can construct a web request or a tool call to fetch the relevant sources. Which one you choose, and how you configure it, depends largely on where you store your sources. There have been successful attempts to source index files from GitHub and VSTS. You’ll find the details on these and some other helpful links below:
- General explanation on Source Indexing
- Source Indexing GitHub (Powershell)
- Source Indexing GitHub (Python)
- Source Indexing VSTS (Powershell)
- srcsrv block language spec
It’s an intense exercise, but it’s definitely doable. Using the resources above, I got it all working after some struggles. However, when I discovered the possibility of embedding sources, I discarded the work and frankly I’ve never looked back.
While both source embedding and indexing are viable options, I’d strongly recommend the former over the latter. Having your PDBs independent of source availability makes them easier to use, publish, and archive. It also requires far less effort to implement.
The moral of the story? Remember to take proper care of your PDB files. They’ll make your debugging life a whole lot easier if you do.