Code for Concinnity

Detecting memory leaks with WinDBG the modern (and free) way

This information is surprisingly scarce on the Internet. I should have realized earlier that the world has simply moved on to more modern languages than those who tax us to deal with mundane things like memory leaks.

Anyway, for those who’re still stuck, this should be your savior. This is almost as good as Valgrind on nix

Minimal example

#include <crtdbg.h>
void leak()
    ::operator new(1000);
    new double[1000];
int main()
    return 0;

Running the above code will get you something like this in the Debug output:

Detected memory leaks!
Dumping objects ->
{67} normal block at 0x00573FC8, 8000 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
{66} normal block at 0x005713E8, 1000 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

This is nice and good, but it isn’t very useful since we (pretend) don’t know where the leak came from!

And then WinDBG comes in

(Obviously, add WinDBG to your PATH first)

First, we’ll turn on heap stack tracing for our program. gflags.exe needs to be run with elevated privileges:

> gflags /i leak.exe +ust

Now we’re ready to go!

> windbg -g leak.exe

At the debug output in WinDBG, we’ll see our leak report:

Detected memory leaks!
Dumping objects ->
{80} normal block at 0x003B4378, 8000 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
{79} normal block at 0x003B3F50, 1000 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

At WinDBG, we can trace down the originating functions

000:0> !heap -p -a 0x003B4378

address 004b4378 found in
_HEAP @ 4b0000
  HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
    004b4340 03f0 0000  [00]   004b4358    01f64 - (busy)
    Trace: cfba8
    7753dd4d ntdll!RtlAllocateHeap+0x00000274
    6fa87743 MSVCR100D!_heap_alloc_base+0x00000053
    6fa95d8c MSVCR100D!_heap_alloc_dbg_impl+0x000001fc
    6fa95b2f MSVCR100D!_nh_malloc_dbg_impl+0x0000001f
    6fa95adc MSVCR100D!_nh_malloc_dbg+0x0000002c
    6fa9906b MSVCR100D!malloc+0x0000001b
    6fa871b1 MSVCR100D!operator new+0x00000011
    6fa872ef MSVCR100D!operator new[]+0x0000000f

    dc13d5 leak!leak+0x00000035 <<< ***

    dc1447 leak!main+0x00000037
    dc19df leak!__tmainCRTStartup+0x000001bf
    dc180f leak!mainCRTStartup+0x0000000f
    75f43677 kernel32!BaseThreadInitThunk+0x0000000e
    774f9d42 ntdll!__RtlUserThreadStart+0x00000070
    774f9d15 ntdll!_RtlUserThreadStart+0x0000001b 

There you go! The highlighted line above is the culprit. For those unfamiliar with WinDBG, here’s a little more hand holding: Click on the source code view, Ctrl+G and then type leak+0x35.

Remember to turn gflags off afterwards. The gflags is a registry and won’t clear even if you delete the file itself.

> gflags /i leak.exe -ust

32/64 bit

To debug 32 bit applications in a 64 bit system, you need to use the 32 bit version of WinDBG, which is not installed by default. You can find dbg_x86_*.msi somewhere in your “Microsoft SDKs” installation directory.

More advanced usage

Heap debugging is not restricted to detecting leaks. If you find your application allocating a lot of memory in some places, you can easily trace down where they come from with WinDBG and the !heap extension. This CodeProject article describes how to do just that in depth.

However, for doing that, I find a more modern and less intrusive way to be using XPerf tool. That combined with WinDBG and you’ve got a debugging power-house. (Resources on how to do it is extremely scarce. If you thought this WinDBG technique is obscure, you ain’t seen nothing :p I may write an post about that some time later when I feel like it.)

Wrong way of doing it

MSDN suggests something like this

    #define _CRTDBG_MAP_ALLOC
    #include <stdlib.h>
    #include <crtdbg.h>
    // At the end of your program

This solution is inadequate for a couple of reasons:

  • It only lists source code location for malloc. What’s the point?
  • Calling _CrtDumpMemoryLeaks() at the end of main() would mistakenly report memory allocated by static objects as leaks.

The only good thing about it is that you don’t need to leave your comfort zone of Visual Studio (that’s probably the entire programmer life for many people, unfortunately).

Some other good resources

Published by kizzx2, on October 9th, 2010 at 12:36 pm. Filled under: UncategorizedNo Comments

No comments yet.

Leave a Reply