While working on some project, we ran into a problem when some infrastructure code was separated from our solution and encapsulated into a sort of 3rd party DLL. The problem was that when trying to debug this external DLL, the debugger tried to look for the source code in a specific path – the path where the original source code was at the time of the compilation.
This is obviously a big problem:
- When working with teams in different geographies, where each team is working on the same code, but each team is using a different development server
- In production – you want to be able to debug your application with source code, without relying on the initial location of the source code
This post will concentrate on how to create PDB objects, objects used by the compiler to debug and find the source code, in a way, it will look for the source code from a source control (TFS) instead of source files. You will see that these steps are really easy to follow (especially if you have already TFS and build configuration), so you can implement them in your project immediately.
This post is a compilation of many other interesting posts about this topic (see resources section at the end). it's intended to be short and to the point, to help your projects to become more "debuggable" with the least of efforts.
Thanks a lot to all the articles' authors - John Robbins, Cameron Skinner, and Ed Blankenship.
John Robbins already did a great work on describing what is a PDB file and how the debugger looks for PDB files in his must-read post.
To sum up, a PDB consists of information used by the debugger to perform its work. A .NET PDB contains two pieces of information, the source file names and their lines and the local variable names. The rest of the data is already in the .NET metadata (e.g. public, private and static addresses, global variable names and addresses and more).
The debugger uses the name of the file and a GUID that's embedded in both the PDB file and the binary to look for the correct PDB version.
Using the Code
In order to enable source control PDB, you need to follow these steps:
- Create a public build definition for your project (I won’t go into the general details of public build definition in post, but you can look for it here (look for the Build support section).
- Create a shared folder to be used as a drop folder. Drop folder is where the DLLs and PDBs will be placed by the public build. These DLLs will be referenced in your code.
- Create a shared folder that will be used as your symbol server. A symbol server is a centralized place where your PDBs will reside along with the required source information.
- Update the definition to use the drop location folder:
- Update the definition and set the path to publish symbols. This step does two things – both set the PDB location in the symbol server, AND, does something called source indexing, which create the mapping between the source code and source version. In order to do that, simply copy your symbol server UNC here:
- That’s it – you are done with the definition of your public build. Now you need to actually build it, and prepare your client to work with the symbol server.
Now, how to debug with a symbol server?
- Make sure to reference the DLLs from the drop location. You don’t have to reference the drop location directly, you can copy them to any folder you like. Make sure to check-in the external DLLs and PDBs along with your solution.
- In VS, go to DebugàOptions and Settings .... this will open the debugging sections in the setting dialog box.
- Go to the “Symbols” option, and add your symbol server (please note you may have several, each for each project, or one centralized). To do that, simply click the folder icon right in the top of the screen. At the end, it should look like:
- Go to the “general” option, and make sure to enable source server support:
- Now, you are ready to go and debug your third party code without having access to the source code. When you debug, you will face the following dialog when stepping in the 3rd party code:
The message above will appear for every new file the debugger need to source. If you don’t like that, create a file called SRCSRV.ini, place it in the <Visual Studio Install Directory>\Common7\IDE folder, and fill it with the following text:
tf.exe=<Visual Studio Install Directory>\Common7\IDE\tf.exe
And that's it!
Points of Interest
Please note that if you follow these steps, you don't need to actually deploy the PDB files along with your code. TFS embed a unique GUID in the DLL file and the matching PDB file. Using the symbol server, the information above, the debugger knows to lookup the correct PDB.
Pretty slick, ha?
- 12th January, 2012 - First revision
- 13th January, 2012 - Added resources