#include "hostenv.h" #include "cmallspy.h" #include "apglobal.h" #define cbAlign 32 #define HEADERSIZE cbAlign // # of bytes of block header #define TRAILERSIZE cbAlign // # of bytes of block trailer static XCHAR g_rgchHead[] = XSTR("OLEAuto Mem Head"); // beginning of block signature static XCHAR g_rgchTail[] = XSTR("OLEAuto Mem Tail"); // end of block signature #define MEMCMP(PV1, PV2, CB) memcmp((PV1), (PV2), (CB)) #define MEMCPY(PV1, PV2, CB) memcpy((PV1), (PV2), (CB)) #define MEMSET(PV, VAL, CB) memset((PV), (VAL), (CB)) #define MALLOC(CB) GlobalLock(GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, CB)) CMallocSpy myMallocSpy; UINT g_cHeapCheckInterval = 10; // only check full heap every 100 times. //--------------------------------------------------------------------- // implementation of the debug allocator //--------------------------------------------------------------------- CAddrNode32 FAR* CAddrNode32::m_pnFreeList = NULL; // AddrNodes are allocated in blocks to reduce the number of allocations // we do for these. Note, we get away with this because the addr nodes // are never freed, so we can just allocate a block, and thread them // onto the freelist. // #define MEM_cAddrNodes 128 void FAR* CAddrNode32::operator new(size_t /*cb*/) { CAddrNode32 FAR* pn; if(m_pnFreeList == NULL) { pn = (CAddrNode32 FAR*)MALLOC(sizeof(CAddrNode32) * MEM_cAddrNodes); for(int i = 1; i < MEM_cAddrNodes-1; ++i) pn[i].m_pnNext = &pn[i+1]; pn[MEM_cAddrNodes-1].m_pnNext = m_pnFreeList; m_pnFreeList = &pn[1]; } else { pn = m_pnFreeList; m_pnFreeList = pn->m_pnNext; } return pn; } void CAddrNode32::operator delete(void FAR* pv) { CAddrNode32 FAR *pn; pn = (CAddrNode32 FAR*)pv; pn->m_pnNext = m_pnFreeList; m_pnFreeList = pn; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::CMallocSpy // // Synopsis: Constructor // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- CMallocSpy::CMallocSpy(void) { m_cRef = 0; m_fWantTrueSize = FALSE; m_cAllocCalls = 0; m_cHeapChecks = 0; MEMSET(m_rganode, 0, sizeof(m_rganode)); } //+--------------------------------------------------------------------- // // Member: CMallocSpy::~CMallocSpy // // Synopsis: Destructor // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- CMallocSpy::~CMallocSpy(void) { CheckForLeaks(); } //+--------------------------------------------------------------------- // // Member: CMallocSpy::QueryInterface // // Synopsis: Only IUnknown and IMallocSpy are meaningful // // Arguments: [riid] -- // [ppUnk] -- // // Returns: S_OK or E_NOINTERFACE // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- HRESULT CMallocSpy::QueryInterface(REFIID riid, LPVOID *ppUnk) { HRESULT hr = S_OK; if (IsEqualIID(riid, IID_IUnknown)) { *ppUnk = (IUnknown *) this; } else if (IsEqualIID(riid, IID_IMallocSpy)) { *ppUnk = (IMalloc *) this; } else { *ppUnk = NULL; return E_NOINTERFACE; } AddRef(); return hr; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::AddRef // // Synopsis: Add a reference // // Returns: New reference count // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- ULONG CMallocSpy::AddRef(void) { return ++m_cRef; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::Release // // Synopsis: Remove a reference // // Returns: The new reference count // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- ULONG CMallocSpy::Release(void) { ULONG cRef; cRef = --m_cRef; if (cRef == 0) { #if 0 // don't delete -- we're statically allocated delete this; #endif } return cRef; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreAlloc // // Synopsis: Called prior to OLE calling IMalloc::Alloc // // Arguments: [cbRequest] -- The number of bytes the caller of // is requesting IMalloc::Alloc // // Returns: The count of bytes to actually allocate // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- SIZE_T CMallocSpy::PreAlloc(SIZE_T cbRequest) { HeapCheck(); return cbRequest + HEADERSIZE + TRAILERSIZE; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PostAlloc // // Synopsis: Called after OLE calls IMalloc::Alloc // // Arguments: [pActual] -- The allocation returned by IMalloc::Alloc // // Returns: The allocation pointer to return to the caller of // IMalloc::Alloc // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void *CMallocSpy::PostAlloc(void *pActual) { IMalloc *pmalloc; SIZE_T cbRequest; HRESULT hresult; XCHAR sz[20]; if (pActual == NULL) // if real alloc failed, then return NULL; // propogate failure if (FAILED(hresult = CoGetMalloc(MEMCTX_TASK, &pmalloc))) { apSPrintf(sz, XSTR("%lX"), hresult); apLogFailInfo(XSTR("ERROR:CoGetMalloc failed!!!"), XSTR("NOEEROR"), sz, XSTR("")); return(NULL); } m_fWantTrueSize = TRUE; cbRequest = pmalloc->GetSize(pActual) - HEADERSIZE - TRAILERSIZE; m_fWantTrueSize = FALSE; pmalloc->Release(); // set header signature MEMCPY(pActual, g_rgchHead, HEADERSIZE); // set trailer signature MEMCPY((BYTE *)pActual+HEADERSIZE+cbRequest, g_rgchTail, TRAILERSIZE); // save info for leak detection AddInst((BYTE *)pActual+HEADERSIZE, cbRequest); // Return the allocation plus offset return (void *) (((BYTE *) pActual) + HEADERSIZE); } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreFree // // Synopsis: Called prior to OLE calling IMalloc::Free // // Arguments: [pRequest] -- The allocation to be freed // [fSpyed] -- Whether it was allocated with a spy active // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void *CMallocSpy::PreFree(void *pRequest, BOOL fSpyed) { HeapCheck(); if (pRequest == NULL) { return NULL; } // Undo the offset if (fSpyed) { CAddrNode32 FAR* pn; SIZE_T sizeToFree; pn = FindInst(pRequest); // check for attempt to operate on a pointer we didn't allocate if(pn == NULL) { apLogFailInfo(XSTR("Attempt to free memory not allocated by this 32-bit test!"), XSTR(""), XSTR(""), XSTR("")); } // check the block we're freeing VerifyHeaderTrailer(pn); sizeToFree = pn->m_cb + HEADERSIZE + TRAILERSIZE; DelInst(pRequest); // mark entire block as invalid MEMSET((BYTE *) pRequest - HEADERSIZE, '~', sizeToFree); return (void *) (((BYTE *) pRequest) - HEADERSIZE); } else { return pRequest; } } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PostFree // // Synopsis: Called after OLE calls IMalloc::Free // // Arguments: [fSpyed] -- Whether it was allocated with a spy active // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void CMallocSpy::PostFree(BOOL /*fSpyed*/) { return; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreRealloc // // Synopsis: Called prior to OLE calling IMalloc::Realloc // // Arguments: [pRequest] -- The buffer to be reallocated // [cbRequest] -- The requested new size of the buffer // [ppNewRequest] -- Where to store the new buffer pointer // to be reallocated // [fSpyed] -- Whether it was allocated with a spy active // // Returns: The new size to actually be allocated // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- SIZE_T CMallocSpy::PreRealloc(void *pRequest, SIZE_T cbRequest, void **ppNewRequest, BOOL fSpyed) { HeapCheck(); if (fSpyed) { CAddrNode32 FAR* pn; SIZE_T sizeToFree; pn = FindInst(pRequest); // check for attempt to operate on a pointer we didn't allocate if(pn == NULL) { apLogFailInfo(XSTR("Attempt to reallocate memory not allocated by this 32-bit test!"), XSTR(""), XSTR(""), XSTR("")); } sizeToFree = pn->m_cb; *ppNewRequest = (void *) (((BYTE *) pRequest) - HEADERSIZE); m_pvRealloc = pRequest; return cbRequest + HEADERSIZE + TRAILERSIZE; } else { *ppNewRequest = pRequest; return cbRequest; } } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PostRealloc // // Synopsis: Called after OLE calls IMalloc::Realloc // // Arguments: [pActual] -- Pointer to the reallocated buffer // [fSpyed] -- Whether it was allocated with a spy active // // Returns: The buffer pointer to return // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void *CMallocSpy::PostRealloc(void *pActual, BOOL fSpyed) { IMalloc *pmalloc; SIZE_T cbRequest; HRESULT hresult; XCHAR sz[50]; if (pActual == NULL) { apLogFailInfo(XSTR("CMallocSpy::PostRealloc - Realloc of a block failed."), XSTR(""), XSTR(""), XSTR("")); return NULL; } // Return the buffer with the header offset if (fSpyed) { DelInst(m_pvRealloc); if (FAILED(hresult = CoGetMalloc(MEMCTX_TASK, &pmalloc))) { apSPrintf(sz, XSTR("%lX"), hresult); apLogFailInfo(XSTR("ERROR:CoGetMalloc failed!!!"), XSTR("NOEEROR"), sz, XSTR("")); } m_fWantTrueSize = TRUE; cbRequest = pmalloc->GetSize(pActual) - HEADERSIZE - TRAILERSIZE; m_fWantTrueSize = FALSE; pmalloc->Release(); if (MEMCMP(pActual, g_rgchHead, HEADERSIZE) != 0) { MEMCPY(sz, pActual, HEADERSIZE); sz[HEADERSIZE] = 0; apLogFailInfo(XSTR("32-bit Memory header not intact!"), g_rgchHead, sz, XSTR("")); } // set new trailer signature MEMCPY((BYTE *)pActual+HEADERSIZE+cbRequest, g_rgchTail, TRAILERSIZE); // save info for leak detection AddInst((BYTE *)pActual+HEADERSIZE, cbRequest); return (void *) (((BYTE *) pActual) + HEADERSIZE); } else { return pActual; } } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreGetSize // // Synopsis: Called prior to OLE calling IMalloc::GetSize // // Arguments: [pRequest] -- The buffer whose size is to be returned // [fSpyed] -- Whether it was allocated with a spy active // // Returns: The actual buffer with which to call IMalloc::GetSize // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void *CMallocSpy::PreGetSize(void *pRequest, BOOL fSpyed) { HeapCheck(); if (fSpyed && !m_fWantTrueSize) { return (void *) (((BYTE *) pRequest) - HEADERSIZE); } else { return pRequest; } } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PostGetSize // // Synopsis: Called after OLE calls IMalloc::GetSize // // Arguments: [cbActual] -- The result of IMalloc::GetSize // [fSpyed] -- Whether it was allocated with a spy active // // Returns: The size to return to the IMalloc::GetSize caller // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- SIZE_T CMallocSpy::PostGetSize(SIZE_T cbActual, BOOL fSpyed) { if (fSpyed && !m_fWantTrueSize) { return cbActual - HEADERSIZE - TRAILERSIZE; } else { return cbActual; } } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreDidAlloc // // Synopsis: Called prior to OLE calling IMalloc::DidAlloc // // Arguments: [pRequest] -- The buffer whose allocation is being tested // [fSpyed] -- Whether it was allocated with a spy active // // Returns: The buffer whose allocation is actually to be tested // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void *CMallocSpy::PreDidAlloc(void *pRequest, BOOL fSpyed) { HeapCheck(); if (fSpyed) { return (void *) (((BYTE *) pRequest) - HEADERSIZE); } else { return pRequest; } } //+--------------------------------------------------------------------- // // Function: PostDidAlloc // // Synopsis: Called after OLE calls the IMalloc::DidAlloc // // Arguments: [pRequest] -- The passed allocation // [fSpyed] -- Whether it was allocated with a spy active // [fActual] -- The result of IMalloc::DidAlloc // // Returns: The result of IMalloc::DidAlloc // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- BOOL CMallocSpy::PostDidAlloc(void * /*pRequest*/, BOOL /*fSpyed*/, BOOL fActual) { return fActual; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PreHeapMinimize // // Synopsis: Called prior to OLE calling the IMalloc::HeapMinimize // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void CMallocSpy::PreHeapMinimize(void) { HeapCheck(); return; } //+--------------------------------------------------------------------- // // Member: CMallocSpy::PostHeapMinimize // // Synopsis: Called after OLE calls the IMalloc::HeapMinimize // // Returns: // // History: 24-Oct-94 Created. // // Notes: // //---------------------------------------------------------------------- void CMallocSpy::PostHeapMinimize(void) { return; } //--------------------------------------------------------------------- // Instance table methods //--------------------------------------------------------------------- VOID CMallocSpy::MemInstance() { ++m_cAllocCalls; } /*** *PRIVATE CMallocSpy::AddInst *Purpose: * Add the given instance to the address instance table. * *Entry: * pv = the instance to add * nAlloc = the allocation passcount of this instance * *Exit: * None * ***********************************************************************/ void CMallocSpy::AddInst(void FAR* pv, SIZE_T cb) { ULONG nAlloc; UINT hash; CAddrNode32 FAR* pn; MemInstance(); nAlloc = m_cAllocCalls; // DebAssert(pv != NULL, ""); pn = (CAddrNode32 FAR*)new FAR CAddrNode32(); // DebAssert(pn != NULL, ""); pn->m_pv = pv; pn->m_cb = cb; pn->m_nAlloc = nAlloc; hash = HashInst(pv); pn->m_pnNext = m_rganode[hash]; m_rganode[hash] = pn; } /*** *PRIVATE CMallocSpy::DelInst(void*) *Purpose: * Remove the given instance from the address instance table. * *Entry: * pv = the instance to remove * *Exit: * None * ***********************************************************************/ void CMallocSpy::DelInst(void FAR* pv) { CAddrNode32 FAR* FAR* ppn, FAR* pnDead; for(ppn = &m_rganode[HashInst(pv)]; *ppn != NULL; ppn = &(*ppn)->m_pnNext) { if((*ppn)->m_pv == pv) { pnDead = *ppn; *ppn = (*ppn)->m_pnNext; delete pnDead; return; } } // didnt find the instance // DebAssert(FALSE, "memory instance not found"); } CAddrNode32 FAR* CMallocSpy::FindInst(void FAR* pv) { CAddrNode32 FAR* pn; for(pn = m_rganode[HashInst(pv)]; pn != NULL; pn = pn->m_pnNext) { if(pn->m_pv == pv) return pn; } return NULL; } void CMallocSpy::DumpInst(CAddrNode32 FAR* pn) { XCHAR szActual[128]; apSPrintf(szActual, XSTR("Block of %ld bytes leaked in test"), pn->m_cb); apLogFailInfo(XSTR("Memory leaked on release of 32-bit test allocator!"), XSTR("no leak"), szActual, XSTR("")); // Printf("[%lp] nAlloc=0x%lx size=0x%lx\n", pn->m_pv, pn->m_nAlloc, pn->m_cb); } /*** *PRIVATE BOOL IsEmpty *Purpose: * Answer if the address instance table is empty. * *Entry: * None * *Exit: * return value = BOOL, TRUE if empty, FALSE otherwise * ***********************************************************************/ BOOL CMallocSpy::IsEmpty() { UINT u; for(u = 0; u < DIM(m_rganode); ++u) { if(m_rganode[u] != NULL) return FALSE; // something leaked } return TRUE; } /*** *PRIVATE CMallocSpy::DumpInstTable() *Purpose: * Print the current contents of the address instance table, * *Entry: * None * *Exit: * None * ***********************************************************************/ void CMallocSpy::DumpInstTable() { UINT u; CAddrNode32 FAR* pn; for(u = 0; u < DIM(m_rganode); ++u) { for(pn = m_rganode[u]; pn != NULL; pn = pn->m_pnNext) { VerifyHeaderTrailer(pn); DumpInst(pn); } } } /*** *PRIVATE void CMallocSpy::VerifyHeaderTrailer() *Purpose: * Inspect allocations for signature overwrites. * *Entry: * None * *Exit: * return value = None. * ***********************************************************************/ VOID CMallocSpy::VerifyHeaderTrailer(CAddrNode32 FAR* pn) { XCHAR sz[50]; XCHAR sz2[100]; if (MEMCMP((char FAR*)pn->m_pv + pn->m_cb, g_rgchTail, TRAILERSIZE) != 0) { // DumpInst(pn); MEMCPY(sz, (char FAR*)pn->m_pv + pn->m_cb, TRAILERSIZE); sz[TRAILERSIZE] = 0; apSPrintf(sz2, XSTR("32-bit memory trailer corrupt on alloc of %ld bytes"), pn->m_cb); apLogFailInfo(sz2, g_rgchTail, sz, XSTR("")); apEndTest(); } if (MEMCMP((char FAR*)pn->m_pv - HEADERSIZE, g_rgchHead, HEADERSIZE) != 0) { // DumpInst(pn); MEMCPY(sz, (char FAR*)pn->m_pv - HEADERSIZE, HEADERSIZE); sz[HEADERSIZE] = 0; apSPrintf(sz2, XSTR("32-bit memory header corrupt on alloc of %ld bytes"), pn->m_cb); apLogFailInfo(sz2, g_rgchHead, sz, XSTR("")); apEndTest(); } } /*** *PRIVATE void CMallocSpy::HeapCheck() *Purpose: * Inspect allocations for signature overwrites. * *Entry: * None * *Exit: * return value = None. * ***********************************************************************/ VOID CMallocSpy::HeapCheck() { UINT u; CAddrNode32 FAR* pn; if (m_cHeapChecks++ < g_cHeapCheckInterval) { return; } m_cHeapChecks = 0; // reset for (u = 0; u < DIM(m_rganode); ++u) { for (pn = m_rganode[u]; pn != NULL; pn = pn->m_pnNext) { VerifyHeaderTrailer(pn); } } } void CMallocSpy::CheckForLeaks() { if (!IsEmpty()) { DumpInstTable(); apEndTest(); // make sure a failure get recorded } } //--------------------------------------------------------------------- // Helper routines //--------------------------------------------------------------------- STDAPI GetMallocSpy(IMallocSpy FAR* FAR* ppmallocSpy) { *ppmallocSpy = &myMallocSpy; return NOERROR; }