Allocator Hooks in release mode

Mar 10, 2011 at 11:18 AM

I have a memory leak that occurs at a customer, that I cannot reproduce at home.

The application uses CRT runtime DLL's because it is split in DLLs, where it is required that they share the same heap.

Would it possible to make the same allocator hooks to the release mode CRT runtime DLL's ?

I know it requires one removes the ifndef _DEBUG from the Vld.h (Or create a special define that enforces Vld eventhough _DEBUG is not enabled).

Coordinator
Mar 10, 2011 at 7:34 PM

VLD not support hooking release CRT runtime dll's. So I think it does not work

Mar 10, 2011 at 8:11 PM

Yes I'm aware that VLD currently doesn't support release CRT runtime dll's, but I was wondering if it is possible (with some modifications) to make hooks into these dll's ?

Coordinator
Mar 10, 2011 at 8:14 PM

I think it's possible, but required a lot of work to be done. Maybe it will work even without CRT hook, because we hook also Heap... functions.

Mar 10, 2011 at 8:52 PM

What about overriding the global new and delete operator, and then "just" call the VLD framework to register allocation and deallocation ?

Coordinator
Mar 10, 2011 at 10:44 PM

You can try that way :-)

Mar 10, 2011 at 10:47 PM
Edited Mar 14, 2011 at 6:00 PM

Think maybe I have solved it, please tell me if I'm doing something awful:

I added this code to the top of testsuite.cpp (after includes):

  void * _cdecl operator new (size_t cbSize)
  {
   return HeapAlloc(GetProcessHeap(), 0x0, cbSize);
  }

  void  _cdecl operator delete(void *p)
  {
   HeapFree(GetProcessHeap(), 0x0, p);
  }

  void* __cdecl operator new[](size_t cbSize)
  {
   return HeapAlloc(GetProcessHeap(), 0x0, cbSize);
  }

  void __cdecl operator delete[](void* p)
  {
   HeapFree(GetProcessHeap(), 0x0, p);
  }

Then I fixed the VLD so it could be activated even if _DEBUG was not defined (comment out the ifdef). It seems VLD is able to hook the HeapAlloc, HeapFree no matter if in debug or release mode.

Mar 11, 2011 at 9:17 PM

Have been testing a little, and so far it is only std::string leaks that fails to be handled. The reason for this is probably that when linking to CRT dlls, then basic_string is imported from the CRT dll instead of creating a template instance.

The VLD also fails to detect std::string leaks without use of global new and delete operator. It only detects them when linking to the static CRT lib. Would be nice if one could specify that one didn't want the CRT dll basic_string import.

Mar 12, 2011 at 8:36 PM

Could also be nice if VLD reacted to a define like "VLDFORCE", so it could be enabled even if _DEBUG was not defined.

Then one could upgrade to the latest VLD without needing to refix the code to work without _DEBUG.

Mar 14, 2011 at 5:51 PM
Edited Apr 5, 2011 at 7:27 PM

Seems the global new operator is not your friend when in the MFC world, as afxmem.cpp overloads them aswell. Very annoying.

This causes errors as afxmem.cpp uses free and malloc, which doesn't work together with HeapAlloc and HeapFree.

I guess I have to make an explict call to VLD to register the memory allocation when in release-mode.

Mar 29, 2011 at 11:32 AM
snakefoot wrote:

Think maybe I have solved it, please tell me if I'm doing something awful:

I added this code to the top of testsuite.cpp (after includes):

  void * _cdecl operator new (size_t cbSize)
  {
   return HeapAlloc(GetProcessHeap(), 0x0, cbSize);
  }

  void  _cdecl operator delete(void *p)
  {
   HeapFree(GetProcessHeap(), 0x0, p);
  }

  void* __cdecl operator new[](size_t cbSize)
  {
   return HeapAlloc(GetProcessHeap(), 0x0, cbSize);
  }

  void __cdecl operator delete[](void* p)
  {
   HeapFree(GetProcessHeap(), 0x0, p);
  }

Then I fixed the VLD so it co

uld be activated even if _DEBUG was not defined (comment out the ifdef). It seems VLD is able to hook the HeapAlloc, HeapFree no matter if in debug or release mode.H

 

After the operator override code is added, memory leak checking is working. Could you tell me why we need to add this extra code? I can't find the difference between debug and release which is related to this operator override code.

 

 

 

Apr 5, 2011 at 7:30 PM
Edited Apr 5, 2011 at 8:50 PM

VLD will in debug builds automatically hook the allocator methods in the CRT runtime, so you don't need to override the global operator new and delete.

If VLD is not able to function in debug mode, then you should create an issue.

My quest is to make the VLD work in release builds, as some memory leaks only occurs in the wild, where debug builds are not possible. Also release builds are a lot faster than debug builds even with no optimization enabled.

Apr 5, 2011 at 8:47 PM
Edited Apr 5, 2011 at 8:48 PM

I have been able to make things work by adding these two methods to the VisualLeakDetector class (And expose them in the DLL interface);

LPVOID VisualLeakDetector::ReportAlloc(LPVOID block, size_t size, HANDLE heap)
{
    context_t context;
    CAPTURE_CONTEXT(context);

    tls_t   *tls = vld.gettls();

    // malloc is a CRT function and allocates from the CRT heap.
    tls->flags |= VLD_TLS_CRTALLOC;

    BOOL firstcall = (tls->context.fp == 0x0);
    if (firstcall) {
        // This is the first call to enter VLD for the current allocation.
        // Record the current frame pointer.
        tls->context = context;
    }

    // The module that initiated this allocation is included in leak
    // detection. Map this block to the specified heap.
    mapblock(heap, block, size, false, tls->ppcallstack);

    if (firstcall)
    {
        if (tls->ppcallstack)
        {
            tls->flags &= ~VLD_TLS_CRTALLOC;
            getcallstack(tls->ppcallstack, tls->context);
        }

        // Reset thread local flags and variables for the next allocation.
        tls->context.fp = 0x0;
        tls->flags &= ~VLD_TLS_CRTALLOC;
    }

    return block;
}

void VisualLeakDetector::ReportFree(LPVOID mem, HANDLE heap)
{
    // Unmap the block from the specified heap.
    vld.unmapblock(heap, mem);
}

Then my override of the global operators looks like this:

#ifndef _DEBUG
  void * _cdecl operator new (size_t cbSize)
  {
      void* mem = malloc(cbSize);
      VLDReportAlloc(mem, cbSize, (HANDLE)_get_heap_handle());
      return mem;
  }

  void  _cdecl operator delete(void *p)
  {
      VLDReportFree(p, (HANDLE)_get_heap_handle());
      free(p);
  }

  void* __cdecl operator new[](size_t cbSize)
  {
      void* mem = malloc(cbSize);
      VLDReportAlloc(mem, cbSize, (HANDLE)_get_heap_handle());
      return mem;
  }

  void __cdecl operator delete[](void* p)
  {
    VLDReportFree(p, (HANDLE)_get_heap_handle());
    free(p);
  }
#endif

May 30, 2011 at 8:37 AM

I have now submitted a patch to VLD:

http://vld.codeplex.com/SourceControl/list/patches

It allows me to detect memory leaks in release-builds with optimization turned off

Hope that you will accept the patch.

-Rolf

Coordinator
Jun 1, 2011 at 7:14 PM

Hi. We apply your patch. Can you try build VLD from latest source code without redefining global new and delete operators in your project? I want remove VLDReportAlloc and VLDReportFree, If that will work.

Jun 3, 2011 at 11:00 AM

I can see that the memory leak detection now works with your TestSuite, when compiled with NDEBUG. So if you like, then you can remove VLDReportAlloc and VLDReportFree.

Jun 6, 2011 at 4:04 PM
Edited Jun 6, 2011 at 4:57 PM

Just tried to use the latest version of VLD on my main application, and it now creates an endless loop in _HeapDestroy(), because when calling the "real" HeapDestroy, then it enters _HeapDestroy again.

Coordinator
Jun 6, 2011 at 9:03 PM

This is strange, can you try again with latest revision. I can't reproduce your problem.

Jun 6, 2011 at 9:32 PM

I tried to call HeapDestroy from the TestSuite, but the endless loop could not be reproduced. My main application is COM+ application where an exe-file calls into a COM+ dll. It is during initialization that the endless loop occurs.

Coordinator
Jun 7, 2011 at 1:54 PM

Can you help us and find revision when bug appear? I think this bug may appear at revision 66209 (because of changes in utility.cpp) or 67239

Jun 14, 2011 at 7:16 PM

Sadly enough I have already spent too much time on getting VLD to work with my main application (Ended up with patching 1.9h so it would work).

I don't have the resources available to investigate further into the issues of the current trunk version :(