Generating and Deploying Debug Symbols with Microsoft Visual C++ 6.0
Summary: This article discusses the process of generating debug symbols in order to locate problems in your application. (7 printed pages)
Generating debug symbols can be a confusing topic. But having debug symbols can make a huge difference when attempting to locate a problem in your application, so it is worth taking a little time to understand the basics.
To begin with, application and Microsoft® Windows NT® debug symbols are essential for getting a call stack with Dr. Watson, Visual C++, or another debugger. When the Microsoft Visual C++® compiler optimizes code, stack frames can be omitted in certain cases. When a stack frame is omitted, the debugger cannot figure out where the local variables for the current function end and the information for the next function begins. A stack frame is a contiguous area on the process stack used to store the local variables of a function. The EBP register is used to point to the beginning of the stack frame and is sometimes called a frame pointer. The debug symbol files contain frame pointer omission (FPO) records that give enough information to find the next function information on the stack without using frame pointers.
The following call stacks demonstrate the advantage to having debug symbols when Dr. Watson creates a log of an application crash: Your call stack will have readable function names.
Call stack without symbols:
0012FF78 0040101D 0012FFC0 004010D3 00000001 00300ED0 !<nosymbols> 0012FF80 004010D3 00000001 00300ED0 00300E40 0012F640 !<nosymbols> 0012FFC0 77E9BC52 0012F640 0012F88F 7FFDF000 C0000005 !<nosymbols> 0012FFF0 00000000 0040101F 00000000 000000C8 00000100 kernel32!BaseProcessStart (FPO: Non-FPO [1,8,3])
Call stack with symbols:
0012FF78 0040103D 0012FFC0 004010FA 00000001 00300ED0 !myfunction 0012FF80 004010FA 00000001 00300ED0 00300E40 0012F640 !main 0012FFC0 77E9BC52 0012F640 0012F88F 7FFDF000 C0000005 !mainCRTStartup 0012FFF0 00000000 00401046 00000000 000000C8 00000100 kernel32!BaseProcessStart (FPO: Non-FPO [1,8,3])
There are three types of debug symbols that Visual C++ can generate:
- Common Object File Format (COFF)
- CodeView (CV)
- Program Database (PDB)
Debug symbols contain several levels of granularity:
- FPO records are explained above.
- Function names are the names and addresses of functions.
- Line numbers are the addresses associated with the lines in the C/C++ source files.
- Variables are the global, static, and local variables.
- EnC is the debug information required to support the Edit and Continue feature (new in Visual C++ 6.0) that allows you to make code modifications during a debug session without stopping to rebuild.
Common Object File Format (COFF) is a non-Microsoft format for debug information. With Visual C++ 6.0, you get only FPO records and function names.
CodeView (CV) is a Microsoft format for debug information. It is embedded in the object file or executable. CodeView symbols cannot support Edit and Continue. The CV format supports FPO records, line numbers, and variables. More information on the CodeView format can be found in the MSDN Library.
Program Database (PDB) is a Microsoft format for debug information. It cannot be embedded in the object file or executable. PDB symbols can support Edit and Continue. The PDB format supports FPO records, line numbers, and variables.
Edit and Continue support is unsuitable for Release configurations. It is used for applying code changes to an image during a debugging session (so you don't have to stop the debugging session to rebuild). Edit and Continue support requires extra calls and extra space in the image. In a Release configuration, you are looking for application performance over ease of development, so the Edit and Continue support is undesirable.
Table 1. The Three Types of Debug Symbols Generated by Visual C++
|COFF||/Z7 or /Zi||/debugtype:coff||Yes||Yes||No||No||No|
|PDB||/Zi or /ZI||/debugtype:cv||Yes||Yes||Yes||Yes||Yes (/ZI)|
Debug information can be used to uncover details about the structure of your application. For example, if you give someone debug symbols with the Variables granularity, all of your function names, source file names, and variable names will be exposed. A serious engineer can use this to "reverse engineer" your application. So, if this is a concern, you will want to take this into account when choosing which type of debug symbols to generate.
In choosing which type of debug symbols to generate, you must determine the purpose for the symbols. For instance, if all you want is a Dr. Watson log with symbol names, COFF symbols are sufficient. If you want to use it for internal testing, you'll need CodeView or PDB symbols to enhance the amount of information you will have for debugging.
Table 2 offers some suggestions to help you decide on the type of symbols to generate.
Table 2. Choosing Which Type of Debug Symbols to Generate
|Internal testing||PDB or CV||Variables|
|Secure customer||PDB or CV||Variables or Line Numbers|
|Non-secure customer||COFF||Function Names|
A secure customer is one who works closely with you. You might have them sign a non-disclosure agreement. They are privy to some of the details regarding the structure of your application.
A non-secure customer includes everyone else. They might work with your competitors, or they might be your competitors.
The trade-offs are:
- Space. More granularity requires more space.
- Security. More granularity reveals more about your application.
- Utility. More granularity means better information about the problem encountered.
You will also need to consider the debug tools you will be using to analyze problems in the process. If you want Dr. Watson to be able to read your symbols without requiring your users to install other software, you must include COFF symbols. If you also install the WinDbg debugger (available at http://msdn.microsoft.com/downloads/default.asp?URL=/code/topic.asp?URL=/MSDN-FILES/028/000/015/topic.xml), both Dr. Watson and WinDbg will be able to read PDB symbols. Of course, the Visual C++ debugger can read all three types of debug information.
You can generate both COFF symbols and PDB symbols without deploying the PDB symbols. Dr. Watson can use the COFF symbols despite the absent .pdb file. However, other debuggers, such as Visual C++ 6.0, might not ignore the absent .pdb file and report that no matching symbolic information was found despite the presence of the COFF symbols.
Generating symbols is as easy as changing a few project settings. By default, the Visual C++ integrated development environment (IDE) does not set up your Release configuration to generate debug symbols. However, this does not prevent you from generating symbols for this configuration.
If you are using the Microsoft Developer Studio® Visual C++ environment:
- Open your workspace in Visual C++.
- Open the Project Settings dialog box.
- Select the Release configuration for your project(s).
- Click the C/C++ tab.
- Click General from the Category drop-down list.
- Click Compiler Debug Type (see Table 3) from the Debug info drop-down list.
Note You do not need to disable any optimizations.
- Click the Link tab.
- Click Debug from the Category drop-down list.
- Select the Debug info check box.
- Select the Linker Debug Type (see Table 3) check box.
- Clear the Separate types check box.
- In the Project Options dialog box, scroll to the bottom and type /opt:ref,icf.
- Rebuild the Release configuration.
Table 3. Compiler and Linker Debug Types
|Symbol type||Compiler debug type||Linker debug type|
|COFF||"C7 Compatible" or "Program Database"||COFF format|
|CodeView||C7 Compatible||Microsoft format|
|CodeView and COFF||C7 Compatible||Both formats|
|PDB||Program Database||Microsoft format|
|PDB and COFF||Program Database||Both formats|
If you are using a makefile or command line:
- Add the compiler switch (based on the Table 4 information).
- Add the link switches (based on the Table 5 information).
Table 4. Compiler Switches
|Symbol type||Compiler switch|
|COFF||/Z7 or /Zi|
|PDB and COFF||/Zi|
Table 5. Link Switches
|Symbol type||/pdb Link switch||/debugtype Link switch|
|CodeView and COFF||/pdb:none||/debugtype:both|
|PDB and COFF||/pdb:"filename"||/debugtype:both|
* /debug is required to generate any type of debug information.
* Generating debug information turns off the default linker optimizations, so you must also add /opt:ref,icf if you want the equivalent of the default linker optimizations.
The side effects of generating debug symbols are minimal; however, image size, optimizations, and debugging facility are impacted.
The size of an image can be increased by a few bytes to a few megabytes, depending on the type of symbols you generate. The program executable image (DLL, EXE, etc.) has some information stored in a header at the beginning of the file that describes what is in the image. If you generate debug information, there will be one or more debug directories in that image header. Each debug directory is relatively small (usually less than 20 bytes). For PDB information, your image is larger by about the size of the path name to the .pdb file you supplied with the /pdb linker switch. This is stored in one debug directory. COFF and CodeView information is embedded in the image. So, your image will be larger by the size of the debug information. The increase depends on the granularity of the debug information and the amount of source code needed to create the image. You will also get several debug directories that point to various parts of the debug information.
Generating debug information does not affect compiler optimizations. The linker turns off some default optimizations if you generate debug information. Simply add the optimization switches as just described to enable the optimizations. Generating debug information has no other effect on linker optimizations.
Generating debug information gives you increased information to track down bugs. I find that the more information I have during a debugging session, the easier it is to pinpoint a problem.
The first thing you have to do is determine whether you should deploy your debug symbols. In general, you should deploy symbols during most of your internal testing. This will give you better information to track down bugs. But before you deploy debug symbols to your customers, you should consider whether you want them to have access to that level of information about your application and whether they have the extra storage space. You also need to consider the type of customer you are targeting. Many software developers like to have debug symbols. Many end users would rather not use up their hard drive space for something they are unlikely to use.
For instance, Windows NT deploys debug symbols with the operating system. These symbols contain primarily function-level symbols and FPO records. Microsoft Office does not deploy debug symbols.
You only need to deploy a symbol file if you build PDB debug information. COFF and CodeView debug information is embedded in the executable image. You must deploy your PDB symbols to either the directory with the image or the directory where the symbols were originally built if you want to use the Visual C++ debugger. For example, if you built your symbols into d:\projects\myapp\release\myapp.pdb on your development machine and deployed your application to another computer to c:\program files\my company\my app, you must deploy your PDB symbols to either c:\program files\my company\my app or d:\projects\myapp\release on the new computer. The Windows NT symbols are located in the \winnt\symbols directory. Visual C++ will not look there for PDB symbols that you generate.
Visual C++ will look in the following locations (in this order) for DBG symbols. (You cannot build DBG symbols with Visual C++.) Visual C++ will not look in these locations for PDB symbols:
Deploying debug symbols does not affect the performance or initialization of your application, but does impact available hard drive space. Debug symbols can take up some significant hard drive space, depending on the granularity.
Deploying the Windows NT 4.0 and Windows 2000 symbols can be a little confusing. First, do not use the Windows NT Symbols Setup in the Visual C++ 6.0 Tools program group. This program is obsolete and does not work with the later Windows NT service packs or with Windows 2000. You will need about 500 MB of free space on the drive containing your Windows directory.
First, install Windows NT 4.0 symbols.
- Insert the Windows NT 4.0 CD into your CD-ROM drive.
- Open a command prompt and type the following command:
H:\Support\Debug\Expandsym.cmd H: C:\Winnt, where H: is the driver letter of your CD-ROM drive and C:\Winnt is the path to your Windows directory.
Next, install the symbols from the previous Windows NT service pack you installed.
- Use Task Manager Help->About to get the current Windows NT service pack version number.
- Insert your Windows NT 4.0 service pack (4 or later) CD into your CD-ROM drive.
- Start H:\Support\Debug\Sp5symi.exe, where H: is your CD-ROM drive.
- When prompted for the directory, type in the path to your Windows directory (for example, C:\Winnt).
The Windows 2000 CD does not contain the debug symbols. You will need to obtain the Windows 2000 Customer Support and Diagnostic Tools CD. This CD ships with the server variations of Windows 2000 but not with Windows 2000 Professional.
To install the Windows 2000 debug symbols
- Start H:\Symbols\I386\Retail\SymbolsX.exe, where H: is your CD-ROM drive.
- When prompted for the directory, type in the path to your symbols directory (for example, C:\Winnt\Symbols).
Unless you are using Windows 2000, you will most likely need to deploy the Visual C++ run-time components. The Visual C++ run-time components are system components under Windows 2000, which you do not redistribute. Windows 2000 supplies debug symbols for these components on the Windows 2000 Customer Support and Diagnostic Tools CD.
For Microsoft's other Win32® operating systems, in most cases you will be deploying Visual C++ run-time components, such as msvcrt.dll and mfc42.dll. In cases where you deploy debug symbols for your application, you should also deploy the debug symbols for the Visual C++ run-time components you are redistributing. The debug symbols for the Visual C++ run-time components are located in the H:\vc98\Debug directory on both the Visual C++ 6.0 and Visual Studio® 6.0 sp3 CDs. Copy the .pdb files that match the Visual C++ run-time components you are redistributing into the target system directory. For instance, if you are installing msvcrt.dll into C:\Win98\system, you will need to copy msvcrt.pdb into C:\Win98\system.