This article helps developers who are at a beginner or intermediate level but don't have much understanding about the importance of PDBs and why they are required.
What is PDB
PDB is an acronym for Program database file.
A PDB file is typically created from source files during compilation. It stores a list of all symbols in a module with their addresses and possibly the name of the file and the line on which the symbol was declared. (from wiki)
Why PDB as a Separate File?
These symbols can be very well embedded in the binary, but it in turn results in the file size growing significantly larger (sometimes by several megabytes). To avoid this extra size, modern compilers and early mainframe debugging systems output the symbolic information into a separate file; for Microsoft compilers, this file is called a PDB file.
What Does the PDB File Contain
Following is some of the important information stored by PDB file:
- Local variable name - To prove that pdb contains local variable name, we will make use of Reflector to decompile the assembly with its PDB present in the same folder as that of assembly. Reflector has a option called "Show PDB symbols" as shown in the screenshot which when checked also loads corresponding PDB for that assembly. When you check the option, you can see the decompiled code has the same variable name as that of your actual code but in the absence of that PDB or when this option is unchecked, your local variables in your decompiled code would get replaced with names like "
string variable and "
num" for decimal, etc.
- Source file name
- Line number of the source
- Source Indexing (explained in later section)
To show that PDB contains source file name and line number of source (point 2 and 3), run the following console application first with PDB present in the same folder and second by deleting the PDB file.
static void Main(string args)
int sum = Add(5, 10);
decimal value = Divide(10, 0);
private static int Add(int i, int j)
return i + j;
private static decimal Divide(int i, int j)
return i / j;
catch (Exception ex)
private static void LogError(Exception ex)
using (var txtWriter = new StreamWriter(@"dump.txt",true))
string error = "Exception:" + ex.Message +
Environment.NewLine + "StackTrace:" + ex.StackTrace
With PDB, this is the exception thrown by the application:
Exception:Attempted to divide by zero.
StackTrace: at UnderstandingPDBs.Program.Divide(Int32 i, Int32 j) in
C:\Users\Rishi\Documents\Visual Studio 2010\Projects\UnderstandingPDBs\Program.cs:line 33
Without PDB, exception shows the following message:
Exception:Attempted to divide by zero.
StackTrace: at UnderstandingPDBs.Program.Divide(Int32 i, Int32 j)
Clearly, the one with PDB shows line number and file name of the class where exception is thrown.
How PDB is Loaded by Debugger?
The Visual Studio debugger expects the PDB file to be under the same folder as the DLL or EXE. PDB files generated for the assembly are unique for each build, that means you can't use the PDB created in previous build with the assembly created in any other build even if there are no code changes. Debugger finds out if the PDB is for that binary by comparing a specific GUID in PDB with that of the binary. This Guid is embedded during compilation in both binary and PDB which tightly links PDB with its binary.
Different Build Settings in Visual Studio
Visual Studio has 3 different Build Options which control the debug symbols generation:
- none: PDB files will not be generated.
- pdb-only: The debug symbols will be only in PDB files and not in Binary
- Full: Along with symbols in PDB binary will also contain some debug symbols
Full is the default option set in Visual Studio.
According to MSDN:
"If you use /debug:full, be aware that there is some impact on the speed and size of JIT optimized code and a small impact on code quality with /debug:full. We recommend /debug:pdbonly or no PDB for generating release code."
Should We Deploy PDBs Along with Binaries?
If the size of deliverables are not a concern, it's good to have PDB deployed along with other binaries as it helps provide more information about exceptions as we saw in the above example. These PDBs can be very helpful during certain crashes occuring intermittently for some user which without PDB will make life difficult.
It is not that you must have PDBs along with Binary deployed to get that extra information about the exception. The same can be achieved using Symbol Server and Source Indexing which I will discuss in the below topic.
Security Risk with PDB?
Anyone having access to DLL/EXE can easily do reverse engineering to generate the source code with or without PDB using tools like reflector. So not providing PDB will not be of much help in this case.
If PDB is deployed and the user doesn't have access to binaries, it's not a good idea to show them the stack trace information and let them know the internals of the application.
Symbol server is used to store pdb files which are known to the debugger and can be used to find more descriptive call stack information.
We can set up our own Symbol server using the symstore.exe, which allows debugger to find the actual PDB associated with binary in question. symstore.exe is included in the Debugging tools for Window package.
Microsoft also maintains Symbol server which we can use by loading the PDBs from Microsoft's Symbol server.
How and Why to load Microsoft Symbol Store
When you stop execution at debug point and open Modules Window (as shown below), you will find all the DLLs(external or internal) loaded until that breakpoint but the Symbol status by default will show "Cannot find or open pdb file" except for your PDB. These are the Microsoft BCL binaries which are not loaded because our debugger can't find the related PDBs.
To load these symbols, go to Debugging->Symbols and check Microsoft Symbol Servers and give the Cache symbols in this directory as any shared folders so that it can be used by all the developers.
Since these binaries are external to your application, you also need to uncheck "Enable just my Code" in Debugging->General menu.
In the below screenshot, you can see that I have loaded the Symbol and now the status of the symbols shows "Symbols loaded".
So how can this be useful?
You can put breakpoint in your code and see the call stack with and without the symbols loaded.
The below figure shows the call stack without the Symbols loaded and it just displays my methods and the BCL's method as just [External Code] .
With the symbols loaded, the call stacks display all the method calls prior to the breakpoint (see below figure). It certainly can be helpful when we want to know what are the external methods called so that it can be analyzed using Reflector or by debugging Disassembly, while sorting out any particular issue in our application related to some behaviour change due to external code.
Like Symbol server, there is also something called Source Server which are used to retrieve the exact version of source file used to build any particular application. Binaries can be source indexed when it is build and this information is stored in PDB file which helps source server to find the exact source file.
You can check MSDN to know more about Symbol and Source Store.
Points of Interest
PDB files are Microsoft proprietary files and are least documented. I will be happy to learn more with your feedback, and will explore and publish more about it in the future.