Debug symbols are one of the key elements of an effective debugging session. Debug symbols keep a mapping of the source code to the generated EXE or DLL binary; this allows the debugger to set breakpoints at source code locations and display rich debugging information when a breakpoint is hit. Debug symbols are stored in program database (PDB) files, and these are generated in Visual C++ 2008 for both debug and release by default. The project setting for debug file generation is located in the Linker | Debugging section, as shown in Figure 1.
Figure 1: Controlling Debug File Generation
For most small-team development, ensuring that all the projects that make up a solution are generating debug information is typically all that is required to ensure a good debugging experience. As the size of the development team grows and as bugs become more interesting, better debugging symbol support is required. For large teams, having all the projects required to build an application within the same solution will eventually become too time consuming from a build perspective. For DLLs that change infrequently and for DLL dependencies built by other sub-teams, excluding the project for these DLLs from the solution makes sense.
The problem with excluding DLLs from a solution occurs when a breakpoint needs to be set in the external project or when the call stack contains significant calls from the binary dependency. Unless the PDB file also has been copied over with the DLL, breakpoints from functions from within the DLL are difficult to set and the call stack will be missing the function names. While manually building the required DLL to generate the PDB file is not usually an overly difficult task, this process can become tedious when it needs to be repeated frequently.
The solution to this problem is to locate all the PDB files from common DLLs onto a network share and get Visual Studio to download automatically and cache the PDBs. Prior to Visual Studio 2005, this was a difficult exercise that involved using the Microsoft Symbol Server DLL (SymSrv.dll) from the Debugging Tools for Windows toolkit and setting up some Windows environmental variables (Knowledge Base Article 311503 details the procedure). Starting with Visual Studio 2005 and continuing with Visual Studio 2008, the download of debug symbols from an intranet server can be achieved easily by using the Visual Studio settings shown in Figure 2.
Figure 2: Download Debug Symbols from an Intranet Server
The dialog allows one or more PDB locations to be specified, and optionally allows a path to be provided where the debug symbols can be cached locally. As with a local PDB file, Visual Studio and the symbol server will only download and cache a PDB if it matches the DLL that has been loaded by the process. Locally cached files are stored with a check-sum in the folder path, so multiple PDB files that correspond to many different builds of a binary can be stored locally at the same time.
PDB files store the full path of the source code files that were used to build the binary, and if the files are in a different location on the machine where the binary is being debugged, Visual Studio will display a prompt asking for the location of the source code file. It is generally a good idea to ensure that all the developers within a team are using the same folder and drive structure to store the source code files that they pull down from the source repository to avoid path mismatches and a number of other similar issues.
Even with the best development environment setup, it is possible that partway through a debugging session, it will become apparent that the debug symbols for a particular DLL haven’t been loaded. If it is simple to reproduce the application state in the debug session, uploading the PDB file to the symbol server and restarting the debugger will generally be the best option. However, if reaching the same point in the debugging session would take a fair amount of time and effort, it is possible to manually load the PDB file from the Modules window, as shown in Figure 3.
Figure 3: Manually Loading Debug Symbols