|
Updated: 14.07.2005
Introduction
Module matching
Module search path
What if we do not have matching modules
ModuleRescue
When we debug an application, the debugger has to load symbols for executable modules in order to be able
to show meaningful call stacks, current source line, values of variables, and so on. If you have ever had
to debug a minidump created on another system, you already know that in addition to symbols the debugger
needs access to the same versions of modules that were loaded by the application at the time the dump was
created. If the debugger cannot find the exact same version of a module (that is, matching module),
it cannot load symbols for the module, severely limiting the possibility of successful debugging.
In this article we will discuss the rules used by VS.NET and WinDbg debuggers to identify and locate
matching modules. We will see how to tell debuggers where to look for matching modules. And we will
also discuss the situation when matching modules cannot be found, and try to find workarounds.
How do debuggers decide whether a module is matched or not? How do they know what version of the module
is needed? Let’s look inside the minidump. Every minidump contains the list of modules loaded by
the dumped process. This list is stored as an array of MINIDUMP_MODULE structures (described in
DbgHelp documentation). This structure is declared as the following:
typedef struct _MINIDUMP_MODULE {
ULONG64 BaseOfImage;
ULONG32 SizeOfImage;
ULONG32 CheckSum;
ULONG32 TimeDateStamp;
RVA ModuleNameRva;
VS_FIXEDFILEINFO VersionInfo;
MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
ULONG64 Reserved0; // Reserved for future use.
ULONG64 Reserved1; // Reserved for future use.
} MINIDUMP_MODULE, *PMINIDUMP_MODULE;
The following table describes the members of the structure:
| Member |
Description |
| BaseOfImage |
Base address of the module in memory.
|
| SizeOfImage |
Size of the module (in bytes) equal to the value stored in the module’s optional header
(IMAGE_OPTIONAL_HEADER.SizeOfImage).
|
| CheckSum |
Checksum of the module, equal to the value stored in the module’s optional header
(IMAGE_OPTIONAL_HEADER.CheckSum). This value can be 0, unless the module was linked
with /release option.
|
| TimeDateStamp |
Time/date stamp of the module, equal to the value in the module’s file header (IMAGE_FILE_HEADER.TimeDateStamp).
|
| ModuleNameRva |
This is a relative address (from the beginning of the minidump) of MINIDUMP_STRING structure that contains
the file name (and path) of the module.
|
| VersionInfo |
This structure contains the version information of the module (including file version and product version).
More information about the contents of this structure can be found in Platform SDK documentation.
|
| CvRecord |
This is a structure that contains the so-called debug record. You can find more information about its contents
in this article (see CV_INFO_PDB20 and CV_INFO_PDB70 structures).
This structure is present only if debug information for the module is stored in .PDB file.
|
| MiscRecord |
This structure also contains the debug record, but in another format (IMAGE_DEBUG_MISC). This structure
is present only if debug information for the module is stored in .DBG file.
|
If you want to display module information for a minidump, you can use MiniDumpView tool.
For example, here is the module information for kernel32.dll from a minidump created on Windows 2000 SP4:
Module: C:\WINNT\system32\KERNEL32.DLL
Address: 7c4e0000
Size: 000b9000
CheckSum: 000bdbb3
TimeDateStamp: 3ef274dc
File version: 5.0.2195.6688
Product version: 5.0.2195.6688
MISC record: Available
As we see, minidumps allow to identify modules very precisely. We can determine the file and
product version of the module, its location in the file system, and have enough information
to load the matching debug information file.
But do debuggers really use all this information to identify modules? It turns out they don’t.
VS.NET debugger uses only the following data to check if a module is matched:
- File name (MINIDUMP_MODULE.ModuleNameRva)
- Module size (MINIDUMP_MODULE.SizeOfImage)
- Module timestamp (MINIDUMP_MODULE.TimeDateStamp)
WinDbg is a bit more precise, since it also uses the module’s checksum. Here is the list of data
used by WinDbg to check if a module is matched:
- File name (MINIDUMP_MODULE.ModuleNameRva)
- Module size (MINIDUMP_MODULE.SizeOfImage)
- Module timestamp (MINIDUMP_MODULE.TimeDateStamp)
- Module checksum (MINIDUMP_MODULE.CheckSum)
It is interesting to see that version information of the module (MINIDUMP_MODULE.VersionInfo)
is not taken into account at all.
VS.NET debugger
Where do debuggers look for matching modules? They search through a predefined set of directories.
Module search path of VS.NET debugger is documented here.
It worth to note that there are two most important locations checked by the debugger:
- The directory where the minidump is stored (and some of its subdirectories)
- The directory where the module was located on the system where the dump was created
If we want to specify additional directories where to search for modules, we can do it.
There are two possible ways – in Registry and in project settings. The following Registry
setting can be used to specify the directories system-wide:
HKLM\Software\Microsoft\VisualStudio\7.1\NativeDE\Dumps
GlobalModPath = REG_SZ
This setting can contain the list of directories, separated with semicolons. A similar setting allows
to specify the module search path for a user.
In project settings, we have to enter the list of directories into the following setting:
Project properties | Configuration Properties | Debugging | Command Arguments
The list of directories should be prefixed by “MODPATH=”. For example:
MODPATH=c:\modules\testapp;d:\bin\testapp
If both ways to specify the module search path are used at the same time (Registry and project settings),
the resulting module search path is the combination of the two. The directories in project settings
are checked first.
WinDbg
WinDbg has more natural ways to specify the module search path - .exepath command, -i command line option,
and _NT_EXECUTABLE_IMAGE_PATH environment variable. I don’t remember what path is used by WinDbg by default,
but it can be easily checked with the help of the following commands:
!sym noisy
.reload /f yourmodule.dll
It is not difficult to ensure that all modules built by ourselves are stored in a safe location
and can be found when we need to debug a crash dump. But what about system DLLs? How can we ensure
that we can find the same versions of system DLLs as installed on the customer’s system?
Especially taking into account such thing as hotfixes?
The simplest way to ensure that is to produce minidumps with full memory contents (use MiniDumpWithFullMemory
option if you use MiniDumpWriteDump function, use “.dump /ma” with NTSD debugger, or check “Full” option for
“Crash Dump Type” setting in Dr. Watson configuration settings (this setting exists only since Windows XP,
older systems always produce full crash dumps)). If complete contents of a module are stored in the minidump,
the debugger – in theory – does not have to look for the matching module somewhere else. In practice, only
WinDbg seems to be able to utilize the module’s contents in the minidump. VS.NET debugger can sometimes
load the module from the minidump only, but in most cases it refuses to do that and insists on loading
the matching module from disk (and if the matching module cannot be found, it refuses to load symbols).
Of course, minidumps with full memory contents have a serious disadvantage – they are huge. If we cannot
afford ourselves to transfer many megabytes of minidump data from customers to developers, we have to look
for better options. Fortunately, a simple solution exists for Windows XP and Windows Server 2003 operating
systems – their modules are available on the symbol server, together with symbols, and debuggers
(with symbol server support) can download them.
How should we configure debuggers to download modules from the symbol server? WinDbg does not need any
additional steps, if the path to the symbol server is specified in _NT_SYMBOL_PATH environment variable
or using .sympath/.symfix commands.
VS.NET debugger cannot utilize _NT_SYMBOL_PATH or symbol path settings for module downloads. Instead,
we have to use Registry or project settings that specify the module search path. Though it is possible
(and works fine) to specify the path to the symbol server in Command Arguments | MODPATH setting,
I find it more convenient to use the system-wide Registry setting:
HKLM\Software\Microsoft\VisualStudio\7.1\NativeDE\Dumps
GlobalModPath = "srv*c:\symbols*http://msdl.microsoft.com/download/symbols"
One more step is recommended to ensure reliable module downloads for VS.NET – upgrade symsrv.dll.
The latest version of this small DLL, which is responsible for symbol server access, can be obtained
with Debugging Tools for Windows.
We should copy symsrv.dll from the installation directory of Debugging Tools into <VSInstallDir>\Common7\IDE
directory, overwriting the older version of the DLL. The process of upgrading symsrv.dll is described
in KB319037.
Unfortunately, system DLLs of older operating systems (Windows 2000 and older) are not available
on the symbol server. It looks like debuggers are almost helpless in this situation. Even though
symbols for system DLLs are on the symbol server, debuggers cannot download them because they
cannot find matching modules. WinDbg offers a little help by looking for any unmatched .DBG and .PDB
file on the symbol path, but it is not too helpful anyway. VS.NET debugger cannot do even that.
Fortunately, it looks like information in the minidump is often enough to manually generate
a module that will allow debuggers to download symbols from the symbol server even if the real
matching module cannot be found. ModuleRescue tool does exactly that – it analyses the minidump
and generates (partially) dummy modules, which can be recognized by debuggers as matched,
and allows them to proceed with downloading symbols. (Generated modules must be on the module
search path, of course). After debuggers have downloaded symbols from the symbol server, they
should not have problems with displaying good call stacks. You can find more information about
ModuleRescue here.
|