Are you in GC?

When you're debugging, you're really just looking into internal memory structures in a process. When it comes to debugging an ASP.NET application, you are oftentimes dumping out method tables, objects, etc. The ability to do so requires a known address. That's no problem most of the time. However, if you're in a GC when a dump is taken, we may be in the process of moving some of those structures around for compaction. In that case, you may see what looks like managed heap corruption when in fact you're seeing the result of the compaction phase of GC. For that reason, it's often convenient to know if you're in the process of GC when you crack open a dump. To do so, you can check GcHeap::GcInProgress.

First thing you'll want to do is make sure that you have a good symbol path. You can check your symbol path by running the .sympath command in Windbg as so:

0:000> .sympath
Symbol search path is: srv*c:\\symbols*https://msdl.microsoft.com/download/symbols

You can set your symbol path inWindbg using the same command like so:

.sympath srv*c:\\symbols*https://msdl.microsoft.com/download/symbols

What this command does is set up a local symbol server in the c:\symbols directory. When Windbg needs symbols for a particular module, it checks the c:\symbols directory for those symbols. If they're there, great. If not, it downloads the symbols from the Microsoft public symbol server (https://msdl.microsoft.com/download/symbols) and stores them in the c:\symbols folder. Note that it can take a bit of time for symbols to download.

Now you'll need to know whether you are running workstation or server GC. In most cases, this is determined by whether you are running a multi-proc box. (Hyperthreaded boxes do count as multi-proc.) A multi-proc box will usually be running server GC (mscorsvr.dll) and a single-proc box will usually be running workstation GC (mscorwks.dll). You can double-check to see which you have loaded using the lmv command in Windbg.

0:000> lmv mmscorsvr
start end module name
791b0000 79419000 mscorsvr (deferred)

Image path: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorsvr.dll
Image name: mscorsvr.dll
Timestamp: Fri Feb 18 14:58:55 2005 (4216570F)
CheckSum: 0026A38C
ImageSize: 00269000
File version: 1.1.4322.2300
Product version: 1.1.4322.2300
File flags: 20 (Mask 3F) Special
File OS: 4 Unknown Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04e4
CompanyName: Microsoft Corporation
ProductName: Microsoft .NET Framework
InternalName: MSCORSVR.DLL
OriginalFilename: mscorsvr.dll
ProductVersion: 1.1.4322.2300
FileVersion: 1.1.4322.2300
FileDescription: Microsoft .NET Runtime Common Language Runtime - Server
LegalCopyright: Copyright © Microsoft Corporation 1998-2002. All rights reserved.
LegalTrademarks: Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation
Comments: Microsoft .NET Runtime Common Language Runtime - Server

Notice that the command is lmv m <module_name> . In other words, don't forget the m in front of the module name. (You can also put the 'm' immediately after the 'v' followed by a space and the module name. My muscle memory simply types it the other way.) In the case above, it's clear that I'm running server GC (mscorsvr.dll). Now you're ready to check for GC.

To check for the status of GC, you will use the dd command in the debugger. The dd command allows you to display double-word values. The format for the dd command is dd <address> and in this case, you want to dump out the address of mscorsvr!GCHeap::GcInProgress.

0:000> dd mscorsvr!GCHeap::GcInProgress
793e3218 00000000 00000004 00000028 00000002
793e3228 00000003 00000002 0000000f 00000001
793e3238 00000000 00000001 00000001 00000002
793e3248 7c821ad8 01a31e90 ffffffff 00f0ba0f
793e3258 792f2e08 00000000 00000000 792543ac
793e3268 00000000 00000000 7921d1fd 00000000
793e3278 00000000 792543c4 00000000 00000000
793e3288 791b4151 00000000 00000000 792f3263

Note that dd dumps out 32 DWORDs by default. You are actually interested in the first DWORD here, so you can clean up the output a bit by using a different command:

0:000> dd mscorsvr!GCHeap::GcInProgress L1
793e3218 00000000

Now we have the one DWORD we're interested in and we can clearly see that the value is 0. Therefore, we know that we are not currently in GC.

One note: If you were using workstation GC, you will need to replace mscorsvr with mscorwks in these commands.

Have fun!

Jim