WindDbg
WindDbg is part of the Debugging tools for Windows that can be installed from the Customer Support Diagnostics CD, the Platform SDK, the Windows Driver Kit, or the web. Download and install it. The Debugging Tools for Windows package is updated frequently. Updating your tools from the Web site assures that you have the most up to date debugging tools possible.Debugging managed code
Symbols
The first issue that we need to address in order to progress is that of symbols. If you want to get decent stack traces and dumps of variable values and so on out of the debugger then you need symbols for the modules that you’re debugging whether those modules are yours or someone else’s and whether those modules are managed or unmanaged (although there’s quite a lot more that you can do with managed code without symbols).
Symbols are typically either private symbols (include variable information), retail symbols (function information but not variable information) or export symbols (generally not so useful for debugging purposes). If you’re debugging with “export” symbols it’s not usually enough to actually work out what’s going on.
For your own code you’re responsible for building the symbol files (usually .PDB program databases) by setting the right flags on the compiler or choosing the right configuration in a project (this is true for VC++, VB6 and all the .NET languages). This will give you full private symbols.
When the debugger (and lots of other tools) want to find symbols they typically check the contents of an environment variable named _NT_SYMBOL_PATH which is used (just like PATH – i.e. a list of folder separated by semi-colons) to find symbol files. You can set _NT_SYMBOL_PATH prior to running WinDbg or you can use the debugger’s own symbol path settings to find symbol files. These settings are controlled via the File->Symbol File Path menu option or using the .sympath command.
For other people’s code you need to get symbols from them. For Microsoft code you’re in luck because the symbols are available from a public symbol server on the internet. In order to make use of the symbol server you should set your symbol path to be something like; SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols What this says is that the debugger should first check a local cache named C:\MyLocalSymbols and if symbols are not found there then go out to the Microsoft symbol server for symbols and, if found, download them and cache them in that folder. Note that symbols are downloaded based upon name, version and checksum information from the modules being debugged so the debugger usually knows if the symbols match the code properly. If you wanted to combine this with a local location for your own code then you’d use something like:C:\MyCodesSymbols; SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols so that the debugger would check your folder first and would then proceed to look in the cache and finally to the symbol server for symbols.
Examining managed code
SOS is a debugger extension DLL designed to aid in the debugging of managed programs. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis. Type "!help
- Press ALT+1 to open the WinDbg command window.
- Type .load SOS
\SOS.dll to load the extension. Amend the path as necessary; in this case the location of SOS.dll is in the SOS folder. - Use the !findtable command to initialize SOS with the table information for the runtime version you will be debugging.
- Type !threads to list the threads.
Displaying the managed call stack
Display the managed call stacks by typing ~*e !clrstack. This command is similar to the ~*k WinDbg command, but it applies to managed calls. WinDbg now displays the current enter stack pointer (ESP), the current enter instruction pointer (EIP), and the method signature for each frame with a managed thread. The !clrstack command displays only the managed threads, not any native call-stack information.
The !clrstack output does not show all of the call-stack frames. When you use the !dumpstack command, the output shows all of the stack frames, including the origin of the thread and the reference to the type of thread you are dealing with, which is a completion port thread. You must change to the appropriate thread before running the !dumpstack command. For example, to change to thread 1, execute ~1s.
Examining the assembly
If the source code is not available, you can examine the assembly by supplying the instruction pointer for the call-stack frame to the !u command. The instruction pointer can be retrieved from the !clrstack: output.
0096f970 03a00e06 [DEFAULT] [hasThis] VoidDebugging.Unexpected.btnSTA_Click(Object,Class System.EventArgs)
To disassemble this function, type !u 03a00e06.
Creating a Dump File Automatically
When an application error occurs, Windows can respond in several different ways, depending on the postmortem debugging settings. If these settings instruct a debugging tool to create a dump file, a user-mode memory dump file will be created.
The default postmortem setting is for a dump file to be written by Dr. Watson. If you wish to use WinDbg as the postmortem debugger, use the following command to automatically configure the registry:
windbg -I
Dr. Watson (drwtsn32.exe)
A tool used for automatically creating dump files and sending error reports to Microsoft Online Crash Analysis (OCA).
A full user-mode dump is the basic user-mode dump file.
This dump file includes the entire memory space of a process, the program's executable image itself, the handle table, and other information that will be useful to the debugger.
It is possible to "shrink" a full user-mode dump file into a minidump. Simply load the dump file into the debugger and then use the .dump (Create Dump File) command to save a new dump file in minidump format.
Note Despite their names, the largest "minidump" file actually contains more information than the full user-mode dump. For example, .dump /mf or .dump /ma will create a larger and more complete file than .dump /f.
In user mode, .dump /m[MiniOptions] is the best choice. The dump files created with this switch can vary in size from very small to very large. By specifying the proper MiniOptions you can control exactly what information is included.
A user-mode dump file that includes only selected parts of the memory associated with a process is called a minidump.
The size and contents of a minidump file varies depending on the program being dumped and the application doing the dumping. Sometimes, a minidump file is fairly large and includes the full memory and handle table. Other times, it is much smaller — for example, it might only contain information about a single thread, or only contain information about modules that are actually referenced in the stack.
The name "minidump" is misleading, because the largest minidump files actually contain more information than the "full" user-mode dump. For example, .dump /mf or .dump /ma will create a larger and more complete file than .dump /f. For this reason, .dump /m[MiniOptions] recommended over .dump /f for all user-mode dump file creation.
Noninvasive Debugging (User Mode)
If a user-mode application is already running, the debugger can debug it noninvasively. Noninvasive debugging does not present you with as broad a repertoire of debugging actions. However, it allows you to keep the debugger's interference with the target application to a minimum.
In noninvasive debugging, the debugger does not actually attach to the target application. The debugger will suspend all of the target's threads and will have access to its memory, registers, and other such information. However, the target cannot be controlled by the debugger, and thus commands such as g (Go) will not work.
Commands that are not allowed during noninvasive debugging will generate the message "The debugger is not attached, so process execution cannot be monitored."
When the debugging session is ended, the target application will be released and will then continue running. You should exit the session by using q (Quit), .detach (Detach from Process), or WinDbg's Debug Detach Debuggee or Debug Stop Debugging menu command. (Exiting by killing the debugger window or by using File Exit in WinDbg will generally freeze your target application.)
Noninvasive debugging is useful if you do not want to terminate the target at the end of the session, and the target is running on Windows NT or Windows 2000. (These operating systems do not support detaching from a target that the debugger has actually attached to.) It is also useful if the target application is completely frozen and cannot launch the break thread necessary for a true attach.
The call stack
The call stack is the chain of function calls which have led to the current location of the program counter. The top function on the call stack is the current function, the next function is the function which called the current function, and so forth.
The call stack also preserves a number of facts each time a function calls a new function. These vary from processor to processor, but typically they include the name or location of the function called, the return address (this is usually the assembly instruction just after the address from which the function was called), the parameters passed to the function, and the base pointer for the stack frame itself.
Many debugger commands have thread identifiers as their parameters.
A tilde ( ~ ) precedes the thread identifier, which can be one of the following:
Thread Identifier | Description |
---|---|
~. | The current thread. |
~# | The thread that caused the current exception or debug event. |
~* | All threads in the process. |
~Number | The thread whose ordinal is Number. |
~~TID] | The thread whose thread ID is TID. (The brackets are required, and there can be no space between the second tilde and the opening bracket.) |
Threads are assigned ordinals as they are created. Note that this is not the same as the Thread ID used by Windows.
When debugging begins, the current thread is the one which caused the present exception or debug event (or the active thread when the debugger attached to the process). That thread remains the current thread until a new one is specified by using a ~s (Set Current Thread) command, or by using the Processes and Threads window in WinDbg.
Thread identifiers usually appear as command prefixes. Note that not all wildcards are available in all commands using thread identifiers.