Using WinDbg for the First Time
WinDbg as a Last Resort
Most .NET developers are really spoiled as far as debugging goes. Most of the time we just start the application from Visual Studio and the debugger is already attached to it and ready to break the execution at breakpoints or when an exception is raised. If that's not possible, Visual Studio debugger can easily be attached to an already running process either on the local machine or a remote machine with Remote Tools installed. It works even when the application is hosted in Azure.
However, none of the above options can be used when an application starts misbehaving in production (slow response times, seemingly random and non-reproducible exceptions or application crashes, etc.). When logging and instrumentation are not enough to resolve the problem, it's time to create a memory dump and analyze it in WinDbg. Thanks to its steep learning curve, using it for the first time is quite a scary thought. And since most of us only need to use it on rare occasions, just enough time passes between them, that it's not much easier doing it the next time after such a pause. I've created this short cheat sheet to make it a bit easier for me, when I'm in such a situation again. I hope it will prove helpful to others as well.
Creating a Memory Dump
Although it is possible to create a memory dump from Task Manager by right clicking the process of interest and selecting the appropriate option, this is not the recommended way of doing it. Not only does it provide only basic functionality, it doesn't even create a useful memory dump for 32-bit processes running in 64-bit Windows.
Your best choice for creating memory dumps is ProcDump from the Windows Sysinternals suite. Just like other tools from the suite it can be just copied to the machine and run without installing, making it even more suitable for use in production environment. To create a full memory dump of a specific process at that point in time, you should call:
procdump -ma <pid> <filename>
Of course, you should replace
<pid> with the process ID value of your process (available in Task Manager) and
<filename> with the desired name for the generated memory dump file (it's best to use
.dmp extension). The tool supports many command line options to tailor it to your needs. For example, the following call will create a memory dump when the process crashes:
procdump -t -ma <pid> <filename>
In spite of being the least invasive way for obtaining run-time information for debugging, depending on the size of the process, creating the dump can still take several seconds. During this time the process is paused, so take this into consideration when using it in production. Of course, for processes consuming a lot of memory the generated file will also be quite large, therefore plan appropriately where to save it to and how to transfer it to your development machine, where you will be able to analyze it.
To analyze memory dumps you will need to install WinDbg on your development machine. It is a part of Windows SDK. Assuming you already have Visual Studio installed, there's no need to download and install it in its entirety; it's enough to only select Debugging Tools for Windows in the dialog:
In case you haven't paid attention at the very beginning of the setup wizard, WinDbg gets installed into
C:\Program Files (x86)\Windows Kits\8.1\; in
Debuggers subfolder, to be exact.
Analyzing the Dump
Once you start the correct version of WinDbg (either
x64\windbg.exe, based on whether you want to analyze a memory dump of 32-bit or a 64-bit process respectively), the first step is to load the memory dump (File > Open Crash Dump... or Ctrl+D). From here on, you'll need to proceed by typing commands.
To make any sense of it, you'll need to set the symbol path:
This will automatically set it to Microsoft's symbol store and at the same time create a local cache for the symbols at the given path. If you need to troubleshoot the loading of symbols, you can enable verbose output before calling the above command:
Since you're not always debugging the same version of CLR that you have on your machine, you should do a verbose reload of debug modules which will report any missing files:
.cordll -ve -u -l
If it complains about missing
SOS.dll, you will need to retrieve them from the machine where the memory dump was created and repeat the above command.
The next issue are usually missing symbols (
.pdb files) for your own assemblies. You'll either need to include a folder containing them to symbol path or copy them directly to the cache folder, as reported by the above command.
Now you're finally ready to start doing the analysis. Depending on the type of the issue, you'll want to start with one of the following commands:
To analyze crashes, output the details about the current exception:
To analyze performance issues, take a look at the current state of all managed threads:
To analyze memory leaks, you should explore the heap: