968 lines
29 KiB
C++
968 lines
29 KiB
C++
//=--------------------------------------------------------------------------=
|
||
// Macros.Cpp
|
||
//=--------------------------------------------------------------------------=
|
||
// Copyright 1997 Microsoft Corporation. All Rights Reserved.
|
||
//
|
||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||
// PARTICULAR PURPOSE.
|
||
//=--------------------------------------------------------------------------=
|
||
// Handy macros like the ones we use in the VB code base.
|
||
//=--------------------------------------------------------------------------=
|
||
#include "pch.h"
|
||
|
||
#ifdef DEBUG
|
||
#include <winuser.h>
|
||
|
||
// for ASSERT and FAIL
|
||
//
|
||
SZTHISFILE
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// Debug control switches
|
||
//=--------------------------------------------------------------------------=
|
||
DEFINE_SWITCH(fTraceCtlAllocs); // Trace all Heap allocations and frees
|
||
// fOutputFile should also be on with this switch
|
||
DEFINE_SWITCH(fOutputFile); // Logs all debug info in file:
|
||
// %CurrentDir%\ctldebug.log
|
||
DEFINE_SWITCH(fNoLeakAsserts); // No Heap memory leak asserts are displayed
|
||
// when turned on.
|
||
|
||
|
||
|
||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
// !DEBUGGING HEAP MEMORY LEAKS!
|
||
// To debug a leak you need to figure out where and when the allocation was made.
|
||
// The top of the assert dialog will give you the OCX/DLL causing the leak.
|
||
// Goto Project/Build...Settings.
|
||
// On the Debug tab, select "additional DLLs"
|
||
// Locate and select the OCX/DLL causing the leak.
|
||
// Put a breakpoint on the noted line below.
|
||
// Goto Edit...Breakpoints.
|
||
// Select the new breakpoint.
|
||
// Press 'Condition'
|
||
// In the 'Enter number of times to skip before breaking' put the value of nAlloc-1.
|
||
// (if the leak was nAlloc=267 then you want to skip the breapoint 266 times, enter 266)
|
||
//
|
||
// WARNING: Each control (OCX/DLL) will have its own instance of the framewrk, and thus
|
||
// its own instance of the memory leak implementaion. Adding a breakpoint
|
||
// anywhere in the framewrk will actually add multiple breakpoints - one for
|
||
// each control.
|
||
// Go back to Edit...Breakpoints.
|
||
// Deselect or remove the breakpoints for the OCX's/DLL's not causing leaks
|
||
//
|
||
// Run your scenario.
|
||
// When you hit this breakpoint verify that pvAddress and nByteCount are correct and then
|
||
// look down the callstack to see where the allocation was made.
|
||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
void PutBreakPointHere(void * pvAddress, ULONG nByteCount, ULONG nAlloc, char * szFile, ULONG uLine)
|
||
{
|
||
pvAddress=pvAddress; nAlloc=nAlloc; nByteCount=nByteCount;
|
||
szFile=szFile;
|
||
uLine=uLine;
|
||
HINSTANCE hInstance = g_hInstance; // hInstance of the OCX/DLL calling this breakpoint
|
||
int PutBreakPointOnThisLine = 1; // <--- breakpoint here.
|
||
} // PutBreakPointHere
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
//
|
||
// Debug Heap Memory Leak implementations
|
||
//
|
||
|
||
class CAddressNode
|
||
{
|
||
public:
|
||
void * m_pv; // Address of memory block allocated
|
||
ULONG m_cb; // Size of allocation in BYTES
|
||
ULONG m_cAlloc; // Allocation pass count.
|
||
LPSZ m_szFile; // Source file where the allocation was made
|
||
ULONG m_uLine; // Source line number where the allocation was made
|
||
CAddressNode * m_pnNext; // Nodes are stored in a linked list
|
||
|
||
void * operator new(size_t cb);
|
||
void operator delete(void * pv);
|
||
|
||
// We maintain a freelist to speed up allocation of AddressNodes.
|
||
static CAddressNode * m_pnFreeList;
|
||
};
|
||
|
||
|
||
CAddressNode * m_rInstTable[NUM_INST_TABLE_ENTRIES]; // Hashing table of all instances of
|
||
// mem alloc
|
||
|
||
CAddressNode * m_pnEnumNode; // Next node for enumerator to return
|
||
UINT m_uEnumIndex; // Current index into m_rInstTable for enumerator
|
||
static ULONG m_cGlobalPassCount; // Pass count of allocation. Common to all heaps
|
||
|
||
ULONG m_cCurNumAllocs; // Current number of allocations
|
||
ULONG m_cNumAllocs; // Total number of allocations ever done.
|
||
ULONG m_cCurNumBytesAllocated; // Current number of bytes allocated.
|
||
ULONG m_cNumBytesAllocated; // Total bytes allocated.
|
||
ULONG m_HWAllocs; // High water allocations.
|
||
ULONG m_HWBytes; // High water bytes.
|
||
static ULONG m_OverallCurAlloc; // These are overall statistics to since we
|
||
static ULONG m_OverallCurBytes; // wouldn't mind the overall high water.
|
||
static ULONG m_OverallHWAlloc;
|
||
static ULONG m_OverallHWBytes;
|
||
|
||
|
||
// Forward declarations
|
||
VOID AddInst(VOID * pv, DWORD dwBytes, LPSZ szFile, UINT uLine);
|
||
VOID DebugInst(ULONG cb);
|
||
VOID AnalyzeInst(LPVOID pv);
|
||
VOID DumpInst(CAddressNode * pn, LPTSTR lpTypeofAlloc);
|
||
LPSTR DumpInstTable(LPSTR lpLeak);
|
||
VOID DeleteInst(LPVOID pv);
|
||
VOID VerifyHeaderTrailer(CAddressNode * pn);
|
||
VOID CheckForLeaks(VOID);
|
||
VOID HeapCheck(VOID);
|
||
VOID OutputToFile(LPSTR szOutput);
|
||
CAddressNode * FindInst(LPVOID pv);
|
||
CAddressNode * EnumReset();
|
||
CAddressNode * EnumNext();
|
||
|
||
|
||
// Initialize a header and trailer for all memory to be allocated.
|
||
// Use 8 bytes so it is also compatible with RISC machines.
|
||
char * g_szHeader = "HEADHEAD";
|
||
char * g_szTrailer = "END!END!";
|
||
|
||
#define HEADERSIZE 8 // # of bytes of block header
|
||
// 0 ==> no block header signature
|
||
#define TRAILERSIZE 8 // # of bytes of block trailer
|
||
// 0 ==> no block trailer signature
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CtlHeapAllocImpl:
|
||
// Debug wrapper for HeapAlloc to track memory leaks:
|
||
//=--------------------------------------------------------------------------=
|
||
LPVOID CtlHeapAllocImpl(
|
||
HANDLE g_hHeap,
|
||
DWORD dwFlags,
|
||
DWORD dwBytesRequested,
|
||
LPSTR lpszFile,
|
||
UINT line
|
||
)
|
||
{
|
||
LPVOID lpvRet;
|
||
DWORD dwBytes;
|
||
LPTSTR lpTypeofAlloc = "HeapAlloc ";
|
||
|
||
|
||
// If someone tries to allocate memory before PROCCESS_ATTATCH (such as in a
|
||
// global constructor), do not track it because neither our heap nor our
|
||
// hInstance have been initialized yet.
|
||
//
|
||
if (!g_fInitCrit)
|
||
{
|
||
g_flagConstructorAlloc = TRUE;
|
||
return HeapAlloc(g_hHeap, dwFlags, dwBytesRequested);
|
||
}
|
||
|
||
|
||
// Increase size to make space for header and trailer signatures
|
||
dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE;
|
||
|
||
// Allocate memory
|
||
lpvRet = HeapAlloc(g_hHeap, dwFlags, dwBytes);
|
||
if (lpvRet)
|
||
{
|
||
// Initialize memory (non-zero)
|
||
if (!(dwFlags & HEAP_ZERO_MEMORY))
|
||
memset(lpvRet, 0xAF, dwBytes);
|
||
|
||
// Add instance to hash table
|
||
AddInst(lpvRet, dwBytesRequested, lpszFile, line);
|
||
|
||
// Trace allocations if switch is on
|
||
if (FSWITCH(fTraceCtlAllocs))
|
||
{
|
||
CAddressNode *pn = FindInst(lpvRet);
|
||
DumpInst(pn, lpTypeofAlloc);
|
||
}
|
||
|
||
// Advance pointer past header signature.
|
||
lpvRet = (LPVOID) ((char *)lpvRet + HEADERSIZE);
|
||
}
|
||
return lpvRet;
|
||
} // CtlHeapAllocImpl
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CtlHeapReAllocImpl:
|
||
//
|
||
//=--------------------------------------------------------------------------=
|
||
LPVOID CtlHeapReAllocImpl(
|
||
HANDLE g_hHeap,
|
||
DWORD dwFlags,
|
||
LPVOID lpvMem,
|
||
DWORD dwBytesRequested,
|
||
LPSTR lpszFile,
|
||
UINT line
|
||
)
|
||
{
|
||
LPVOID lpvRet;
|
||
CAddressNode * pn;
|
||
int byte;
|
||
DWORD cbOffset, dwBytes;
|
||
LPTSTR lpTypeofAlloc = "HeapReAlloc ";
|
||
|
||
// Move pointer to beginning of header
|
||
lpvMem = (LPVOID)((char *)lpvMem - HEADERSIZE);
|
||
|
||
// Find instance in hash table
|
||
pn = FindInst(lpvMem);
|
||
if (!pn)
|
||
{
|
||
FAIL("CtlHeapReAllocImpl - could not find lpvMem in the instance table. See debug \
|
||
output for more info.");
|
||
AnalyzeInst(lpvMem);
|
||
return 0;
|
||
}
|
||
|
||
// Increase size to make space for header and trailer signatures
|
||
dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE;
|
||
lpvRet = HeapReAlloc(g_hHeap, dwFlags, lpvMem, dwBytes);
|
||
if (lpvRet)
|
||
{
|
||
// If the reallocation grew, we must intialize new memory
|
||
if (dwBytesRequested > pn->m_cb)
|
||
{
|
||
if (dwFlags & HEAP_ZERO_MEMORY)
|
||
byte = 0x0;
|
||
else
|
||
byte = 0xAF;
|
||
|
||
// Get the byte offset of trailer in the old allocation
|
||
cbOffset = pn->m_cb + HEADERSIZE;
|
||
memset((char *)lpvRet + cbOffset, byte, dwBytes - cbOffset);
|
||
}
|
||
// Update hash table
|
||
EnterCriticalSection(&g_csHeap);
|
||
DeleteInst(lpvMem);
|
||
AddInst(lpvRet, dwBytesRequested, lpszFile, line);
|
||
LeaveCriticalSection(&g_csHeap);
|
||
|
||
// Trace Allocations if switch is on
|
||
if (FSWITCH(fTraceCtlAllocs))
|
||
{
|
||
CAddressNode *pn = FindInst(lpvRet);
|
||
DumpInst(pn, lpTypeofAlloc);
|
||
}
|
||
|
||
// Advance pointer past header signature.
|
||
lpvRet = (LPVOID)((char *)lpvRet + HEADERSIZE);
|
||
}
|
||
return lpvRet;
|
||
} // CtlHeapReAllocImpl
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CtlHeapFreeImpl:
|
||
// Debug wrapper for HeapFree
|
||
//=--------------------------------------------------------------------------=
|
||
BOOL CtlHeapFreeImpl(
|
||
HANDLE g_hHeap,
|
||
DWORD dwFlags,
|
||
LPVOID lpvMem
|
||
)
|
||
{
|
||
BOOL fRet = FALSE;
|
||
CAddressNode * pn;
|
||
LPTSTR lpTypeofAlloc = "HeapFree ";
|
||
|
||
|
||
// If someone tries to de-allocate memory after PROCCESS_DETATCH (such as in a
|
||
// global destructor), Re-initialize critical section and free memory.
|
||
//
|
||
if (!g_fInitCrit)
|
||
InitializeCriticalSection(&g_csHeap);
|
||
|
||
|
||
// Move pointer to beginning of header
|
||
lpvMem = (LPVOID) ((char *)lpvMem - HEADERSIZE);
|
||
|
||
// Find the instance in the hash table
|
||
pn = FindInst(lpvMem);
|
||
if (pn)
|
||
{
|
||
// Verify the memory has not been overwritten
|
||
VerifyHeaderTrailer(pn);
|
||
|
||
// Trace allocations if switch is on
|
||
if (FSWITCH(fTraceCtlAllocs))
|
||
{
|
||
CAddressNode *pn = FindInst(lpvMem);
|
||
DumpInst(pn, lpTypeofAlloc);
|
||
}
|
||
|
||
// Free memory -- NOTE: WinNT will set free memory to 0xEEFEEEFE which is "<22><><EFBFBD><EFBFBD>"
|
||
fRet = HeapFree(g_hHeap, 0, lpvMem);
|
||
if (!fRet)
|
||
FAIL("CtlHeapFreeImpl - lpvMem was found to be allocated in the heap passed in \
|
||
but HeapFree() failed. Maybe the pointer was already freed.");
|
||
}
|
||
|
||
// Remove instance from hash table
|
||
if (fRet)
|
||
DeleteInst(lpvMem);
|
||
|
||
// Make sure this memory wasn't allocated in a global constructor
|
||
else if (!g_flagConstructorAlloc)
|
||
{
|
||
FAIL("CtlHeapFreeImpl - could not find lpvMem in the instance table. See debug \
|
||
output for more info.");
|
||
AnalyzeInst(lpvMem);
|
||
}
|
||
else
|
||
fRet = TRUE;
|
||
|
||
// If called after PROCESS_DETATCH delete critical section and Check for leaks again
|
||
// NOTE: Only the LAST Assert will have the exact leak information. All previous
|
||
// Asserts will not take into account a HeapFree which occurs after PROCESS_DETACH.
|
||
// This only occurs in controls using global static destructors.
|
||
if (!g_fInitCrit)
|
||
{
|
||
CheckForLeaks();
|
||
DeleteCriticalSection(&g_csHeap);
|
||
}
|
||
|
||
return fRet;
|
||
} // CtlHeapFreeImpl
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CheckForLeaks:
|
||
// We are calling PROCESS_DETATCH so check if hash table is empty. If not
|
||
// dump info on memory that has been leaked.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID CheckForLeaks(VOID)
|
||
{
|
||
CAddressNode * pn = EnumReset();
|
||
BOOL IsEmpty = (pn == NULL); // FALSE if there are leaks
|
||
|
||
// First check for memory trashing of any leaked memory
|
||
HeapCheck();
|
||
|
||
if (!IsEmpty)
|
||
{
|
||
|
||
// First find out which OCX/DLL is leaking
|
||
TCHAR lpCtlName[128];
|
||
DWORD nSize = 128;
|
||
DWORD fValidPath;
|
||
fValidPath = GetModuleFileName(g_hInstance, (LPTSTR)lpCtlName, nSize);
|
||
|
||
LPSTR lpLeaks;
|
||
// Allocate some memory to hold the data but use GlobalAlloc since we
|
||
// don't want to use the vb memory stuff since it will muck things up.
|
||
lpLeaks = (LPSTR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE,128));
|
||
|
||
lstrcpy(lpLeaks, lpCtlName);
|
||
lstrcat(lpLeaks, " has leaked memory.\nUse PutBreakPointHere() in macros.cpp to debug.\r\n");
|
||
|
||
// Collect all leak info
|
||
lpLeaks = DumpInstTable(lpLeaks);
|
||
|
||
// Dump output to file if "fOutputFile" switch is on
|
||
if (FSWITCH(fOutputFile))
|
||
OutputToFile(lpLeaks);
|
||
|
||
// Dump output to an assert as long as "fNoLeakAsserts" is off
|
||
else if (!FSWITCH(fNoLeakAsserts))
|
||
{
|
||
// Truncate output so it fits into DisplayAssert (512 Max)
|
||
if (lstrlen(lpLeaks) > 500)
|
||
{
|
||
lstrcpyn(lpLeaks, lpLeaks, 500);
|
||
lstrcat(lpLeaks, "\nMore...");
|
||
}
|
||
DisplayAssert(lpLeaks, "FAIL", NULL, 0);
|
||
}
|
||
|
||
// Release memory used to store leak info
|
||
GlobalUnlock((HGLOBAL)GlobalHandle(lpLeaks)),
|
||
(BOOL)GlobalFree((HGLOBAL)GlobalHandle(lpLeaks));
|
||
|
||
}
|
||
return;
|
||
} // CheckForLeaks
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// AddInst:
|
||
// A heap allocation occured so here we add the allocation information to
|
||
// the instance table. To debug memory leaks where you need to use pass
|
||
// counts, set a passcount breakpoint in this function using the passcount
|
||
// value given in the debug output.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID AddInst(
|
||
VOID * pv,
|
||
DWORD dwBytes,
|
||
LPSZ szFile,
|
||
UINT uLine
|
||
)
|
||
{
|
||
UINT uHash;
|
||
CAddressNode * pn = new CAddressNode();
|
||
ASSERT(pn,"");
|
||
|
||
EnterCriticalSection(&g_csHeap);
|
||
|
||
m_cGlobalPassCount++;
|
||
|
||
pn->m_pv = pv; // Memory address of allocation
|
||
pn->m_cb = dwBytes; // Bytes requested to be allocated
|
||
pn->m_cAlloc = m_cGlobalPassCount; // This is the pass count value in debug output.
|
||
pn->m_szFile = szFile; // Source file the allocation call was made
|
||
pn->m_uLine = uLine; // Line number in source file.
|
||
|
||
PutBreakPointHere(pv, dwBytes, m_cGlobalPassCount, szFile, uLine);
|
||
|
||
// Add instance to proper position in table
|
||
uHash = HashInst(pv);
|
||
pn->m_pnNext = m_rInstTable[uHash];
|
||
m_rInstTable[uHash] = pn;
|
||
|
||
// Copy header and trailer signatures.
|
||
memcpy((char *)pv, g_szHeader, HEADERSIZE);
|
||
memcpy((char *)pv + HEADERSIZE + dwBytes, g_szTrailer, TRAILERSIZE);
|
||
|
||
LeaveCriticalSection(&g_csHeap);
|
||
|
||
// Track extra memory debug info
|
||
DebugInst( dwBytes );
|
||
} // AddInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// DebugInst:
|
||
// Updates the memory debug information
|
||
//=--------------------------------------------------------------------------=
|
||
VOID DebugInst(
|
||
ULONG cb
|
||
)
|
||
{
|
||
EnterCriticalSection(&g_csHeap);
|
||
|
||
++m_cCurNumAllocs;
|
||
++m_cNumAllocs;
|
||
++m_OverallCurAlloc;
|
||
m_cCurNumBytesAllocated+=cb;
|
||
m_cNumBytesAllocated+=cb;
|
||
m_OverallCurBytes+=cb;
|
||
|
||
m_HWAllocs = (m_HWAllocs < m_cCurNumAllocs) ? m_cCurNumAllocs : m_HWAllocs;
|
||
m_HWBytes = (m_HWBytes < m_cCurNumBytesAllocated) ? m_cCurNumBytesAllocated : m_HWBytes;
|
||
m_OverallHWAlloc = (m_OverallHWAlloc < m_OverallCurAlloc)
|
||
? m_OverallCurAlloc : m_OverallHWAlloc;
|
||
m_OverallHWBytes = (m_OverallHWBytes < m_OverallCurBytes)
|
||
? m_OverallCurBytes : m_OverallHWBytes;
|
||
|
||
LeaveCriticalSection(&g_csHeap);
|
||
|
||
} // DebugInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// FindInst:
|
||
// Give a pointer to an allocation, return a pointer to the debug
|
||
// allocation information.
|
||
//=--------------------------------------------------------------------------=
|
||
CAddressNode * FindInst(
|
||
LPVOID pv
|
||
)
|
||
{
|
||
CAddressNode * pn;
|
||
|
||
EnterCriticalSection(&g_csHeap);
|
||
|
||
pn = m_rInstTable[HashInst(pv)];
|
||
while (pn && pn->m_pv != pv)
|
||
pn = pn->m_pnNext;
|
||
|
||
LeaveCriticalSection(&g_csHeap);
|
||
return pn;
|
||
|
||
} // FindInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// AnalyzeInst:
|
||
// Given a pointer try determine if it is a valid Read and Write pointer
|
||
// and if it was allocated.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID AnalyzeInst(
|
||
LPVOID pv
|
||
)
|
||
{
|
||
LPTSTR lpTypeofAlloc = "Bad lpvMem ";
|
||
CAddressNode * pn = NULL;
|
||
|
||
// Either we have a bad pointer or the pointer does not point to any
|
||
// known heap allocations. Here we check if it points to readable or
|
||
// writable memory.
|
||
BOOL fBadPointer = (IsBadReadPtr(pv, 4) || IsBadWritePtr(pv, 4));
|
||
|
||
// Report what we know about the memory address
|
||
if (fBadPointer)
|
||
DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is not writable\n\r" \
|
||
"or readable. The allocation is either outside the addressable range\n\r" \
|
||
"for this operating system or the allocation was already freed.\n\r",pv);
|
||
else
|
||
DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is readable and writable,\n\r" \
|
||
"so the allocation was made without being added to instance table\n\r" \
|
||
"(prior to PROCESS_ATTATCH), or the memory was already freed.\n\r",pv);
|
||
|
||
} // AnanlyzeInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// DumpInst:
|
||
// Dump instance information out to an assert window.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID DumpInst(
|
||
CAddressNode * pn,
|
||
LPTSTR lpTypeofAlloc
|
||
)
|
||
{
|
||
char szOutput[255];
|
||
|
||
// Format output
|
||
wsprintf(szOutput, "%s: %s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", lpTypeofAlloc,
|
||
pn->m_szFile, pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb);
|
||
|
||
// Dump output to file if switch is turned on
|
||
if (FSWITCH(fOutputFile))
|
||
OutputToFile(szOutput);
|
||
else if (FSWITCH(fNoLeakAsserts))
|
||
DebugPrintf(szOutput);
|
||
|
||
// Else display output in assert
|
||
else
|
||
DisplayAssert(szOutput, "FAIL", _szThisFile, __LINE__);;
|
||
|
||
} // DumpInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// DumpInstTable:
|
||
// Memory leak has been detected so dump the entire instance table.
|
||
//=--------------------------------------------------------------------------=
|
||
LPSTR DumpInstTable(
|
||
LPSTR lpLeak
|
||
)
|
||
{
|
||
CAddressNode * pn = EnumReset();
|
||
DWORD sizeoflpLeak;
|
||
LPSTR lpTemp;
|
||
|
||
EnterCriticalSection(&g_csHeap);
|
||
|
||
DebugPrintf(lpLeak);
|
||
|
||
while (pn)
|
||
{
|
||
// Format the leak info
|
||
char szOut[250] = {NULL};
|
||
wsprintf(szOut, "\t%s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", pn->m_szFile,
|
||
pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb);
|
||
|
||
DebugPrintf(szOut);
|
||
|
||
// Convert lpLeak to a handle and get its current allocation size
|
||
sizeoflpLeak = GlobalSize(GlobalHandle(lpLeak));
|
||
|
||
// Reallocate memory to make space for more leak info
|
||
lpTemp = (LPSTR) (GlobalUnlock((HGLOBAL)GlobalHandle(lpLeak)),
|
||
GlobalLock(GlobalReAlloc((HGLOBAL)GlobalHandle(lpLeak),
|
||
sizeoflpLeak + lstrlen(szOut) + 1, GMEM_MOVEABLE)));
|
||
|
||
// Add new leak info to lpLeak
|
||
if(lpTemp)
|
||
{
|
||
lpLeak = lpTemp;
|
||
lstrcat(lpLeak, szOut);
|
||
}
|
||
|
||
// Get the next leak
|
||
pn = EnumNext();
|
||
}
|
||
LeaveCriticalSection(&g_csHeap);
|
||
return lpLeak;
|
||
|
||
} // DumpInstTable
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// DeleteInst:
|
||
// A heap allocation got free or was reallocated so remove the
|
||
// information from the instance table and check for memory trashing.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID DeleteInst(
|
||
LPVOID pv
|
||
)
|
||
{
|
||
CAddressNode ** ppn, * pnDead;
|
||
ppn = &m_rInstTable[HashInst(pv)];
|
||
|
||
EnterCriticalSection(&g_csHeap);
|
||
|
||
// Find allocation instance
|
||
while (*ppn != NULL)
|
||
{
|
||
if ((*ppn)->m_pv == pv)
|
||
{
|
||
pnDead = *ppn;
|
||
*ppn = (*ppn)->m_pnNext;
|
||
|
||
// Correct memory debug info
|
||
--m_cCurNumAllocs;
|
||
m_cCurNumBytesAllocated -= pnDead->m_cb;
|
||
--m_OverallCurAlloc;
|
||
m_OverallCurBytes -= pnDead->m_cb;
|
||
|
||
// Remove instance
|
||
delete pnDead;
|
||
LeaveCriticalSection(&g_csHeap);
|
||
return;
|
||
} // if
|
||
|
||
ppn = &((*ppn)->m_pnNext);
|
||
} // while
|
||
|
||
FAIL("DeleteInst - memory instance not found");
|
||
} // DeleteInst
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// VerifyHeaderTrailer:
|
||
// Inspect allocation for header and trailer signature overwrites
|
||
//=--------------------------------------------------------------------------=
|
||
VOID VerifyHeaderTrailer(
|
||
CAddressNode * pn
|
||
)
|
||
{
|
||
LPTSTR lpTypeofAlloc = "Memory trashed ";
|
||
|
||
//Verify the header
|
||
if (memcmp((char *)pn->m_pv, g_szHeader, HEADERSIZE) != 0)
|
||
{
|
||
FAIL("Heap block header has been trashed.");
|
||
DebugPrintf("Heap block header trashed.");
|
||
DebugPrintf("\r\n");
|
||
DumpInst(pn, lpTypeofAlloc);
|
||
}
|
||
|
||
//Verify the trailer
|
||
if (memcmp((char *)pn->m_pv + pn->m_cb + HEADERSIZE, g_szTrailer, TRAILERSIZE) != 0)
|
||
{
|
||
FAIL("Heap block trailer has been trashed.");
|
||
DebugPrintf("Heap block trailer trashed.");
|
||
DebugPrintf("\r\n");
|
||
DumpInst(pn, lpTypeofAlloc);
|
||
}
|
||
return;
|
||
|
||
} // VerifyHeaderTrailer
|
||
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// HeapCheck:
|
||
// Inspect all of the allocations for header and trailer signature
|
||
// overwrites.
|
||
//=--------------------------------------------------------------------------=
|
||
VOID HeapCheck(VOID)
|
||
{
|
||
ASSERT(HeapValidate(g_hHeap, 0, NULL) != 0, "OS Says heap is corrupt");
|
||
|
||
CAddressNode * pn = EnumReset();
|
||
while (pn)
|
||
{
|
||
VerifyHeaderTrailer(pn);
|
||
pn = EnumNext();
|
||
}
|
||
return;
|
||
} // HeapCheck
|
||
|
||
|
||
|
||
//=-------------------------------------------------------------------------=
|
||
// For use with CAddresssNode
|
||
//=-------------------------------------------------------------------------=
|
||
#define MEM_cAddressNodes 128 // Nodes are block allocated
|
||
#define UNUSED(var) ((var) = (var)) // Used to avoid warnings
|
||
|
||
// The free list is common
|
||
CAddressNode * CAddressNode::m_pnFreeList = NULL;
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CAddressNode::operator new:
|
||
// Returns a pointer to an allocated address node. If there are none on
|
||
// the free list then we allocate a block of address nodes, chain them
|
||
// together and add them to the free list. These nodes are never
|
||
// actually freed so it is ok to allocate them in blocks.
|
||
//=--------------------------------------------------------------------------=
|
||
void * CAddressNode::operator new(
|
||
size_t cb
|
||
)
|
||
{
|
||
CAddressNode * pn;
|
||
UNUSED(cb);
|
||
|
||
EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
|
||
|
||
if (m_pnFreeList == NULL)
|
||
{
|
||
UINT cbSize = sizeof(CAddressNode) * MEM_cAddressNodes; //allocate a block
|
||
pn = (CAddressNode *) HeapAlloc(g_hHeap, 0, cbSize);
|
||
//chain all except the first node together. the first node
|
||
//is the one returned
|
||
for (int i = 1; i < MEM_cAddressNodes - 1; ++i)
|
||
pn[i].m_pnNext = &pn[i+1];
|
||
pn[MEM_cAddressNodes - 1].m_pnNext = NULL;
|
||
m_pnFreeList = &pn[1];
|
||
}
|
||
else
|
||
{
|
||
pn = m_pnFreeList;
|
||
m_pnFreeList = pn->m_pnNext;
|
||
}
|
||
|
||
LeaveCriticalSection(&g_csHeap);
|
||
return pn;
|
||
} // CAddressNode::operator new
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// CAddressNode::operator delete
|
||
// Return the address node to the free list. We never actually free
|
||
// the node since nodes are allocated in blocks.
|
||
//=--------------------------------------------------------------------------=
|
||
void CAddressNode::operator delete(
|
||
void * pv
|
||
)
|
||
{
|
||
EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
|
||
|
||
CAddressNode * pn = (CAddressNode *) pv;
|
||
pn->m_pnNext = m_pnFreeList;
|
||
m_pnFreeList = pn;
|
||
|
||
LeaveCriticalSection(&g_csHeap);
|
||
} // CAddressNode::operator delete
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// EnumReset:
|
||
// Reset the enumerator and return the first node. NULL if empty.
|
||
//=--------------------------------------------------------------------------=
|
||
CAddressNode * EnumReset()
|
||
{
|
||
m_pnEnumNode = NULL;
|
||
for (m_uEnumIndex = 0; m_uEnumIndex < NUM_INST_TABLE_ENTRIES; ++m_uEnumIndex)
|
||
{
|
||
m_pnEnumNode = m_rInstTable[m_uEnumIndex];
|
||
if (m_pnEnumNode != NULL)
|
||
return m_pnEnumNode;
|
||
}
|
||
return NULL; //Instance table is empty
|
||
} // EnumReset
|
||
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// EnumNext:
|
||
// Return the next node in the enumeration. m_pnEnumNode points to the last
|
||
// node returned. It is NULL if no more left.
|
||
//=--------------------------------------------------------------------------=
|
||
CAddressNode * EnumNext()
|
||
{
|
||
ASSERT(m_uEnumIndex <= NUM_INST_TABLE_ENTRIES, "");
|
||
|
||
if (m_pnEnumNode == NULL)
|
||
return NULL; //end of enumeration
|
||
|
||
m_pnEnumNode = m_pnEnumNode->m_pnNext;
|
||
if (m_pnEnumNode == NULL)
|
||
{
|
||
//at end of this linked list so search for next list
|
||
m_uEnumIndex++;
|
||
while (m_uEnumIndex < NUM_INST_TABLE_ENTRIES && m_rInstTable[m_uEnumIndex] == NULL)
|
||
m_uEnumIndex++;
|
||
if (m_uEnumIndex < NUM_INST_TABLE_ENTRIES)
|
||
m_pnEnumNode = m_rInstTable[m_uEnumIndex];
|
||
}
|
||
return m_pnEnumNode;
|
||
} // EnumNext
|
||
|
||
|
||
|
||
//=---------------------------------------------------------------------------=
|
||
// OutputToFile:
|
||
// Dumps output to file "ctldebug.log"
|
||
//=---------------------------------------------------------------------------=
|
||
VOID OutputToFile
|
||
(
|
||
LPSTR szOutput
|
||
)
|
||
{
|
||
DWORD nPathSize;
|
||
DWORD nDirPathSize = 128;
|
||
TCHAR lpFilePath[128];
|
||
LPCTSTR lpFileName = "\\CtlDebug.log";
|
||
HANDLE hFile;
|
||
BOOL fWritten, fClosed = FALSE;
|
||
DWORD nBytesWritten;
|
||
|
||
// Create path to output file
|
||
nPathSize = GetCurrentDirectory(nDirPathSize, (LPTSTR)lpFilePath);
|
||
if (nPathSize == 0)
|
||
FAIL("Unable to get current directory...");
|
||
lstrcat(lpFilePath, lpFileName);
|
||
|
||
// Open and write to file
|
||
hFile = CreateFile((LPCTSTR)lpFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||
DWORD SetPtr = SetFilePointer(hFile, NULL, NULL, FILE_END);
|
||
fWritten = WriteFile(hFile, (LPCVOID)szOutput, (DWORD)strlen(szOutput),
|
||
&nBytesWritten, NULL);
|
||
if (!fWritten)
|
||
FAIL("Unable to write output to file...");
|
||
|
||
// Close file handle
|
||
fClosed = CloseHandle(hFile);
|
||
if (!fClosed)
|
||
FAIL("Unable to close output file...");
|
||
|
||
} // OutputToFile
|
||
|
||
|
||
//
|
||
// End of Debug Memory Leak implemntation
|
||
//
|
||
//=--------------------------------------------------------------------------=
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// This routine outputs through DebugPrintf some information if the
|
||
// given hr fails to succeed. This is used by RRETURN to output where
|
||
// a function that returns a failing error code.
|
||
//=--------------------------------------------------------------------------=
|
||
HRESULT HrDebugTraceReturn
|
||
(
|
||
HRESULT hr,
|
||
char *pszFile,
|
||
int iLine
|
||
)
|
||
{
|
||
// We only output information if the hr fails.
|
||
if (FAILED(hr))
|
||
{
|
||
char szMessageError[128];
|
||
szMessageError[0] = '\0';
|
||
BOOL fMessage;
|
||
|
||
#if RBY_MAC
|
||
fMessage = FALSE; // FormatMessage not available on the mac
|
||
#else
|
||
// Get the message from the system
|
||
// CONSIDER, t-tshort 10/95: Getting some messages from us instead
|
||
// of the system?
|
||
fMessage = FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK
|
||
| FORMAT_MESSAGE_FROM_SYSTEM,
|
||
NULL, hr,
|
||
MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
|
||
szMessageError, sizeof(szMessageError), NULL);
|
||
#endif
|
||
|
||
// Erps didn't get a message.
|
||
if(!fMessage)
|
||
lstrcpy(szMessageError,"Unknown Hresult");
|
||
|
||
// Output the information that we want.
|
||
DebugPrintf("FAILED RETURN: %s(%d) : 0x%08lx, %s\n",
|
||
pszFile, iLine, hr, szMessageError);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//---------------------------------------------------------------------
|
||
// The following is a common output formatting buffer shared by several
|
||
// of the following debug routines.
|
||
//---------------------------------------------------------------------
|
||
char s_rgchOutput[2048]; // pretty big...
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// Emit debugging information
|
||
//=--------------------------------------------------------------------------=
|
||
void _DebugOutput(char* pszOutput)
|
||
{
|
||
OutputDebugString(pszOutput);
|
||
}
|
||
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// Emit a formatted debugging string to the location specified in
|
||
// the debug options dialog.
|
||
//=--------------------------------------------------------------------------=
|
||
void _DebugPrintf(char* pszFmt, ...)
|
||
{
|
||
va_list args;
|
||
|
||
va_start(args, pszFmt);
|
||
wvsprintf(s_rgchOutput, pszFmt, args);
|
||
va_end(args);
|
||
|
||
// sqwak if we overrun the formatting buffer!
|
||
ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
|
||
|
||
_DebugOutput(s_rgchOutput);
|
||
}
|
||
|
||
//=--------------------------------------------------------------------------=
|
||
// Conditional form of DebugPrintf
|
||
//=--------------------------------------------------------------------------=
|
||
void _DebugPrintIf(BOOL fPrint, char* pszFmt, ...)
|
||
{
|
||
va_list args;
|
||
|
||
if (!fPrint)
|
||
return;
|
||
|
||
va_start(args, pszFmt);
|
||
wvsprintf(s_rgchOutput, pszFmt, args);
|
||
va_end(args);
|
||
|
||
// sqwak if we overrun the formatting buffer!
|
||
ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
|
||
|
||
_DebugOutput(s_rgchOutput);
|
||
}
|
||
|
||
|
||
#endif // DEBUG
|