Saturday, March 25, 2006

Finding/counting instances of (leaked) C++ classes in crash dumps using WinDbg.

When dealing with memory leaks you have an armoury of stuff at your disposal: debugging versions of memory allocation routines, leak tracking tools from Microsoft (see DebugDiag and UMDH) and others.

An additional technique that I've used with some success (when for various reasons the above techniques are not immediately available) on full crash dumps is to go hunting for instances of C++ classes that I suspect may be being leaked.

Note that this technique only works for classes that contain virtual functions, and you'll need your symbols to be lined up properly.

Consider how a C++ class instance with (virtual functions) is typically laid out in memory: it starts with a pointer to a virtual function table, followed by the member data of the class. The values stored in the member data for different instances of the same class will vary, but the pointer to the virtual function table will be invariant.

That's nice: it means that if we know the whereabouts in memory of the virtual function table for a particular class, we can go hunting in memory for instances of that class. We can find out whereabouts in memory the virtual function table is by asking WinDbg. The X command is what we need here. Let's say we want to find the address of the virtual function table for class CMyClass in module mylibrary.dll. We can ask WinDbg to tell us by executing the command

x mylibrary!CMyClass::*vftable*

The * characters above are wildcards - I find it faster to type * than the more correct ` (backtick) character and it's also useful to know if there's more than one virtual function table associated with the class.

WindDbg will list the addresses of all the virtual function tables in the CMyClass class - there may be more than one if you are dealing with multiple inheritance and/or nested classes. For now, let's assume that there's only one. You should see something like this:

0123abc4 mylibrary!CMyClass::`vftable' =

Thus we know that we need to look for the address 0123abc4. We also need to know where to look in memory. Microsoft's DebugDiag tool is handy here: running it's memory analysis script over your dump will let you know which heaps belong to which modules, where their segments are located in memory and other interesting things.
The WinDbg !heap metacommand is also useful for listing the heap segments, but it won't tell you which heap belongs to which module.

Armed with this heap segment addresses and a virtual function table address, you can go hunting for instances of your class in the appropriate segment(s) in memory.
The WinDbg s (for search) command is what we need here - and we also need to have a WinDbg log file open before we start in case there's a lot to be found. Say we have discovered that mylibrary has a nice big heap segment from 0d000000 to 0d400000, we can search it for CMyClass instances with the command

s -d
0d000000 0d400000 0123abc4


Of course, it helps if you have a reasonable idea of the number of CMyClass instances to expect at this stage of the game, so you can tell if you find too many. Note also that if you do have a leak, its possible (likely, even) that the leaked instances will be splattered across multiple heap segments, so you'll want to search in all the heap segments associated with your module.

I often use a perl script to convert the output of the !heap command into a little batch file of commands to feed into WinDbg via the $$< command.

One other useful thing: often you may not know which class (or classes) are being leaked. You might get lucky with the information that DebugDiag provides about sizes of memory allocations. DebugDiag will tell you the most frequently occurring allocation sizes, and the sizes of allocations associated with the greatest memory consumption. You can use the C++ sizeof(...) expression in WinDbg with the C++ expression evaluator thus:

?? sizeof(mylibrary!CMyClass)

to see if the size of instances of CMyClass agrees with what DebugDiag tells you about the most frequently allocated size. A better thing to do is to ask WinDbg to tell you the names of ALL the classes in your module, then run sizeof on each of them.

x mymodule::*vftable*

is your friend here if you have more classes to deal with than you can comfortably enumerate by hand. Note that won't be all the C++ classes, just those with virtual functions). I use a perl script to produce a list of class name/size of instance pairs sorted by size to give a good list of candidate class types to start hunting for.

As I said originally, I've had some success with this "hunt the vftable" technique. Your mileage may vary. Happy hunting!

2 Comments:

Anonymous Anonymous said...

This is interesting stuff...Will it work when the code is completely optimized? I.e. Production code?

8:57 pm  
Blogger Dan said...

Yep. As long as you have symbols (or some other way of deducing the address of the virtual function table) and you're looking for instances of classes that have a virtual function table this technique works.

I've used this technique (successfully) to examine the heap contents of crash dumps taken from live systems running optimized release code.

9:56 pm  

Post a Comment

<< Home