This is something I typed up internally, to help resolve the confusion that precipitates when Visual Studio begins stepping through comments. Hopefully this will be helpful to someone else.
What is required for source mode debugging?
- Binaries (.dll, .exe, .ocx, …).
- Symbols (the .pdb file).
- Source files.
The symbol files tell your debugger how points in the binary relate to the source files. When you set a breakpoint in a source file, the debugger uses the symbol file to look up the corresponding instruction in the binary.
I have all of these things. Why is my debugger behaving strangely or not working at all?
Well, there are a bunch of different things that can go wrong.
Your symbols might be mismatched.
You can’t (as an example) use the symbols for version 2.0 of a DLL to debug version 1.0 of that DLL. Perhaps, in this case, you have copied a new binary into your run directory without copying the corresponding .pdb file. It’s also possible that your IDE or compiler did that for you.
Some debuggers, like WinDbg, will NOT load a mismatched PDB unless you force this to happen. Others, like Visual Studio 2003, will do this without so much as a complaint. That can be very confusing in some situations.
The module you are trying to debug is not loaded into the process.
If you are attempting to hit a breakpoint, a good sanity check is to make sure that the module you are trying to debug is being loaded. If it isn’t, you are experiencing something other than a PDB problem. For example, your application could be experiencing an exception before your code is called. Make sure you have “break on exceptions” turned on.
In Visual Studio, you can look at the “modules” window to make sure your binary is loaded. The corresponding command in WinDbg is lm.
You are attached with the wrong debugger.
This is another non-symbol issue. There are many different kinds of debuggers:
- Native debuggers: cdb, ntsd, windbg, Visual Studio.
- CLR debuggers: cordbg, mdbg, deblector, Visual Studio.
- Oddballs: the VB6 IDE, VS.NET’s script debugger, etc.
You should make sure you are attaching to or starting your process using the right kind of debugger. Visual Studio gives you a dialog to choose the appropriate debugger(s).
The source file is not recognized.
Say I build a .dll from c:\foo\x.cpp
, and you take that library from me and try to set a breakpoint in c:\bar\x.cpp
. You might need to tell the debugger that your source path is different. Visual Studio is usually pretty good at figuring this out on its own and/or asking you when it is having a problem.
The _NT_SOURCE_PATH
environment variable is honored as a search path for source files by most of the Windows debuggers. The .srcpath command in WinDbg/CDB can be used to display and change the source search path in those debuggers.
The symbols are corrupt.
This is rare, but it can happen. The fix is usually to just rebuild the binary and regenerate the PDB.
The binary is not debuggable.
.NET assemblies have the added limitation that they must be built in /debug mode for conventional CLR debugging to work. You can tell if a module is debuggable by opening it up in Reflector or ILDASM. You should see this attribute:
[assembly: Debuggable(true, true)]
Where does the debugger get the symbols?
Most Windows debuggers work like this:
- The folder containing the binary is searched for a matching PDB.
- The debugger’s symbol search path is then searched. Most Windows debuggers (including Visual Studio) take their default search path from the
_NT_SYMBOL_PATH
environment variable. - How can I tell where the debugger is getting its symbols?
In WinDbg/CDB, the !sym noisy
command can be used to show verbose output when the debugger is trying to resolve symbols. Visual Studio 2005 shows the symbol path as a column in the Modules window.
I am not aware of a simple way to identify where Visual Studio 2003 is loading its symbols from. The easiest way might be to use the handle utility from Sysinternals
C:\src> handle foo.pdb
Handle v3.01
Copyright (C) 1997-2005 Mark Russinovich
Sysinternals - www.sysinternals.com
devenv.exe pid: 4832 C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temp
orary ASP.NET Files\someapp\dbf846f9\1ab0397c\assembly\dl2\c33a0bcb\eca89e48_3
b78c601\foo.PDB:
In this case ASP.NET has copied the binary and the PDB to a temporary location. This is another way a PDB can wind up being mismatched.
How are PDB’s matched to binaries?
The short version: the PDB contains a date stamp and a checksum which is matched against the binaries.
How can I tell if a PDB matches a binary?
The symchk
utility that comes with the Debugging Tools for Windows can be used to do this. There are many options for this utility, so you should read the output of symchk /?
.