Leaking memory is probably the single most painful aspect of native code – its the reason managed was ever born.
At work, our code routes ‘new’ calls through_aligned_malloc_dbg. This CRT API, along with cousins like_malloc_dbg and_calloc_dbg, takes extra parameters containing a file name and line number, and so enables the CRT to report the exact location of an unreleased allocation upon process termination:
Sadly, this is useful only in the handful of cases where the code allocates directly. What if the offending allocation is performed via some common container routine? Even worse – what if the leak is properly de-allocated in destructors at shutdown time? The CRT support would be of no use. Wouldn’t it be nice if we could see the entire stack that allocated unreleased memory?
There are two powerful, free, and vastly different tools from Microsoft, that achieve just that.
Enter LeakDiag!
I’m actually not sure how public this tool is. Two of its components, LeakDiag and LDGrapher are available on the public MS FTP, but a third one, LDParser, seems to be available only by Microsoft Premier Support. Anyway, both LDParser and LDGrapher only format the output, and LDGrapher can do most (but not all!) of what LDParser does.
LeakDiag does its magic by using Detours technology (fascinating read!) to intercept memory allocators calls. Detours enables interception of any API, and not just replace it butextend it – that is, it preserves the original function, and enables calling it via a so called ‘trampoline’ stub. LeakDiag allows you to specify various low-level allocators, and once activated it intercepts them and adds stack-walking functionality to them.
To demonstrate, consider the leaking code here (adapted from a UMDH demo) :
#include "stdafx.h"
#include
#include
void LeakyFunc1();
void LeakyFunc2();
void LeakyFunc3();
void LeakHere(int value);
int _tmain(int argc, _TCHAR* argv[])
{
printf("Activate LeakDiag tracking here...nn");
_getch();
printf("Take first log now...nn");
_getch();
for (int i=0;i<1000;i++)
{
LeakyFunc1();
LeakyFunc2();
LeakyFunc3();
}
printf("Take second log..nnPress any key to exit application...n");
_getch();
return 0;
}
void LeakyFunc1() { LeakHere(500); }
void LeakyFunc2() { LeakHere(1000); }
void LeakyFunc3() { LeakHere(1500); }
void LeakHere(int value) { char * cBuff = new char[value]; }
Start LeakDiag and run your program. In the LeakDiag window select view/Refresh (just a good habit, meaningful if it was already running), and select your process at the list (in this example: LeakyApp.exe). At the first wait for input (and generally, as early as you can in the program), start the LeakDiag C Runtime allocator:
This activates the API interception in your selected process (LeakyApp.exe), for your selected API (CRT allocator).
Focus back to LeakyApp.exe and press any key. At the second and third waits for input, take a LeakDiag log:
Click anything again to end the program. LeakDiag dumps his logs as xml files, by default into %LeakDiag dir%/logs. The files are quite readable and it is occasionally useful to manually dig in – but to gain some insight into large dumps (or many dumps), LDGrapher can help tremendously.
Start LDGrapher. Open all the allocation logs you dumped. A multi-line graph would appear, where every line represents a specific recurring allocation stack. The stack responsible for most allocations is coloured red, the rest are yellow. Each x-tick is a specific log dumped. And the useful part: click any circle, representing an allocation stack at a specific log, and inspect thecomplete stack leading to that allocation, along with source file names and line numbers!
While this is mega-cool as is, it can be further tweaked in many ways. You can intercept and dump different allocators. If you’re hunting for a leak of a known size, you can limit the dump to include just that size (or a size range). You can use DbgHlpStackWalk to overcomeFPO (which youshouldn’tuse in your own code anyway!), and some more.
However, LeakDiag has one significant flaw – that I think amounts to just a weird design choice (that is, I can’t understand when it would be helpful):all its functionality (except the GUI) seems to run in the target process. You can actually see LeakDiag messages in your debugger.
That can make delicate control quite hard. For one, you cannot place breakpoints in locations where you want a dump. (hence,the ‘getch’ in the code sample). For two, suppose you’re not continuously leaking memory – but just forgot to release some. Wouldn’t it be nice to be able to take an allocation dump just before the process terminates, and see whatever still needs releasing? Alas, you cannot do this with LeakDiag, as any code you intend to run immediately before terminating would not run.
The solution (hint, UMDH), would have to wait for another post.
http://www.codeproject.com/KB/cpp/Tools.aspx