Monday, May 23, 2011

LeakMon :- Part 3 Opening a file in visual studio through automation

This is the 3rd post related to the LeakMon tool. In this post we will see how the DumpViewer( DumpViewer is an application used to analyze the leak dump created by LeakMon ) is able to open a perticular file in Visual Studio and highlight the specified line.

Step 1:- Finding out the version of Visual studio to use
It is 2011 now and we have Visual studio 2010. But there are peoples or companies who still use Visual Studio 6. Things have changed a lot from Visual Studio 6 to Visual Studion 10, even the EXE name has changed. So the first task is to find out which verion of Visual Studio to use.

DumpViewer uses the same Visual Studio version that is reigstered as the default program for documents such as .c, .cpp, .h, .hpp in your machine. For instance, say we have the following line shown in DumpViewer..
func2 t:\naveen\pgms\cpp\2008\mulitthread\mulitthreaddlg.cpp(178)
DumpViewer will extract the file path and line number, from the above line and pass it to the FindExecutable function. This function will return the path of executable that is registered for above file types, which will be usually Visual Studio. If it is not Visual Studio, sorry it will fail!

In the file name of the executable returned by FindExecutable function, we will check there is a string "msdev.exe" or "devenv.exe" in it. If there is "msdev.exe" in the string, code that does the automation of Visual Studio 6 is called or otherwise the code to handle higher version of Visual Studio.
const LPCTSTR VC_6_APP_NAME = _T("msdev.exe");
const LPCTSTR VC_8_APP_NAME = _T("devenv.exe");
if( 0 == csExe.CompareNoCase( VC_6_APP_NAME ))
{
 // launch vc6
 OpenUsingVS<IApplication>( IID_IDispatch, L"MSDEV.Application",csFileName_i, nLineNo );
}
else if( 0 == csExe.CompareNoCase( VC_8_APP_NAME ) )
{
 // launch vs7 or any other higher versions of VS
 OpenUsingVS<_DTE>( EnvDTE::IID__DTE, 
   L"VisualStudio.DTE",csFileName_i, nLineNo );
}

Step 2:- Getting a pointer to already running instance of VisualStudio
Since we are going to open a file through auotmation, we need a pointer of either IApplication or _DTE (IApplication for Visual Studio 6 and _DTE for Visual Studio 7 and higher ) . We can create and instance of above type using the CoCreateInstance by passing the IID and CLSID. But this will create a new instance of VisualStudio each time we try to open a new file. So what we want is to get a pointer to IApplication / _DTE of an already running instance of Visual Studio if any. This can be done by enumerating the Moniker in the ROT (Running Object Table).

template<class T>
bool OpenUsingVS( IID riid, CString csProgID, CString csFileName_i, int nLineNo)
{
    HRESULT hRes; 
    CLSID clsid; 
    CComPtr<IUnknown> punk; 
    CComPtr<T> dte; 
    RETURN_ON_FAIL( ::CLSIDFromProgID( csProgID.operator LPCTSTR(), &clsid) ); 

    // Search through the Running Object Table for an instance of Visual Studio 
    // to use that either has the correct solution already open or does not have  
    // any solution open. 
    CComPtr<IRunningObjectTable> ROT; 
    RETURN_ON_FAIL( GetRunningObjectTable( 0, &ROT ) ); 

    CComPtr<IBindCtx> bindCtx; 
    RETURN_ON_FAIL( CreateBindCtx( 0, &bindCtx ) ); 

    CComPtr<IEnumMoniker> enumMoniker; 
    RETURN_ON_FAIL( ROT->EnumRunning( &enumMoniker ) ); 

    CComPtr<IMoniker> dteMoniker; 
    RETURN_ON_FAIL( CreateClassMoniker( clsid, &dteMoniker ) ); 

    CComPtr<IMoniker> moniker; 
    CComPtr<IMoniker> moniker2; 
    ULONG monikersFetched = 0; 
    while ( enumMoniker->Next( 1, &moniker, &monikersFetched ) == S_OK) 
    { 
        moniker2 =NULL;
        moniker->Reduce( bindCtx, MKRREDUCE_ALL, NULL, &moniker2 );
        IMoniker* pMon = (0 != moniker2)?moniker2:moniker;
        if ( moniker2->IsEqual( dteMoniker ) ) 
        { 
            hRes = ROT->GetObject( moniker, &punk ); 
            if ( hRes == S_OK ) 
            { 
                dte = punk; 
 
                if ( dte ) 
                { 
                    // We have got an instance pointer. Lets try to open
                    // the file using that pointer
                    if( OpenFile( dte,csFileName_i, nLineNo ))
                        return true;
                    dte = 0;
                } 
            } 
            punk = NULL; 
        } 
        moniker = NULL; 
    } 

    if ( !dte ) 
    {                
        // we didnt get any pointer from ROT. So lets go an create and new
        // instance.
        RETURN_ON_FAIL( ::CoCreateInstance( clsid, NULL, CLSCTX_LOCAL_SERVER, riid , (LPVOID*)&punk ) ); 
        dte = punk; 
        if ( !dte ) 
            return false; 
        OnLaunch( dte );
        OpenFile( dte,csFileName_i, nLineNo );
    }
    return true;
}

As you can in the above code, if we failed to get a vaild instance pointer of Visual Studio, we create a new one using CoCreateInstance. In Visual Studio 7 and higher, as soon as we release the com pointer that we got from CoCreateInstance, Visual Studio will terminate. To prevent Visual Studio from exiting like this, we need to call put_UserControl of DTE interface.

Step 3 Opening a File and selecting a line of it
From this part on, the processing is different for Visual Studio 6 and other versions of Visual Studio.

In Visual Studio 6

#define dsWindowStateMaximized 1
bool OpenFile(IApplication* m_spApplication,CString csFileName_i, int nLineNo )
 {
    CComPtr<IDispatch> pDispatch;
    // Make it visible just in case if it is not
    m_spApplication->put_Visible( VARIANT_TRUE );
    // Maximize the window
    m_spApplication->put_WindowState( dsWindowStateMaximized );
    // Get a pointer to the IDocument interface
    m_spApplication->get_Documents( &pDispatch );
    CComQIPtr<IDocuments> pDocs = pDispatch;
    CComVariant type="Auto";
    CComVariant read="False";
    pDispatch = 0;
    // The Open function of IDocument opens the file.
    pDocs->Open( CComBSTR(csFileName_i),type, read, &pDispatch );
    // Now for selecting a line, we need to get pointer to ITextSelection
    // Interface
    CComQIPtr<ITextDocument> pDoc = pDispatch;
    pDispatch = 0;
    pDoc->get_Selection( &pDispatch );
    CComQIPtr <ITextSelection> pTextSelection = pDispatch;
    CComVariant varReserved( FALSE );
    // now just set the cursor to the correct line and call SelectLine of ITextSelection
    pTextSelection->StartOfDocument( varReserved );
    pTextSelection->LineDown( varReserved, CComVariant(nLineNo-1));
    Sleep( 1000 );
    pTextSelection->SelectLine();
    return true;
 }

In Visual Studio 7 and higher

bool OpenFile( _DTE* pDTE,CString csFileName_i, int nLineNo )
{
    HRESULT hRes = 0; 
    CComPtr<ItemOperations> Operations;
    // Get the ItemOperations pointer
    RETURN_ON_FAIL(pDTE->get_ItemOperations( &Operations ));
    CComPtr<Window> Wnd;
    // Open the file using ItemOperations pointer
    RETURN_ON_FAIL(Operations->OpenFile( CComBSTR( csFileName_i),
                   CComBSTR(vsViewKindTextView), &Wnd ))
    
    // It might take some time to open the file.
    // so we will wait in a loop
    CComPtr<Document> pDoc;
    for( int nIdx =0; nIdx < 5; nIdx ++ )
    {
        pDTE->get_ActiveDocument( &pDoc );
        //Sleep( 100 );
        if( pDoc )
        {
            break;
        }
        if( nIdx == 4 )
        {
            return false;
        }
        Sleep( 100 );
    }
    // Active the main window. 
    CComPtr<Window> pMainWnd;
    RETURN_ON_FAIL(pDTE->get_MainWindow( &pMainWnd ))
    pMainWnd->Activate();
    
    // Get the TextSelection pointer
    CComPtr<IDispatch> pDisp = 0;
    RETURN_ON_FAIL(pDoc->get_Selection( &pDisp ));
    CComQIPtr<TextSelection> pSelection = pDisp;
     //GotoLine with VARIANT_TRUE will make the line selected
    pSelection->GotoLine( nLineNo, VARIANT_TRUE);
    return true;
}

Wednesday, March 30, 2011

LeakMon :- Part 2 Under the hood

This post explains how LeakMon application tracks various kind of resource allocation and report them.

Download Source :- LeakMon_source.zip

Basic working principle

Consider the case of tracking memory allocation. In this case, Leakmon will hook all the possible memory allocation and de-allocation functions. So when ever a function allocates some memory it will keep the pointer to the newly allocated memory as a key in a map and the call stack as value. If this memory is de-allocated, this entry in the map is removed. So at any point all the entries in the map are those memory pointers which haven't de-allocted. When you ask the Leakmon to Dump the leak, all it does is dumping the entries in the map.

This application has the following distinguishing features from the other resource leak tracking applications in codeproject and around.

1. You dont have to modify any single line of code to start tracking
2. It will track the allocation from all the binaries in the process. Means the tracking is not restricted to only the EXE binary of the process. It will track allocation from all the dependent DLL's as well

OK, Let's start with the working of LeakMon from the first binary, Injector.exe

Injector application



The Injector.exe allows you to select the process of which you want to track leak. It will list all the process in the system with the help of CreateToolhelp32Snapshot, Process32First and Process32Next functions.

So, when if you select a process and press the "Inject" button, what it does is, It will inject the "HookDll.dll" to the traget process' address space. This is done with the help of CreateRemoteThread API. Injecting a DLL using CreateRemoteThread API is explained detailed in the article Three Ways to Inject Your Code into Another Process. Anyway in Injector.exe the following code part does the injection.
void CInjectorDlg::OnInject()
{
......
CString csPid = m_List.GetItemText( nSelected, 0 );
DWORD dwPID = _ttoi( csPid );
HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|
PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ,
FALSE, dwPID );
if( !hProcess)
{
      AfxMessageBox( _T("Failed to open the process" ));
      return;
}
HINSTANCE hLib = LoadLibrary( "Kernel32.dll" );
PROC pLoadLib = (PROC)GetProcAddress( hLib, "LoadLibraryA" );
void* pLibRemote = ::VirtualAllocEx( hProcess, NULL, csPath.GetLength(),
MEM_COMMIT, PAGE_READWRITE );
::WriteProcessMemory( hProcess, pLibRemote, (void*)csPath.operator LPCTSTR(),
csPath.GetLength(), NULL );
if( !CreateRemoteThread( hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadLib, pLibRemote, 0, 0 ))
{
          AfxMessageBox( "Create Remote thread Failed" );
          :VirtualFreeEx( hProcess, pLibRemote,csPath.GetLength(), MEM_RELEASE );
}
}
The HookDll

HookDll.dll is the core part of the LeakMon. It performs almost all of the resource tracking stuffs. HookDll starts its working once it got injected into the target process. The BOOL CHookDllApp::InitInstance function gets called when this dll is loaded in the target process. This function however doesn't do much other than just creating a worker thread, DumpController.
BOOL CHookDllApp::InitInstance()
{
       HANDLE hThread = ::CreateThread( 0,0,DumpController, 0,0, 0 );
      CloseHandle( hThread );
      return CWinApp::InitInstance();
}
Now when the DumpController starts, it will first show the configuration dialog, where you can,
1. Select what kind of resource allocation you want to track
2. Select the path of the PDB's of the application
3. Stack depth


DebugHelp Functions
HookDll creates the call the stack, with the help of DebugHelp Functions. For the DebugHelp function to return the call stack correctly, it is necessary that you specify the correct PDB path of the application. Also it always good to use the latest version of the dbghelp.dll that comes with latest version of Debugging tool for windows. Another good feature that LeakMon support is integration with symbol server. However, adding the Symbol server functionality is very easy. All you have to do is to ensure that, the symsrv.dll is present in the same directory from where dbghelp.dll is loaded.
So now once you click the OK button, in the config dialog, the HookDll will start initializing symbol handler. To initialize symbol handler we have to call SymInitialize. When you call SymInitialize with the value of fInvadeProcess as TRUE, it will load symbol table for all the dlls in the process. But wait, we do better, we can show the progress of loading the symbols of each dll. Showing the progress of symbol loading is important especially when you are downloading a symbol from symbol server, which may take significant time.You might have seen this feature in Visual studio. when you start debugging an application in VS, it will show the symbol loading progress of each dll in the status bar.
To show the progress, we can register one call back with the function SymRegisterCallback64. Our callback function will get called before loading each symbol.
void ConfigDlg::OnOk()
{
..................
       SymInitialize(GetCurrentProcess(), (LPTSTR)csWholePath.operator LPCTSTR() , FALSE );
       SymRegisterCallback64( GetCurrentProcess(),SymRegisterCallbackProc64,(ULONG64 )this );
..................
       if( pSymRefreshModuleList )
       {
              pSymRefreshModuleList( GetCurrentProcess());
       }
       ..................
}


BOOL CALLBACK ConfigDlg::SymRegisterCallbackProc64(HANDLE hProcess,
                                                                                                ULONG ActionCode,
                                                                                                ULONG64 CallbackData,
                                                                                                ULONG64 UserContext)
{
       if( CBA_DEFERRED_SYMBOL_LOAD_START == ActionCode )
       {
              PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 pSybolLoadInfo =(PIMAGEHLP_DEFERRED_SYMBOL_LOAD64)CallbackData;
              ConfigDlg* pDlg = (ConfigDlg*)UserContext;
              CString csLoadtext = _T("Loading symbol for file: ");
              csLoadtext += pSybolLoadInfo->FileName;
              pDlg->m_ProgressDlg.SetDlgItemText( IDC_LOAD_INFO, csLoadtext );
       }
       // return false to indicate that we are not doing any symbol loading by ourself
       return FALSE;
}
API Hooking

To intercept the allocation and de-allocation calls(functions) made by dll's, LeakMon relies on User mode API hooking though IAT patching. I am not going to explain the IAT patching because there are already several articles in codeproject and other sites having a detailed explanation. Here is one good article to start with API hooking revealed.
How ever one thing that is different in LeakMon with usual custom is that, it doesn't walk through the dependency chain of dll. I mean, if A.exe has statically linked to B.dll and again if B.dll is linked to C.dll, what usually does for API hooking is, it will hook the API imported by A.exe, then find out the dependency of A.exe, which is B.dll, so hook its imported functions. Then it find out the depended dll of B.dll which is C.dll, hook it functions and so on. The problem with this approach is that only statically linked dlls will be hooked. If you are making some calls to a COM dll or if I you are creating an Active-X control in your application, those dll will never get hooked. So instead of walking through the dependency chain, Leakmon, enumerates all the dll's in that process and hooks it. In fact this approach is a little more easier too. It make use of the Module32First and Module32Next set of functions for this purpose.

As you can see, this application can track three kind of resource allocation and de-allocation namely Memory, GDI objects and HANDLES. Windows have a wider set of function for allocation and de-allocation for each of of resources. The following table shows the list of API's LeakMon hooks for tracking allocations/de-allocation of resources.

Memory allocation and de-allocation functions
HeapAllocHeapFreeHeapReAllocVirtualAllocEx
VirtualFreeExGlobalAllocGlobalReAllocGlobalFree
LocalAllocLocalReAllocLocalFree

GDI object creation and deletion functions
Bitmaps
LoadBitmapALoadBitmapWLoadImageA
LoadImageWCreateBitmapCreateBitmapIndirect
CreateCompatibleBitmapCreateDIBitmapCreateDIBSection
CreateDiscardableBitmapCopyImageGetIconInfo
GetIconInfoExAGetIconInfoExW
Icons
CopyIconCreateIconCreateIconFromResource
CreateIconFromResourceExCreateIconIndirectDestroyIcon
DuplicateIconExtractAssociatedIconAExtractAssociatedIconW
ExtractAssociatedIconExAExtractAssociatedIconExWExtractIconA
ExtractIconWExtractIconExAExtractIconExW
LoadIconALoadIconWPrivateExtractIconsA
PrivateExtractIconsW
Cursor
CreateCursorDestroyCursorLoadCursorA
LoadCursorWLoadCursorFromFileALoadCursorFromFileW
Brush
CreateBrushIndirectCreateSolidBrushCreatePatternBrush
CreateDIBPatternBrushCreateDIBPatternBrushPtCreateHatchBrush
Device context
CreateCompatibleDCCreateDCACreateDCW
CreateICACreateICWGetDC
GetDCExGetWindowDCReleaseDC
DeleteDC
Font
CreateFontACreateFontWCreateFontIndirectA
CreateFontIndirectW
Metafile
CreateMetaFileACreateMetaFileWCreateEnhMetaFileA
CreateEnhMetaFileWGetEnhMetaFileAGetEnhMetaFileW
GetMetaFileAGetMetaFileWDeleteMetaFile
DeleteEnhMetaFileCopyEnhMetaFileACopyEnhMetaFileW
CloseEnhMetaFileCloseMetaFile
Pen
CreatePenCreatePenIndirectExtCreatePen
Region
PathToRegionCreateEllipticRgnCreateEllipticRgnIndirect
CreatePolygonRgnCreatePolyPolygonRgnCreateRectRgn
CreateRectRgnIndirectCreateRoundRectRgnExtCreateRegion
Palette
CreateHalftonePaletteCreatePalette
Common Function
DeleteObject

Handle creation and deletion functions
Synchronization objects
CreateEventACreateEventWCreateEventExA
CreateEventExWOpenEventAOpenEventW
CreateMutexACreateMutexWCreateMutexExA
CreateMutexExWOpenMutexAOpenMutexW
CreateSemaphoreACreateSemaphoreWCreateSemaphoreExA
CreateSemaphoreExWOpenSemaphoreAOpenSemaphoreW
CreateWaitableTimerACreateWaitableTimerWCreateWaitableTimerExA
CreateWaitableTimerExWOpenWaitableTimerAOpenWaitableTimerW
File function
CreateFileACreateFileWCreateFileTransactedA
CreateFileTransactedWFindFirstFileAFindFirstFileW
FindFirstFileExAFindFirstFileExWFindFirstFileNameTransactedW
FindFirstFileNameWFindFirstFileTransactedAFindFirstFileTransactedW
FindFirstStreamTransactedWFindFirstStreamWFindClose
OpenFileByIdReOpenFileCreateIoCompletionPort
Authorization function
CreateRestrictedTokenDuplicateTokenDuplicateTokenEx
OpenProcessTokenOpenThreadToken
Directory management
FindFirstChangeNotificationAFindFirstChangeNotificationWFindCloseChangeNotification
File mapping
CreateMemoryResourceNotificationCreateFileMappingACreateFileMappingW
CreateFileMappingNumaACreateFileMappingNumaWOpenFileMappingA
OpenFileMappingW
Memory
HeapCreateHeapDestroyGlobalAlloc
GlobalReAllocGlobalFreeLocalAlloc
LocalReAllocLocalFree
Process and thread
CreateProcessACreateProcessWCreateProcessAsUserA
CreateProcessAsUserWCreateProcessWithLogonWCreateProcessWithTokenW
OpenProcessCreateThreadCreateRemoteThread
OpenThreadCreateJobObjectACreateJobObjectW
Mail slot
CreateMailslotACreateMailslotW
pipe
CreatePipeCreateNamedPipeACreateNamedPipeW
Registry
RegCreateKeyExARegCreateKeyExWRegCreateKeyTransactedA
RegCreateKeyTransactedWRegOpenCurrentUserRegOpenKeyA
RegOpenKeyWRegOpenKeyExARegOpenKeyExW
RegOpenKeyTransactedARegOpenKeyTransactedWRegOpenUserClassesRoot
RegCreateKeyARegCreateKeyWRegCloseKey
Common functions
DuplicateHandleCloseHandle

Note:- I have tried my best to include all the functions I know which allocates or de-allocate resources. Still, if know some function that i have missed, please let me know.

Now lets see how the hooking works. For each of the function in the above table LeakMon have a dummy function ( Which is actually 199 dummy functions ). So after hooking, the application will be calling my dummy function instead of the original function. In the dummy function what it does is,

1) Call the original function
2) Create the call stack and store it
3) Return the return value of retured by original function

Let's check the example of HeapAlloc function ( Remember that all the memory allocation functions such as new, malloc etc will finally ends up in calling HeapAlloc function to allocate memory. Similarly free, delete functions calls HeapFree ).
LPVOID WINAPI MyHeapAlloc( HANDLE hHeap,
                                                         DWORD dwFlags,
                                                         SIZE_T dwBytes )
{
           LPVOID lMem = pOrgHeapAlloc( hHeap, dwFlags, dwBytes );
           CreateCallStack( lMem, dwBytes );
           return lMem;
}
The CreateCallStack function creates the current function call stack and save it in a map. The key of the map is the memory address returned by original HeapAlloc and value is a structure which holds the call stack and size of memory allocated.

Now when the application calls HeapFree, the dummy function just removes the entry from the map and then call the original HeapFree function.
BOOL WINAPI MyHeapFree( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem )
{
       RemovCallStack( lpMem );
       return pOrgHeapFree( hHeap, dwFlags, lpMem );
}
Creating the Call Stack

This is the only part which actually has different processing for 64 bit and 32 bit. In 32 bit applications the call stack is created using the StackWalk64 function. You can either check the code ( StackDump function ) or read this nice article Walking the callstack, to learn more about the StackWalk64 function.

The stack walking in x64 machine was bit difficult for me. I actually had no idea how to do it. Thanks to Ken Johnson for posting an example of stack walking in x64 machine which practically made me to port this application to 64 bit also.

Dumping the leaks

After the hook dll have setup all the hooks, it will wait for couple of events. One among those event is Dump event. When user clicks on the Dump button in the Injector application, it will set the dump event to signalled state. When this event is signalled the thread in the HookDll will start dumping each entry in the map to a user specified file. The format of the dumpfile is mentioned in the previous post.

That's all the main thing about the Injector and HookDll. There is one more application DumpViewer, which actually has some nice features done using Visual Studio automation. I will explain those things in another post.

Open Source

I got couple of request to make this project open source, so I have opened a project in code.google.com and is available at http://code.google.com/p/leakmon/ . So if you have some idea or got some improvement points you are welcome to join me there. Suggestions are also welcome.

Tuesday, January 25, 2011

LeakMon - Track Handle leak, GDI leak and Memory leak in your applications

LeakMon is a light weight resource leak tracking utility which can track Memory leaks, GDI object leaks and handle leaks of a process. This tool is suitable mainly for tracking down the leaks that happens in between an operation.

Downloads
LeakMon

[Download from Codeproject]
LeakMon

Prerequisite

• You need to have installed VS2010 or Microsoft Visual C++ 2010 Redistributable Package.

Visual C++ 2010 Redistributable Package can be downloaded from the below path

For 32 bit applications (4.8 MB)

http://www.microsoft.com/downloads/en/details.aspx?familyid=A7B7A05E-6DE6-4D3A-A423-37BF0912DB84&displaylang=en

For 64 bit application (5.5 MB)
http://www.microsoft.com/downloads/en/details.aspx?familyid=BD512D9E-43C8-4655-81BF-9350143D5867&displaylang=en

How to use

This tool consists of three binaries, Injector, HookDll and a DumpViewer.
To start with, run the Injector.exe.



• Refresh :- Refresh the process list
• Inject :- Injects the hook DLL to the target process to start tracking of the leak
• Dump : - Dumps the leak found so far to a file.
• Reset: :- Ignore/Clear all the leaks found up to this point.
• PDB Info : - After Injecting the DLL to the target process, you can click this button to view and make sure that all the pdb’s of necessary DLL’s are loaded correctly.

Using the injector.exe, select the target process that you want to debug. In this case say "MultiThread.exe". Now to start tracking, click the “Inject” button. On doing this, a settings window as in the below figure will pop up. (Note: The settings window is actually shown in the target process' context).



The setting window allows you to choose the “type of leak” you want to track. The types include memory leak, GDI leak and Handle leak.

The next option in the Settings dialog is “Stack Depth”. The value in the stack depth controls the number of functions that will be retrieved while tracking the allocation call stack. The value you enter in here can considerably affect the speed of the application. Lesser the value, the faster the application will be.

Collecting the call stack will never be perfect, without proper PDB files. So to get proper call stack information, specify the path PDB files of the binaries used in your application. Without the pdb’s of Microsoft dll’s such as MSVCRT.dll, MFCxx.dll etc, you will not be able to see the call stack of memory allocated using new and new[] sometimes. If you don’t have the symbols for Microsoft dlls, click on the “Add Symbol server”. Also it is a must that you have to specify the Folder in which the LeakMon binary resides. (“T:\LeakMon” in the below sample). Once you have set all the necessary pdb’s path click OK. The tracking starts from this moment

So now, once you have finished your processing and want to see all the allocations that haven’t de-allocated, press the “Dump” button in the injector. If there is some leak, it will prompt you with a save dialog, though which you can save the leak information file.

Analyzing the leak dump file


I will explain the format of the dump file with the following example. Consider the below code


i.e. The sequence of calling is in the order


CMulitThreadDlg::OnBnClickedButton1()->func1()->func2().

Now the information regarding the memory leak in the above code will look as follows.


-->Bytes allocated -- 76

_heap_alloc_base f:\dd\vctools\crt_bld\self_x86\crt\src\malloc.c(105)
_heap_alloc_dbg_impl f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c(427)
_nh_malloc_dbg_impl f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c(239)
_nh_malloc_dbg f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c(296)
_malloc_dbg f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c(160)
operator new f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxmem.cpp(407)
operator new[] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxmem.cpp(442)
operator new[] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxmem.cpp(67)
func2 t:\naveen\pgms\cpp\2008\mulitthread\mulitthreaddlg.cpp(178)
func1 t:\naveen\pgms\cpp\2008\mulitthread\mulitthreaddlg.cpp(184)
CMulitThreadDlg::OnBnClickedButton1 t:\naveen\pgms\cpp\2008\mulitthread\mulitthreaddlg.cpp(189)
_AfxDispatchCmdMsg f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp(82)
CCmdTarget::OnCmdMsg f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp(381)
CDialog::OnCmdMsg f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp(85)
CWnd::OnCommand f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(2364)
CWnd::OnWndMsg f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(1769)
CWnd::WindowProc f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(1755)
Starting from the top function, traverse down, until you find a function from your file. In the above case, func2().  So it can be concluded that, some memory that has allocated from the func2() haven’t de-allocated.

Analyzing the leak with DumpViewer

Even though you can viewer the dump file in any text editor, DumpViewer provides you some extra features that are really handy. Most of the features are self explanatory. One of the main feature is that, when you double click on a particular function in the tree, it will open the source file in visual studio with the line in which leak occurred as selected.


Under the Hood

This application basically works by hooking all the possible functions that allocates and de allocates a resource. For example when tracking memory leak it hooks the memory allocation API's such as HeapAlloc, HeapFree, VirtualAlloc, VirtualFree etc. A more detailed step by step explanation of this tool will be posted in next posts.

I have tried my best to remove the bugs in this application. But still if you encounter any bugs, let me know. Suggestions or improvements are highly appreciated.

[Update] :- Part 2  Leakmon- Under the hood