jComHeap : checks the COM heap for leaks using IMallocSpy

Download jcomheap.zip

Synopsis:

testjcomheap.cpp
debug.reg
allocationmap.cpp
AllocationMap.h
Allocator.cpp
Allocator.h
AllocInfo.cpp
AllocInfo.h
AllocNumberHandler.cpp
AllocNumberHandler.h
ArenaHeader.cpp
ArenaHeader.h
ComHeapRegistry.cpp
ComHeapRegistry.h
comheapspy.cpp
comheapspy.h
common.cpp
common.h
jcomheap.cpp
jcomheap.h
reporter.cpp
Reporter.h
stats.cpp
Stats.h


testjcomheap.cpp

Synopsis
//////////////////////////////////////////////////////

#include <comdef.h>
#include "..\jcomheap\jcomheap.h"
#include <tchar.h>
#include <crtdbg.h>
//
//#ifdef _DEBUG
//#define DBG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__)
//#define new DBG_NEW
//#endif

void test_empty()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;

  ch->Init();
  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1alloc0free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  void *p = CoTaskMemAlloc(128);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1alloc1free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  void *p = CoTaskMemAlloc(128);
  CoTaskMemFree(p);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_2alloc1free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  void *p = CoTaskMemAlloc(128);
  p = CoTaskMemAlloc(28);
  CoTaskMemFree(p);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1alloc1realloc1free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  void *p = CoTaskMemAlloc(128);
  p = CoTaskMemRealloc(p, 5001);
  CoTaskMemFree(p);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1alloc1realloc0free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  void *p = CoTaskMemAlloc(128);
  p = CoTaskMemRealloc(p, 5000);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1bstr1free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  BSTR bstr = SysAllocString(L"bob");
  SysFreeString(bstr);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }
void test_1bstr0free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  BSTR bstr = SysAllocString(L"bob");

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1bstr1realloc1free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  BSTR bstr = SysAllocString(L"bob");
  SysReAllocStringLen(&bstr, L"ted", 21); //actual alloc is 48 bytes (2 * 21 rounded to the next 16 byte paragraph)
  SysFreeString(bstr);

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

void test_1bstr1realloc0free()
  {
  CoInitialize(0);
  jComHeap* ch = new jComHeap;
  ch->Init();

  BSTR bstr = SysAllocString(L"bob");
  SysReAllocStringLen(&bstr, L"ted", 21); //actual alloc is 48 bytes (2 * 21 rounded to the next 16 byte paragraph)

  CoUninitialize();
  ch->Report(__FILE__, __LINE__);  
  delete ch;
  }

int main()
  {
  //set the memory leak dump at the end of the exe run.
#ifdef _DEBUG
  int flag;
  flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
  flag |= _CRTDBG_LEAK_CHECK_DF;
  _CrtSetDbgFlag(flag);

  _CrtSetBreakAlloc(-1);
  //  int* x = new int;
#endif

  test_1bstr1free();
  test_1alloc1realloc1free();
  test_1alloc1free();
  test_1bstr1realloc1free();
  test_empty();

  //leaks from here on; only 1 leak allowed!
  //test_1alloc0free();
  //test_2alloc1free();
  //test_1alloc1realloc0free();
  //test_1bstr0free();
  test_1bstr1realloc0free();

  //untested:
  //    // occasionally use a bad ptr, potentially corrupting the heap
  //        DWORD *pdw = LPDWORD(p) + 10;
  //          *(--pdw) = 12;  
  //        while (pdw >= LPDWORD(p)); // highly defective statement!

  return 0;
  }


debug.reg

Synopsis
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\arrizza\Debug]
"jComHeap"=dword:00000002 
 


allocationmap.cpp

Synopsis
#pragma warning(disable: 4786) //identifier was truncated to '255' characters in the debug info

#include <crtdbg.h>
#include "common.h"
#include "allocationmap.h"

#pragma warning(disable: 4291) //xxx : no matching operator delete found;

void AllocationMap::ClearAll()
  {
  ALLOCINFOP ai;
  for (iterator p = begin(); p != end(); p++)
    {
    ai =(*p).second;
    _ASSERTE(ai);
    destroymem(ai);
    }
  clear();
  }

ALLOCINFOP AllocationMap::Add(unsigned long size, void* pAdjusted, AllocNumberHandler& allocnum)
  {
  ALLOCINFOP ai = (*this)[pAdjusted];
  if (ai == 0)
    {
    ai =  new (0,0) ALLOCINFO(size, pAdjusted, allocnum);
    (*this)[pAdjusted] = ai;
    }
  else
    ai->ResetAlloc(size, pAdjusted, allocnum);
  return ai;
  }

ALLOCINFOP AllocationMap::FreeRequest(void* pRequest)
  {
  ALLOCINFOP ai = (*this)[pRequest];
  if (ai == 0)
    HeapTrace(_T("ERROR! Free: has not been allocated: 0X%p\n"), pRequest);
  else
    ai->SetFree();
  return ai;
  }

AllocationMap.h

Synopsis
#pragma once

#include <map>
#include <functional>
using std::less;
using std::map;
#include "allocinfo.h"
#include "allocator.h"
#include "allocnumberhandler.h"

typedef map<const void*, ALLOCINFOP, less<const void*>, MyAllocator > BaseMap;
class AllocationMap : public BaseMap
  {
  public:
    void ClearAll();
    ALLOCINFOP Add(unsigned long size, void* pAdjusted, AllocNumberHandler& allocnum);
    ALLOCINFOP FreeRequest(void* pRequest);
  };

Allocator.cpp

Synopsis
#include "allocator.h"
#include "common.h"
using namespace std;

char* MyAllocator::_Charalloc(size_type _N)
  {
  return (char*) new (0,0) char[_N];
  }

MyAllocator::pointer MyAllocator::allocate(size_type _N, const void *)
  {
  return (pointer) new (0,0) char [_N * sizeof(value_type)];
  }

void MyAllocator::construct(pointer _P, const_reference _V)
  {
  new ((void *)_P) value_type(_V);
  }

void MyAllocator::deallocate(void *_P, size_type)
  {
  //destroymem(_P);
  }

void MyAllocator::destroy(pointer _P)
  {
  deallocate(_P , 0);
  }

Allocator.h

Synopsis
#pragma once

#include "allocinfo.h"
#include <memory>
using std::allocator;

//------------------
//-- used to allocate map memory on to a separate heap
class MyAllocator : public allocator<ALLOCINFOP>
  {
  public:
    char * _Charalloc(size_type _N);
    pointer allocate(size_type _N, const void *);
    void construct(pointer _P, const_reference _V);
    void deallocate(void *_P, size_type);
    void destroy(pointer _P);
  } ;

AllocInfo.cpp

Synopsis
//#define WIN32_LEAN_AND_MEAN
//#include <windows.h>
#include <crtdbg.h>
#include "allocinfo.h"


ALLOCINFO::ALLOCINFO(unsigned long lastalloc, void* v, unsigned long allocnum)
: inuse(false), numallocs(0), numfrees(0), size(0), val(0), allocnumber(0)
  {
  ResetAlloc(lastalloc, v, allocnum);
  }

void ALLOCINFO::ResetAlloc(unsigned long lastalloc, void* v, unsigned long allocnum)
  {
  _ASSERTE(!inuse && "alloced memory is already in use");
  numallocs++;
  inuse       = true;
  size        = lastalloc;
  val         = v;
  allocnumber = allocnum;
  }

void ALLOCINFO::SetFree()
  {
  _ASSERTE(inuse && "memory was not in use");
  inuse = false;
  numfrees++;
  }


AllocInfo.h

Synopsis
#pragma once

//---------
//-- contains individual node information
class ALLOCINFO
  {
  public:
    ALLOCINFO(unsigned long lastalloc, void* v, unsigned long allocnum);
    void ResetAlloc(unsigned long lastalloc, void* v, unsigned long allocnum);
    void SetFree();

    bool  inuse;
    unsigned long numallocs;
    unsigned long numfrees;
    unsigned long size;
    void* val;
    unsigned long allocnumber;

  private:
    ALLOCINFO(); //no default ctor
    ALLOCINFO(const ALLOCINFO&); //no copy ctor
  } ;
typedef ALLOCINFO* ALLOCINFOP;


AllocNumberHandler.cpp

Synopsis
#include "allocnumberhandler.h"

AllocNumberHandler::AllocNumberHandler() 
: m_BreakAllocNumber(-1), m_lAllocNumber(0)
  {
  }
AllocNumberHandler::operator unsigned long ()
  {
  return m_lAllocNumber;
  }
void AllocNumberHandler::Increment()
  {
  m_lAllocNumber++;
  if (m_lAllocNumber == m_BreakAllocNumber)
    {
    _asm int 3;
    }
  }
void AllocNumberHandler::BreakOn(unsigned long num)
  {
  m_BreakAllocNumber = num;
  }

AllocNumberHandler.h

Synopsis
#pragma once

class AllocNumberHandler
  {
  public:
    AllocNumberHandler();
    operator unsigned long ();
    void Increment();
    void BreakOn(unsigned long num);
  private:
    unsigned long m_BreakAllocNumber;
    unsigned long m_lAllocNumber;    //the current allocation number
  } ;

ArenaHeader.cpp

Synopsis
#include <crtdbg.h>
#include "arenaheader.h"
const unsigned long SIGNATURE = 0x1BADABBAL;

//------------------------------------------------------------------------
// write signature and alloc size
void ArenaHeader::Set(void *ptr, unsigned long dwAllocSize)
  {
  if (ptr == 0) return;
  ArenaHeader& arena = *((ArenaHeader *)ptr);
  arena.m_dwSignature = SIGNATURE;
  arena.m_dwAllocSize = dwAllocSize;
  }

// helper function to verify and return the prepended 
// header (or null if failure)
ArenaHeader* ArenaHeader::Get(void *ptr)
  {
  ArenaHeader *result = 0;
  if (ptr)
    {
    result = (ArenaHeader *)((char*)ptr - Size());
    _ASSERTE(result->m_dwSignature == SIGNATURE && "The heap is corrupted");
    }
  return result;
  }

unsigned long ArenaHeader::Size()
  {
  return sizeof(ArenaHeader);
  }

ArenaHeader.h

Synopsis
#pragma once

struct ArenaHeader
  {
  static void Set(void *ptr, unsigned long dwAllocSize);
  static ArenaHeader* Get(void *ptr);
  static unsigned long Size();
  unsigned long m_dwAllocSize;

  private:
    unsigned long m_dwSignature;
  };

ComHeapRegistry.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
#include "comheapregistry.h"

static const TCHAR* const HeapKey = _T("jComHeap");
static const TCHAR* const DebugKey = _T("SOFTWARE\\arrizza\\Debug");

class HDRegistry::Private
  {
  public:
    HKEY hKey;
    DWORD dwCount;
    DWORD dwValue;
  } ;

HDRegistry::HDRegistry() : impl(* new HDRegistry::Private())
  {
  Init();
  }
HDRegistry::~HDRegistry()
  {
  RegCloseKey(impl.hKey);
  delete &impl;
  }
int HDRegistry::GetOption()
  {
  impl.dwCount = sizeof(DWORD);
  DWORD dwType = 0;
  impl.dwValue = 0;
  LONG rc = RegQueryValueEx(impl.hKey,
    HeapKey,
    0,
    &dwType,
    (PBYTE)&impl.dwValue,
    &impl.dwCount);
  if (rc != ERROR_SUCCESS)
    SetOptionKey();
  return (int) impl.dwValue;
  }
void HDRegistry::Init()
  {
  impl.dwCount = 0;
  LONG rc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, 
    DebugKey,
    0,
    0,
    REG_OPTION_NON_VOLATILE, 
    KEY_WRITE|KEY_READ,
    0,
    &impl.hKey,
    &impl.dwCount);
  _ASSERTE(rc == ERROR_SUCCESS);
  }
void HDRegistry::SetOptionKey()
  {
  impl.dwValue = 0;
  //trace mask is not there so set up a default value of 0
  LONG rc = RegSetValueEx(impl.hKey,
    HeapKey,
    NULL,
    REG_DWORD,
    (LPBYTE)&impl.dwValue, 
    sizeof(impl.dwValue));
  _ASSERTE(rc == ERROR_SUCCESS);
  }

ComHeapRegistry.h

Synopsis
#pragma once

class HDRegistry
  {
  public:
    HDRegistry();
    ~HDRegistry();
    int GetOption();
  
  private:
    void Init();
    void SetOptionKey();

  private:
    class Private;
    Private& impl;
  } ;

comheapspy.cpp

Synopsis
#include <crtdbg.h>
#pragma warning(disable: 4786) //identifier was truncated to '255' characters in the debug info

#include "comheapspy.h"
#include "allocinfo.h"
#include "common.h"
#include "reporter.h"
#include "arenaheader.h"
#include "allocnumberhandler.h"

class ComHeapSpy::Private
  {
  public:
    Private(bool sumonly)
      {
      Reset();
      mReporter.SummaryOnly(sumonly);
      }
    AllocNumberHandler AllocNumber; //a property??
    void Free(void* p)
      {
      mReporter.ReportFree(mMap.FreeRequest(p));
      }
    void Alloc(void* pActual, void* pAdjusted)
      {
      AllocNumber.Increment();
      mReporter.ReportAlloc(mMap.Add(m_cbLastAlloc, pAdjusted, AllocNumber), pActual);
      }
    void Realloc(void* pOriginal, void* pActual, void* pAdjusted)
      {
      Free(pOriginal);
      AllocNumber.Increment();
      mReporter.ReportRealloc(mMap.Add(m_cbLastAlloc, pAdjusted, AllocNumber), pActual, m_pvLastRealloc);
      }
    void Reset()
      {
      m_cbLastAlloc = 0;
      m_pvLastRealloc = 0; 
      mMap.ClearAll();
      }
    void Report(const char* fname, long lineno)
      {
      mReporter.Set(fname, lineno);
      mReporter.ReportOn(mMap);
      }
    void Summary(const char* fname, long lineno)
      {
      mReporter.Set(fname, lineno);
      mReporter.SummaryOf(mMap);
      }

    unsigned long  m_cbLastAlloc;     //size of last allocation
    void* m_pvLastAlloc;   //pointer to last realloced memory
    void* m_pvLastRealloc;   //pointer to last realloced memory

  private:
    Reporter mReporter;
    AllocationMap mMap;
    bool  m_summaryonly;     //flag indicates whether to print individual allocs/frees
  } ;

//------------------------------------------------------------------------
// initialize data members
ComHeapSpy::ComHeapSpy(bool sumonly)
: impl(* new ComHeapSpy::Private(sumonly))
  {
  Reset();
  }

//------------------------------------------------------------------------
ComHeapSpy::~ComHeapSpy()
  {
  delete &impl;
  }
void ComHeapSpy::BreakOnAllocNumber(unsigned long num)
  {
  impl.AllocNumber.BreakOn(num);
  }
//------------------------------------------------------------------------
HRESULT ComHeapSpy::Revoke()
  {
  return ::CoRevokeMallocSpy();
  }
//------------------------------------------------------------------------
HRESULT ComHeapSpy::Register()
  {
  return ::CoRegisterMallocSpy(this);
  }
//------------------------------------------------------------------------
void ComHeapSpy::Reset()
  {
  impl.Reset();
  }

//------------------------------------------------------------------------
//-- report any leaks
void ComHeapSpy::Report(const char* fname, long lineno)
  {
  impl.Report(fname, lineno);
  }

//------------------------------------------------------------------------
void ComHeapSpy::Summary(const char* fname, long lineno)
  {
  impl.Summary(fname, lineno);
  }

// IUnknown Methods ////////////////////

//------------------------------------------------------------------------
STDMETHODIMP ComHeapSpy::QueryInterface(REFIID riid, void**ppv)
  {
  if (riid == IID_IUnknown || riid == IID_IMallocSpy)
    LPUNKNOWN(*ppv = (IMallocSpy*)this)->AddRef();
  else
    *ppv = 0;
  return ResultFromScode(*ppv ? S_OK : E_NOINTERFACE);
  }

//------------------------------------------------------------------------
// this object is global and doesn't hold server, so punt
STDMETHODIMP_(ULONG) ComHeapSpy::AddRef()
  {
  return 1; 
  }

//------------------------------------------------------------------------
STDMETHODIMP_(ULONG) ComHeapSpy::Release()
  {
  return 0;
  }

// IMallocSpy Methods //////////////////

//------------------------------------------------------------------------
// PreAlloc reserves space for our arena header
//
STDMETHODIMP_(ULONG) ComHeapSpy::PreAlloc(ULONG cbRequest)
  {
  impl.m_cbLastAlloc = cbRequest;
  return cbRequest + ArenaHeader::Size();
  }

//------------------------------------------------------------------------
// PostAlloc writes the arena header and updates the alloc count
STDMETHODIMP_(void*) ComHeapSpy::PostAlloc(void *pActual)
  {
  LPBYTE pAdjusted = LPBYTE(pActual);
  if (pActual) // alloc succeeded
    {
    // write arena header 
    ArenaHeader::Set(pActual, impl.m_cbLastAlloc);
    pAdjusted += ArenaHeader::Size();

    impl.Alloc(pActual, pAdjusted);
    }

  return pAdjusted;
  }

//------------------------------------------------------------------------
// PreFree verifies the header, and adjusts the alloc count
STDMETHODIMP_(void*) ComHeapSpy::PreFree(void *pRequest, BOOL fSpyed)
  {
  LPBYTE pAdjusted = LPBYTE(pRequest);

  if (pRequest && fSpyed) // it is definitely ours
    {
    ArenaHeader *phdr = ArenaHeader::Get(pRequest);
    pAdjusted -= ArenaHeader::Size();

    impl.Free(pRequest);
    }
  return pAdjusted;
  }

//------------------------------------------------------------------------
// PostFree gets called after the task allocator overwrites
// our block, so there is very little we can do
STDMETHODIMP_(void) ComHeapSpy::PostFree(BOOL fSpyed)
  {
  }

//------------------------------------------------------------------------
// PreRealloc must verify the header, subtract the old size 
// from the alloc count, and cache the arguments for PostRealloc
STDMETHODIMP_(ULONG) ComHeapSpy::PreRealloc(void *pRequest,  ULONG cbRequest, 
                                            void **ppNewRequest, BOOL fSpyed)
  {
  LPBYTE pNewRequest = LPBYTE(pRequest);

  if (fSpyed)  // it is ours
    {
    // cache the pointer for PostRealloc
    impl.m_pvLastRealloc = pRequest;
    impl.m_cbLastAlloc = cbRequest;
    cbRequest += ArenaHeader::Size();
    if (pRequest)  // genuine realloc
      pNewRequest -= ArenaHeader::Size();
    }
  *ppNewRequest = pNewRequest;
  return cbRequest;
  }

//------------------------------------------------------------------------
// Post realloc must adjust the alloc count and write the 
// new arena header if succeeded
STDMETHODIMP_(void*) ComHeapSpy::PostRealloc(void *pActual, BOOL fSpyed)
  {
  LPBYTE pAdjusted = LPBYTE(pActual);
  if (!fSpyed) return pAdjusted;
  if (pActual == 0) return pAdjusted;
  ArenaHeader::Set(pActual, impl.m_cbLastAlloc);
  pAdjusted += ArenaHeader::Size();

  impl.Realloc(impl.m_pvLastRealloc, pActual, pAdjusted);
  return pAdjusted;
  }

//------------------------------------------------------------------------
// PreGetSize simply needs to shear off and verify the header
STDMETHODIMP_(void*) ComHeapSpy::PreGetSize(void *pRequest, BOOL fSpyed)
  {
  LPBYTE pAdjusted = LPBYTE(pRequest);
  if (fSpyed && pRequest && ArenaHeader::Get(pRequest)) // it is ours and valid
    pAdjusted -= ArenaHeader::Size();  
  return pAdjusted;
  }

//------------------------------------------------------------------------
// PostGetSize adjusts the size reported by ArenaHeader::Size()
STDMETHODIMP_(ULONG) ComHeapSpy::PostGetSize(ULONG cbActual, BOOL fSpyed)
  {
  return fSpyed ? (cbActual - ArenaHeader::Size()) : cbActual;
  }

//------------------------------------------------------------------------
// PreDidAlloc simply needs to shear off and verify the header
STDMETHODIMP_(void*) ComHeapSpy::PreDidAlloc(void *pRequest, BOOL fSpyed)
  {
  LPBYTE pAdjusted = LPBYTE(pRequest);
  if (fSpyed && pRequest && ArenaHeader::Get(pRequest)) // it is ours and valid
    pAdjusted -= ArenaHeader::Size();  // adjust pAdjusted pointer
  return pAdjusted;
  }

//------------------------------------------------------------------------
STDMETHODIMP_(int) ComHeapSpy::PostDidAlloc(void *pRequest, BOOL fSpyed, int fActual)
  {
  return fActual;
  }

//------------------------------------------------------------------------
STDMETHODIMP_(void) ComHeapSpy::PreHeapMinimize(void)
  {
  }

//------------------------------------------------------------------------
STDMETHODIMP_(void) ComHeapSpy::PostHeapMinimize(void)
  {
  }

comheapspy.h

Synopsis
#pragma once

#include <comdef.h>

//----------------------------------------------------------------------
//-- the extension of the IMallocSpy callback
class ComHeapSpy : public IMallocSpy
  {                   
  public:
    virtual ~ComHeapSpy();
    ComHeapSpy(bool sum);

    HRESULT Register();
    HRESULT Revoke();
    void  Reset();

    void BreakOnAllocNumber(unsigned long num);
    void  Report(const char* fname, long lineno);
    void  Summary(const char* fname, long lineno);

  private:
    ComHeapSpy(); //no default ctor
    ComHeapSpy(const ComHeapSpy&); //no copy
    ComHeapSpy& operator=(const ComHeapSpy&); //no assignment

    class Private;
    Private& impl;
    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void**ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IMallocSpy methods
    STDMETHODIMP_(ULONG) PreAlloc(ULONG cbRequest);
    STDMETHODIMP_(void*) PostAlloc(void *pActual);

    STDMETHODIMP_(void*) PreFree(void *pRequest, BOOL fSpyed);
    STDMETHODIMP_(void)  PostFree(BOOL fSpyed);

    STDMETHODIMP_(ULONG) PreRealloc(void *pRequest, ULONG cbRequest, void **ppNewRequest, BOOL fSpyed);
    STDMETHODIMP_(void*) PostRealloc(void *pActual, BOOL fSpyed);

    STDMETHODIMP_(void*) PreGetSize(void *pRequest, BOOL fSpyed);
    STDMETHODIMP_(ULONG) PostGetSize(ULONG cbActual, BOOL fSpyed);

    STDMETHODIMP_(void*) PreDidAlloc(void *pRequest, BOOL fSpyed);
    STDMETHODIMP_(int)   PostDidAlloc(void *pRequest, BOOL fSpyed, int fActual);

    STDMETHODIMP_(void)  PreHeapMinimize(void);
    STDMETHODIMP_(void)  PostHeapMinimize(void);
  };

common.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <crtdbg.h>
#include <tchar.h>
#include <stdio.h>
#include "common.h"

//----------------------------------------------------------------------
//-- use this new to get all comheap allocations into the private heap
//-- typical call is new (0,0) xxx
//-- putting all heapdet. memory onto a private heap keeps it from showing up
//-- on the crtdbg reports
static HANDLE _ghHeap = 0;

void* operator new(size_t size, void*, int)
  {
  if (_ghHeap == 0)
    {
    _ghHeap = HeapCreate(0, 65535, 0);
    _ASSERTE(_ghHeap);
    }

  _ASSERTE(size >= 0);
  if (size < 0)
    size = 0;
  return HeapAlloc(_ghHeap, 0, size);
  }

//----------------------------------------------------------------------
//-- frees the memory 
void destroymem(void *p)
  {
  if (!p) return;
  _ASSERTE(_ghHeap);
  BOOL wrc = HeapFree(_ghHeap, 0, p);
  _ASSERTE(wrc);
  }


//----------------------------------------------------------------------
//-- the common trace line facility
void HeapTrace(LPCTSTR lpszFormat, ...)
  {
  static int count = 0;

  va_list args;
  va_start(args, lpszFormat);

  TCHAR szBuffer[2048];
  int nBuf = _vstprintf(szBuffer, lpszFormat, args);
  _ASSERTE(nBuf < sizeof(szBuffer)/sizeof(TCHAR));

  OutputDebugString(szBuffer);
  va_end(args);

  //OutputDebugString has trouble with large volumes of lines
  //doing a context switch every so often seems to help
  count++;
  if (count > 100)
    {
    Sleep(1);  
    count = 0;
    }
  }


common.h

Synopsis
#pragma once

#include <tchar.h>

//----------------------------------------------------------------------
//-- use this new to get all comheap allocations into the private heap
//-- typical call is new (0,0) xxx
//-- putting all heapdet. memory onto a private heap keeps it from showing up
//-- on the crtdbg reports
void* operator new(size_t size, void*, int);
void destroymem(void *p);

//----------------------------------------------------------------------
//-- the common trace line facility
void HeapTrace(const TCHAR* const lpszFormat, ...);

jcomheap.cpp

Synopsis
//////////////////////////////////////////////////////
// 
// HeapDet.cpp - Copyright 1995, Don Box 
// modifications and additions by John Arrizza
//
// Simple IMallocSpy to track allocation byte count
#include <crtdbg.h>
#include "jcomheap.h"
#include "comheapregistry.h"
#include "common.h"
#include "comheapspy.h"
#pragma warning(disable: 4291) //no matching operator delete found

//------------------------------------------------------------------------
class jComHeap::CHState
  {
  public:
    bool traceit;
    ComHeapSpy* spy;

    CHState() : traceit(false), sumonly(false), spy(0)
      {
      }
    ~CHState()
      {
      if (spy == 0) return;
      if (spy->Revoke() == S_OK)
        {
        spy->~ComHeapSpy();
        destroymem(spy);
        }
      spy = 0;
      }
    void Init()
      {
      GetOptionsFromRegistry();
      if (!traceit) return;
      // declare a spy object and register it
      if (!spy)
        CreateCoComHeap();
      spy->Reset();
      }
  private:
    bool sumonly;

    void GetOptionsFromRegistry()
      {
      traceit = false;    //be pessimistic
      sumonly = false;

#ifdef _DEBUG
      HDRegistry reg;

      DWORD val = reg.GetOption();
      if (val == 0) 
        return;
      traceit = true;
      if (val == 1)
        sumonly = true;
#endif
      }

    void CreateCoComHeap()
      {
      spy = new (0, 0) ComHeapSpy(sumonly);
      _ASSERTE(spy);
      spy->Register();
      }
  } ;

//------------------------------------------------------------------------
jComHeap::jComHeap()
: m_state(* new (0,0) jComHeap::CHState)
  {
  }

//------------------------------------------------------------------------
jComHeap::~jComHeap()
  {
  m_state.~CHState();
  }

//------------------------------------------------------------------------
void jComHeap::Init()
  {
  m_state.Init();
  }

//------------------------------------------------------------------------
void jComHeap::Report(const char* fname, long lineno)
  {
  if (!m_state.traceit) return;
  _ASSERTE(m_state.spy);
  m_state.spy->Report(fname, lineno);
  }

//------------------------------------------------------------------------
void jComHeap::Summary(const char* fname, long lineno)
  {
  if (!m_state.traceit) return;
  _ASSERTE(m_state.spy);
  m_state.spy->Summary(fname, lineno);
  }

//------------------------------------------------------------------------
void jComHeap::Clear()
  {
  if (!m_state.traceit) return;
  _ASSERTE(m_state.spy);
  m_state.spy->Reset();
  }

void jComHeap::BreakOnAllocNumber(long num)
  {
  _ASSERTE(m_state.spy);
  m_state.spy->BreakOnAllocNumber(num);
  }

jcomheap.h

Synopsis
//////////////////////////////////////////////////////
// Original code Copyright 1995, Don Box 
// modified by John Arrizza
#pragma once

#pragma comment(lib, "jComHeap.lib")

class jComHeap
  {
  public:
    jComHeap();
    ~jComHeap();

    void Init();
    void Report(const char *fname, long lineno);  //all outstanding mem allocs
    void Summary(const char *fname, long lineno); //just the totals
    void Clear();
    void BreakOnAllocNumber(long num);

  private:
    jComHeap& operator=(const jComHeap& ); //not allowed
    jComHeap(const jComHeap&); //not allowed
    class CHState;
    CHState& m_state;
  } ;

reporter.cpp

Synopsis
#pragma warning(disable: 4786) //identifier was truncated to '255' characters in the debug info
#include <algorithm>
using std::sort;
#include "common.h"
#include "reporter.h"
#include "allocationmap.h"

Reporter::Reporter()
  {
  mSummaryOnly = false;
  }
void Reporter::Set(const char* f, long l)
  {
  fname = f;
  lineno = l;
  }
void Reporter::SummaryOnly(bool sumonly)
  {
  mSummaryOnly = sumonly;
  }
void Reporter::ReportOn(AllocationMap& H)
  {
  IterateOver(H);
  WritePrefix();
  if (mStats.IsNoLeaks())
    {
    WriteNoLeaksFound();
    return;
    }

  WriteLeakMessage();
  ReportLeaks(H);
  HeapTrace(_T("jComHeap: Report -------- Done\n"));
  }

void Reporter::SummaryOf(AllocationMap& H)
  {
  IterateOver(H);

  WritePrefix();
  if (mStats.IsNoLeaks())
    WriteNoLeaksFound();
  else
    WriteLeakMessage();
  }

void Reporter::WritePrefix()
  {
  HeapTrace(_T("%s(%ld) : jComHeap: "), fname, lineno);
  }
void Reporter::WriteNoLeaksFound(bool writetotals)
  {
  HeapTrace(_T("no leaks found. "));
  if (writetotals)
    mStats.Report();
  HeapTrace(_T("\n"));
  }
void Reporter::WriteLeakMessage()
  {
  HeapTrace(_T("LEAKS! ")); 
  mStats.Report();
  HeapTrace(_T("\n"));
  }

void Reporter::CopyTo(AllocationMap& H, VEC& v)
  {
  for(AllocationMap::iterator p = H.begin(); p != H.end(); p++)
    v.push_back((*p).second);
  }
void Reporter::Sort(VEC& v)
  {
  //sort the vector by allocation number
  sort(v.begin(), v.end(), local_less());
  }
void Reporter::ReportLeaks(AllocationMap& H)
  {
  //since the map can't be sorted, create a vector from the map
  VEC v;
  CopyTo(H, v);
  Sort(v);
  IterateOver(v);
  }

void Reporter::IterateOver(VEC& v)
  {
  mStats.Reset();
  for(VEC::iterator it = v.begin(); it != v.end(); ++it)
    {
    VEC::value_type ai = *it;
    if (!ai) continue;
    if(ai->inuse)
      ReportUnfreedBlock(ai);
    mStats.BumpTotals(ai);
    }
  }
void Reporter::IterateOver(AllocationMap& H)
  {
  mStats.Reset();
  for(AllocationMap::iterator it = H.begin(); it != H.end(); ++it)
    {
    ALLOCINFOP ai =(*it).second;
    if(!ai) continue;
    if(ai->inuse)
      mStats.BumpUnfreed(ai);
    mStats.BumpTotals(ai);
    }
  }

void Reporter::ReportUnfreedBlock(ALLOCINFOP ai)
  {
  HeapTrace(_T("-----  [%6ld] alloc: 0x%p size=%ld bytes \n"),
	  (long) ai->allocnumber, ai->val, ai->size);

  TCHAR widebuf[200] = _T("");
  TCHAR bytebuf[200] = _T("");
  long bstrlen = 0;
  PrintToBuffers(ai, bytebuf, widebuf, bstrlen);

  HeapTrace(_T("-----    as Data: %s\n"), bytebuf);
  HeapTrace(_T("-----    as bstr: len=%-4ld bytes <%s>\n"), bstrlen, widebuf);

  mStats.BumpUnfreed(ai);
  }

void Reporter::PrintToBuffers(ALLOCINFOP ai, TCHAR* bytebuf, TCHAR* widebuf, long& bstrlen)
  {
  char* b;
  unsigned int   i;

  //save up to 160 bytes of info into the buffers
  for(i = 0, b = (char*)ai->val; i < 160 && i < 1 + ai->size; i++, b++)
    {
    //save the memory as raw data
    if(i < 30)
      _stprintf(&bytebuf[_tcslen(bytebuf)], _T("%02.2X "), (*b) & 0xFF); 

    //save the memory as wide chars
    if(i >= 4  && i % 2 == 0 && i < (ai->size + 1 - 4))
      _stprintf(&widebuf[_tcslen(widebuf)], _T("%C"), _istprint(*b) ? *b : 0x0001); 
    }

  bstrlen = *((long *)ai->val);
  }

void Reporter::ReportAlloc(ALLOCINFOP ai, void* pActual)
  {
  if (mSummaryOnly) return;
  HeapTrace(_T("jComHeap:: Alloc  {%6ld} %s at 0X%p, %5u bytes\n"), 
    (long) ai->allocnumber, (pActual != 0 ? _T("succeeded") : _T("failed")),
    ai->val, ai->size);
  }
void Reporter::ReportRealloc(ALLOCINFOP ai, void* pActual, void* pOriginal)
  {
  if (mSummaryOnly) return;
  HeapTrace(_T("jComHeap:: Realloc{%6ld} %s at 0X%p, %5u bytes (old ptr 0X%p)\n"), 
    (long) ai->allocnumber,
    (pActual != 0 ? _T("succeeded") : _T("failed")), 
    ai->val, ai->size,
    pOriginal);
  }
void Reporter::ReportFree(ALLOCINFOP ai)
  {
  if (mSummaryOnly) return;
  HeapTrace(_T("jComHeap:: Free   {%6ld} succeeded at 0X%p, %5u bytes\n"), 
    (long) ai->allocnumber,
    ai->val, ai->size);
  }

Reporter.h

Synopsis
#pragma once

#include <functional>
#include <vector>
using std::vector;
using std::binary_function;
#include <tchar.h>

#include "allocinfo.h"
#include "allocationmap.h"
#include "stats.h"

class Reporter
  {
  public:
    Reporter();
    void Set(const char* f, long l);
    void SummaryOnly(bool sumonly);
    void ReportOn(AllocationMap& H);
    void SummaryOf(AllocationMap& H);
    void ReportAlloc(ALLOCINFOP ai, void* pActual);
    void ReportRealloc(ALLOCINFOP ai, void* pActual, void* pOriginal);
    void ReportFree(ALLOCINFOP ai);

  private:
    typedef vector<ALLOCINFOP, MyAllocator> VEC;
    struct local_less : public binary_function<ALLOCINFOP,ALLOCINFOP,bool>
      {
      bool operator()(const ALLOCINFOP __x, const ALLOCINFOP __y) const { return __x->allocnumber < __y->allocnumber; }
      };

    void WritePrefix();
    void WriteNoLeaksFound(bool writetotals = false);
    void WriteLeakMessage();
    void CopyTo(AllocationMap& H, VEC& v);
    void Sort(VEC& v);
    void ReportLeaks(AllocationMap& H);
    void IterateOver(VEC& v);
    void IterateOver(AllocationMap& H);
    void ReportUnfreedBlock(ALLOCINFOP ai);
    void PrintToBuffers(ALLOCINFOP ai, TCHAR* bytebuf, TCHAR* widebuf, long& bstrlen);

    bool mSummaryOnly;
    const char* fname;
    long lineno;
    Stats mStats;
  } ;


stats.cpp

Synopsis
#include "stats.h"
#include "common.h"

void Stats::Report()
  {
  HeapTrace(_T("Total %ld bytes unfreed in %ld allocation(s)"),
    total, unfreedcount);
  }
bool Stats::IsNoLeaks()
  {
  return totalallocs == totalfrees;
  }
void Stats::Reset()
  {
  total        = 0;
  unfreedcount = 0;
  totalallocs  = 0;
  totalfrees   = 0;
  }
void Stats::BumpTotals(ALLOCINFOP ai)
  {
  totalallocs += ai->numallocs;      
  totalfrees  += ai->numfrees;      
  }

void Stats::BumpUnfreed(ALLOCINFOP ai)
  {
  total += ai->size;
  unfreedcount++;
  }

Stats.h

Synopsis
#pragma once

#include "allocinfo.h"

class Stats
  {
  public:
    long total;
    long unfreedcount;
    long totalallocs;
    long totalfrees;

    void Report();
    bool IsNoLeaks();
    void Reset();
    void BumpTotals(ALLOCINFOP ai);
    void BumpUnfreed(ALLOCINFOP ai);
  };






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010