Diagnostic - memory leak analyzer

Diagnostic.dll is easy to user memory leak detection tool for Windows applications.


Main features:

- Does not require any integration or recompilation of your project.
- Tracks memory leaks from all .dll's within same process.
- Works with any programming language.
- Application being analyzed can be remotely controlled to enable or disable memory leak detection.
- 32-bit and 64-bit platforms are supported.
- Any native allocation functions are tracked (malloc, realloc, free, new, new[], ...)
- Windows api's heap allocations procedures are tracked: LocalAlloc, LocalReAlloc, LocalFree, ...
- Any C++ higher level container allocation functions are also tracked (E.g. std::vector, str::map and so on).
- Any other 3-rd party library which uses native allocation routines are also supported (for example OpenGL - gl* )
- .NET garbage collected memory is not supported (gcnew or memory allocated in C#), but managed call stack determination is supported.

Diagnostic v2 improvements:

- (Bugfixes) Improved mechanism of memory releasing especially then it's done through windows internal functions.
- (Feature) Added support for licensing per machine or per user.

Using diagnostic memory leak control is relatively simple - four steps procedure without any additional integration or re-compiling.

If this relatively simple approach is not so useful to you - you can check also source code level integration - from this link.


Analyzing statistics

Let's take relatively simple example:


... somewhere in main ...

CMyTestClass testx;

for( int i = 0; i < 2; i++ )
   testx.InTestMethod1();


MyTestClass.cpp:
#include "MyTestClass.h"
#include "DiagnosticControl.h"

void CMyTestClass::InTestMethod1(void)
{
    new char;
    InTestMethod2();
}

void CMyTestClass::RecursionTest(int level)
{
    level--;
    if( level <= 0 )
        return;

    new char[ rand() % 1000 ];
    RecursionTest( rand() % level );
    RecursionTest( rand() % level );
}

void CMyTestClass::InTestMethod2(void)
{
    malloc(1024);
    RecursionTest( rand() % 30 );
}

malloc (in red color) consumes most heavy memory leak from all functions, and because of for-loop at begging of program it will loose alltogether 2 * 1'024 = 2'048 bytes. This now can be seen as top 1 in memory leak report:

Total amount of leaked memory: 7'436 / in 8 allocation pools

Leak 1)  2'048 bytes / 27.5 %

Diagnostic.dll                   _hookHeapAlloc (00007FF9BB2B7AE6)
MSVCR100.dll                     malloc (0000000063CE8D17)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(25):  CMyTestClass::InTestMethod2 (00007FF74A401124)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(8):  CMyTestClass::InTestMethod1 (00007FF74A401082)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\testdiagnostic.cpp(14):  main (00007FF74A40132D)
TestDiagnostic.exe               f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c(555):  __tmainCRTStartup (00007FF74A401696)
KERNEL32.DLL                     BaseThreadInitThunk (00007FF9C8CC13D2)
ntdll.dll                        RtlUserThreadStart (00007FF9C9FB5454)

Because call stack is the same during two function calls, memory leaks are recombined into same "allocation pool".

Most useful information in here is percentage of total memory leaks (27,5% in our case) - if that value is large enough - then by making one bugfix in one function we can fix memory leak of 27,5% percents. The most intresting thing comes with recursive function calls or when call functions changes all the time - this is something we can see in rest of memory leaks:

Leak 2)  1'402 bytes / 18.9 %

Diagnostic.dll                   _hookHeapAlloc (00007FF9BB2B7AE6)
MSVCR100.dll                     malloc (0000000063CE8D17)
MSVCR100.dll                     operator new (0000000063CE8DDB)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(16):  CMyTestClass::RecursionTest (00007FF74A4010CA)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(18):  CMyTestClass::RecursionTest (00007FF74A4010E8)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(26):  CMyTestClass::InTestMethod2 (00007FF74A401140)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(8):  CMyTestClass::InTestMethod1 (00007FF74A401082)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\testdiagnostic.cpp(14):  main (00007FF74A40132D)
TestDiagnostic.exe               f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c(555):  __tmainCRTStartup (00007FF74A401696)
KERNEL32.DLL                     BaseThreadInitThunk (00007FF9C8CC13D2)
ntdll.dll                        RtlUserThreadStart (00007FF9C9FB5454)

Leak 3)  1'126 bytes / 15.1 %

Diagnostic.dll                   _hookHeapAlloc (00007FF9BB2B7AE6)
MSVCR100.dll                     malloc (0000000063CE8D17)
MSVCR100.dll                     operator new (0000000063CE8DDB)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(16):  CMyTestClass::RecursionTest (00007FF74A4010CA)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(19):  CMyTestClass::RecursionTest (00007FF74A401101)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(26):  CMyTestClass::InTestMethod2 (00007FF74A401140)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(8):  CMyTestClass::InTestMethod1 (00007FF74A401082)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\testdiagnostic.cpp(14):  main (00007FF74A40132D)
TestDiagnostic.exe               f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c(555):  __tmainCRTStartup (00007FF74A401696)
KERNEL32.DLL                     BaseThreadInitThunk (00007FF9C8CC13D2)
ntdll.dll                        RtlUserThreadStart (00007FF9C9FB5454)

...

Leak 7)  281 bytes / 3.8 %

Diagnostic.dll                   _hookHeapAlloc (00007FF9BB2B7AE6)
MSVCR100.dll                     malloc (0000000063CE8D17)
MSVCR100.dll                     operator new (0000000063CE8DDB)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(16):  CMyTestClass::RecursionTest (00007FF74A4010CA)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(18):  CMyTestClass::RecursionTest (00007FF74A4010E8)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(19):  CMyTestClass::RecursionTest (00007FF74A401101)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(26):  CMyTestClass::InTestMethod2 (00007FF74A401140)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(8):  CMyTestClass::InTestMethod1 (00007FF74A401082)
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\testdiagnostic.cpp(14):  main (00007FF74A40132D)
TestDiagnostic.exe               f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c(555):  __tmainCRTStartup (00007FF74A401696)
KERNEL32.DLL                     BaseThreadInitThunk (00007FF9C8CC13D2)
ntdll.dll                        RtlUserThreadStart (00007FF9C9FB5454)

Please note that in this case leak #2 and #3 call stack looks almost identical, but in practise address of second function differs (as well as source code line number) - because such difference - memory leaks detection can defragment memory leak report quite heavily. With our leak report it defragmented memory leak report into 5 call stack frames, but for real-life application this can result in 100 / 1'000 and even more memory allocation leaks.

To fight with this fragmentation problem memory leak report contains additional section at the end of report, which looks like this:

Top functions via which most memory disappered

Leak 1)  7'436 bytes / 100.0 %
ntdll.dll                        RtlUserThreadStart (00007FF9C9FB5454)
Leak 2)  7'436 bytes / 100.0 %
KERNEL32.DLL                     BaseThreadInitThunk (00007FF9C8CC13D2)
Leak 3)  7'436 bytes / 100.0 %
Diagnostic.dll                   _hookHeapAlloc (00007FF9BB2B7AE6)
Leak 4)  7'436 bytes / 100.0 %
TestDiagnostic.exe               f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c(555):  __tmainCRTStartup (00007FF74A401696)
Leak 5)  7'436 bytes / 100.0 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\testdiagnostic.cpp(14):  main (00007FF74A40132D)
Leak 6)  7'436 bytes / 100.0 %
MSVCR100.dll                     malloc (0000000063CE8D17)
Leak 7)  7'434 bytes / 100.0 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(8):  CMyTestClass::InTestMethod1 (00007FF74A401082)
Leak 8)  5'388 bytes / 72.5 %
MSVCR100.dll                     operator new (0000000063CE8DDB)
Leak 9)  5'386 bytes / 72.4 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(26):  CMyTestClass::InTestMethod2 (00007FF74A401140)
Leak 10)  5'386 bytes / 72.4 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(16):  CMyTestClass::RecursionTest (00007FF74A4010CA)
Leak 11)  2'407 bytes / 32.4 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(18):  CMyTestClass::RecursionTest (00007FF74A4010E8)
Leak 12)  2'402 bytes / 32.3 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(19):  CMyTestClass::RecursionTest (00007FF74A401101)
Leak 13)  2'048 bytes / 27.5 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(25):  CMyTestClass::InTestMethod2 (00007FF74A401124)
Leak 14)  2 bytes / 0.0 %
TestDiagnostic.exe               c:\prototyping\memory\diagnostic\release\mytestclass.cpp(6):  CMyTestClass::InTestMethod1 (00007FF74A401073)

 

This statistics “counts” how many time each function were called in each call start and calculates summary amount of memory lost. This is interesting from point – whether function is a common / routing function ( does not perform any allocation by itself ) or actual function doing the allocation. Routing / common functions are for example: RtlUserThreadStart / CorExeMain – every call stack starts from them, and Diagnostic.dll / _hookHeapAlloc – every function ends with it.

By checking functions through we can locate function marked by red color which allocates most of memory leaks - that's the line "new char[ rand() % 1000 ];". By fixing it - we can potentially fix 72.4 % of memory leaks, which in a turn can potentially include first found 27.5 %

 

Common practicalities

 

Currently known limitations:

Demo version limitations:

To switch to fully registered version, pick up administrator password from readme.txt - but if you dare - take a challenge of hacking Diagnostic tool - to be able to license product without altering source code of Diagnostic.