You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1210 lines
25 KiB
1210 lines
25 KiB
/*
|
|
* gllocal.c
|
|
*
|
|
* Implementation of global and local heaps
|
|
*
|
|
* Copyright (C) 1994 Microsoft Corporation
|
|
*/
|
|
#include "_apipch.h"
|
|
|
|
#define _GLLOCAL_C
|
|
|
|
#ifdef MAC
|
|
#include "ole2ui.h"
|
|
#include <utilmac.h>
|
|
#include <mapiprof.h>
|
|
|
|
#ifdef GetPrivateProfileInt
|
|
#undef GetPrivateProfileInt
|
|
#undef GetPrivateProfileString
|
|
#endif
|
|
#define GetPrivateProfileInt MAPIGetPrivateProfileInt
|
|
#define GetPrivateProfileString MAPIGetPrivateProfileString
|
|
#endif // MAC
|
|
|
|
// #include "glheap.h"
|
|
|
|
#ifdef MAC
|
|
#pragma code_seg("glheap", "fixed, preload")
|
|
#else
|
|
#ifdef OLD_STUFF
|
|
#pragma SEGMENT(glheap)
|
|
#endif // OLD_STUFF
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#define STATIC
|
|
#else
|
|
#define STATIC static
|
|
#endif
|
|
|
|
// Local Heap Debug Implementation --------------------------------------------
|
|
|
|
#ifdef DEBUG
|
|
|
|
static TCHAR szDebugIni[] = TEXT("WABDBG.INI");
|
|
static TCHAR szSectionHeap[] = TEXT("Memory Management");
|
|
static TCHAR szKeyUseVirtual[] = TEXT("VirtualMemory");
|
|
static TCHAR szKeyAssertLeaks[] = TEXT("AssertLeaks");
|
|
static TCHAR szKeyDumpLeaks[] = TEXT("DumpLeaks");
|
|
static TCHAR szKeyFillMem[] = TEXT("FillMemory");
|
|
static TCHAR szKeyFillByte[] = TEXT("FillByte");
|
|
|
|
// Artificial Errors for local heaps
|
|
BOOL FForceFailure(HLH hlh, UINT cb);
|
|
|
|
static TCHAR szAESectionHeap[] = TEXT("Local Heap Failures");
|
|
static TCHAR szAEKeyFailStart[] = TEXT("AllocsToFirstFailure");
|
|
static TCHAR szAEKeyFailInterval[] = TEXT("FailureInterval");
|
|
static TCHAR szAEKeyFailBufSize[] = TEXT("FailureSize");
|
|
|
|
#ifdef HEAPMON
|
|
static TCHAR szKeyHeapMon[] = TEXT("MonitorHeap");
|
|
#ifdef MAC
|
|
static TCHAR szHeapMonDLL[] = TEXT("GLHM");
|
|
#else
|
|
static TCHAR szHeapMonDLL[] = TEXT("GLHMON32.DLL");
|
|
#endif
|
|
static char szHeapMonEntry[] = "HeapMonitor";
|
|
static char szGetSymNameEntry[] = "GetSymbolName";
|
|
#endif
|
|
|
|
// Virtual Memory Support --------------------------------------------
|
|
//
|
|
// The VM Allocators do not currently work on:
|
|
// AMD64
|
|
// MAC
|
|
//
|
|
#if defined(MAC) || defined(_AMD64_) || defined(_IA64_)
|
|
#define VMAlloc(cb) 0
|
|
#define VMAllocEx(cb, ul) 0
|
|
#define VMRealloc(pv, cb) 0
|
|
#define VMReallocEx(pv, cb, ul) 0
|
|
#define VMFree(pv)
|
|
#define VMFreeEx(pv, ul)
|
|
#define VMGetSize(pv) 0
|
|
#define VMGetSizeEx(pv, ul) 0
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(MAC)
|
|
#define LH_EnterCriticalSection(hlh) EnterCriticalSection(&hlh->cs)
|
|
#define LH_LeaveCriticalSection(hlh) LeaveCriticalSection(&hlh->cs)
|
|
#else
|
|
#define LH_EnterCriticalSection(hlh)
|
|
#define LH_LeaveCriticalSection(hlh)
|
|
#endif
|
|
|
|
#ifdef HEAPMON
|
|
/*
|
|
- FRegisterHeap
|
|
-
|
|
* Purpose:
|
|
* If the user wants to monitor the Heap, then load the DLL with
|
|
* the HeapMonitor UI.
|
|
*/
|
|
|
|
BOOL FRegisterHeap(PLH plh)
|
|
{
|
|
HINSTANCE hInst;
|
|
LPHEAPMONPROC pfnHeapMon;
|
|
LPGETSYMNAMEPROC pfnGetSymName;
|
|
|
|
plh->hInstHeapMon = 0;
|
|
plh->pfnGetSymName = NULL;
|
|
|
|
hInst = LoadLibrary(szHeapMonDLL);
|
|
|
|
if (!hInst)
|
|
{
|
|
DebugTrace(TEXT("FRegisterHeap: Failed to LoadLibrary GLHMON32.DLL.\n"));
|
|
goto ret;
|
|
}
|
|
|
|
pfnHeapMon = (LPHEAPMONPROC)GetProcAddress(hInst, szHeapMonEntry);
|
|
|
|
if (!pfnHeapMon)
|
|
{
|
|
DebugTrace(TEXT("FRegisterHeap: Failed to GetProcAddress of HeapMonitor.\n"));
|
|
FreeLibrary(hInst);
|
|
goto ret;
|
|
}
|
|
|
|
pfnGetSymName = (LPGETSYMNAMEPROC)GetProcAddress(hInst, szGetSymNameEntry);
|
|
|
|
if (!pfnGetSymName)
|
|
{
|
|
DebugTrace(TEXT("FRegisterHeap: Failed to GetProcAddress of GetSymName.\n"));
|
|
}
|
|
|
|
plh->hInstHeapMon = hInst;
|
|
|
|
if (!pfnHeapMon(plh, HEAPMON_LOAD))
|
|
{
|
|
DebugTrace(TEXT("FRegisterHeap: Call to HeapMonitor failed.\n"));
|
|
plh->hInstHeapMon = 0;
|
|
goto ret;
|
|
}
|
|
|
|
plh->pfnHeapMon = pfnHeapMon;
|
|
plh->pfnGetSymName = pfnGetSymName;
|
|
|
|
ret:
|
|
return (plh->hInstHeapMon ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
void UnRegisterHeap(HLH hlh)
|
|
{
|
|
if (hlh->pfnHeapMon)
|
|
hlh->pfnHeapMon(hlh, HEAPMON_UNLOAD);
|
|
}
|
|
#endif // HEAPMON
|
|
|
|
|
|
/*
|
|
- LH_ReportLeak
|
|
-
|
|
* Purpose:
|
|
* To report individual memory leaks through DebugTrace and the
|
|
* LH_LeakHook breakpoint function.
|
|
*/
|
|
|
|
void LH_ReportLeak(HLH hlh, PLHBLK plhblk)
|
|
{
|
|
DebugTrace(TEXT("Memory leak '%s' in %s @ %08lX, Allocation #%ld, Size: %ld\n"),
|
|
plhblk->szName[0] ? plhblk->szName : TEXT("NONAME"),
|
|
hlh->szHeapName, PlhblkToPv(plhblk),
|
|
plhblk->ulAllocNum, CbPlhblkClient(plhblk));
|
|
|
|
#if defined(WIN32) && defined(_X86_) && defined(LEAK_TEST)
|
|
{
|
|
int i;
|
|
for (i = 0; i < NCALLERS && plhblk->pfnCallers[i]; i++)
|
|
{
|
|
char szSymbol[256];
|
|
char szModule[64];
|
|
DWORD dwDisp;
|
|
BOOL fGotSym = FALSE;
|
|
|
|
szSymbol[0] = 0;
|
|
szModule[0] = 0;
|
|
|
|
if (hlh->pfnGetSymName)
|
|
if (hlh->pfnGetSymName((DWORD) plhblk->pfnCallers[i], szModule,
|
|
szSymbol, &dwDisp))
|
|
fGotSym = TRUE;
|
|
|
|
if (fGotSym)
|
|
{
|
|
DebugTrace(TEXT("[%d] %s %s"), i, szModule, szSymbol);
|
|
if (dwDisp)
|
|
DebugTrace(TEXT("+%ld"), dwDisp);
|
|
DebugTrace(TEXT("\n"));
|
|
}
|
|
else
|
|
DebugTrace(TEXT("[%d] %s %08lX \n"), i, szModule, plhblk->pfnCallers[i]);
|
|
DBGMEM_LeakHook(plhblk->pfnCallers[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
- LH_DumpLeaks
|
|
-
|
|
* Purpose:
|
|
* Gets called at LH_Close time to report any memory leaks against
|
|
* this heap. There are 3 reporting fascilities used by this routine:
|
|
*
|
|
* => Breakpoint hooking (via LH_LeakHook)
|
|
* => Asserts (via TrapSz)
|
|
* => Debug trace tags (via DebugTrace)
|
|
*
|
|
* The Debug Trace is the default method if no others are specified
|
|
* or if the others are in-appropriate for the given platform.
|
|
*/
|
|
|
|
void LH_DumpLeaks(HLH hlh)
|
|
{
|
|
PLHBLK plhblk;
|
|
BOOL fDump = !!(hlh->ulFlags & HEAP_DUMP_LEAKS);
|
|
BOOL fAssert = !!(hlh->ulFlags & HEAP_ASSERT_LEAKS);
|
|
int cLeaks = 0;
|
|
|
|
for (plhblk = hlh->plhblkHead; plhblk; plhblk = plhblk->plhblkNext)
|
|
{
|
|
if (fDump)
|
|
LH_ReportLeak(hlh, plhblk);
|
|
cLeaks++;
|
|
}
|
|
|
|
if (cLeaks)
|
|
{
|
|
#if defined(WIN16) || (defined(WIN32) && defined(_X86_))
|
|
if (fAssert)
|
|
{
|
|
TrapSz3( TEXT("GLHEAP detected %d memory leak%s in Heap: %s"),
|
|
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
|
|
}
|
|
else
|
|
DebugTrace(TEXT("GLHEAP detected %d memory leak%s in Heap: %s\n"),
|
|
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
|
|
#else
|
|
DebugTrace(TEXT("GLHEAP detected %d memory leak%s in Heap: %s\n"),
|
|
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
BOOL LH_ValidatePlhblk(HLH hlh, PLHBLK plhblk, char ** pszReason)
|
|
{
|
|
if (IsBadWritePtr(plhblk, sizeof(LHBLK)))
|
|
{
|
|
*pszReason = "Block header cannot be written to";
|
|
goto err;
|
|
}
|
|
|
|
if (plhblk->hlh != hlh)
|
|
{
|
|
*pszReason = "Block header does not have correct pointer back to heap";
|
|
goto err;
|
|
}
|
|
|
|
if (plhblk->plhblkNext)
|
|
{
|
|
if (IsBadWritePtr(plhblk->plhblkNext, sizeof(LHBLK)))
|
|
{
|
|
*pszReason = "Block header has invalid next link pointer";
|
|
goto err;
|
|
}
|
|
|
|
if (plhblk->plhblkNext->plhblkPrev != plhblk)
|
|
{
|
|
*pszReason = "Block header points to a next block which doesn't "
|
|
"point back to it";
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (plhblk->plhblkPrev)
|
|
{
|
|
if (IsBadWritePtr(plhblk->plhblkPrev, sizeof(LHBLK))) {
|
|
*pszReason = "Block header has invalid prev link pointer";
|
|
goto err;
|
|
}
|
|
|
|
if (plhblk->plhblkPrev->plhblkNext != plhblk)
|
|
{
|
|
*pszReason = "Block header points to a prev block which doesn't "
|
|
"point back to it";
|
|
goto err;
|
|
}
|
|
}
|
|
else if (hlh->plhblkHead != plhblk)
|
|
{
|
|
*pszReason = "Block header has a zero prev link but the heap doesn't "
|
|
"believe it is the first block";
|
|
goto err;
|
|
}
|
|
|
|
if (plhblk->ulAllocNum > hlh->ulAllocNum)
|
|
{
|
|
*pszReason = "Block header has an invalid internal allocation number";
|
|
goto err;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
err:
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// $MAC - Need WINAPI
|
|
|
|
BOOL
|
|
#ifdef MAC
|
|
WINAPI
|
|
#endif
|
|
LH_DidAlloc(HLH hlh, LPVOID pv)
|
|
{
|
|
PLHBLK plhblk;
|
|
char * pszReason;
|
|
BOOL fDidAlloc = FALSE;
|
|
|
|
for (plhblk = hlh->plhblkHead; plhblk; plhblk = plhblk->plhblkNext)
|
|
{
|
|
AssertSz2(LH_ValidatePlhblk(hlh, plhblk, &pszReason),
|
|
TEXT("Block header (plhblk=%08lX) is invalid\n%s"),
|
|
plhblk, pszReason);
|
|
|
|
if (PlhblkToPv(plhblk) == pv)
|
|
{
|
|
fDidAlloc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fDidAlloc;
|
|
}
|
|
|
|
|
|
BOOL LH_ValidatePv(HLH hlh, LPVOID pv, char * pszFunc)
|
|
{
|
|
PLHBLK plhblk;
|
|
char * pszReason;
|
|
|
|
plhblk = PvToPlhblk(hlh, pv);
|
|
|
|
if (!plhblk)
|
|
{
|
|
TrapSz3( TEXT("%s detected a memory block (%08lX) which was either not ")
|
|
TEXT("allocated in heap '%s' or has already been freed."),
|
|
pszFunc, pv, hlh->szHeapName);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (LH_ValidatePlhblk(hlh, plhblk, &pszReason))
|
|
return(TRUE);
|
|
|
|
TrapSz4( TEXT("%s detected an invalid memory block (%08lX) in heap '%s'. %s."),
|
|
pszFunc, pv, hlh->szHeapName, pszReason);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
- PlhblkEnqueue
|
|
-
|
|
* Purpose:
|
|
* To add a newly allocated block to the allocation list hanging
|
|
* off the heap. We do an InsertSorted because the HeapMonitor
|
|
* will need to reference the allocations ordered by their
|
|
* location in the heap. Since the monitor will walk the heap
|
|
* often, it is more efficient to do the sort up front.
|
|
*/
|
|
|
|
void PlhblkEnqueue(PLHBLK plhblk)
|
|
{
|
|
PLHBLK plhblkCurr = NULL;
|
|
PLHBLK plhblkNext = plhblk->hlh->plhblkHead;
|
|
|
|
while (plhblkNext)
|
|
{
|
|
if (plhblkNext > plhblk)
|
|
break;
|
|
|
|
plhblkCurr = plhblkNext;
|
|
plhblkNext = plhblkCurr->plhblkNext;
|
|
}
|
|
|
|
if (plhblkNext)
|
|
{
|
|
plhblk->plhblkNext = plhblkNext;
|
|
plhblk->plhblkPrev = plhblkCurr;
|
|
plhblkNext->plhblkPrev = plhblk;
|
|
}
|
|
else
|
|
{
|
|
plhblk->plhblkNext = NULL;
|
|
plhblk->plhblkPrev = plhblkCurr;
|
|
}
|
|
|
|
if (plhblkCurr)
|
|
plhblkCurr->plhblkNext = plhblk;
|
|
else
|
|
plhblk->hlh->plhblkHead = plhblk;
|
|
}
|
|
|
|
|
|
/*
|
|
- PlhblkDequeue
|
|
-
|
|
* Purpose:
|
|
* To remove a freed block from the list of allocations hanging
|
|
* off the heap.
|
|
*/
|
|
|
|
void PlhblkDequeue(PLHBLK plhblk)
|
|
{
|
|
if (plhblk->plhblkNext)
|
|
plhblk->plhblkNext->plhblkPrev = plhblk->plhblkPrev;
|
|
|
|
if (plhblk->plhblkPrev)
|
|
plhblk->plhblkPrev->plhblkNext = plhblk->plhblkNext;
|
|
else
|
|
plhblk->hlh->plhblkHead = plhblk->plhblkNext;
|
|
}
|
|
|
|
|
|
/*
|
|
- HexByteToBin
|
|
-
|
|
* Purpose:
|
|
* Takes a hex string and converts the 2 msd's to a byte, ignoring
|
|
* the remaining digits. This function assumes the string is
|
|
* formatted as: 0xnn, otherwise it simply returns 0x00.
|
|
*/
|
|
|
|
BYTE HexByteToBin(LPSTR sz)
|
|
{
|
|
int i, n[2], nT;
|
|
|
|
if (*sz++ != '0')
|
|
return 0x00;
|
|
|
|
nT = *sz++;
|
|
|
|
if (nT != 'x' && nT != 'X')
|
|
return 0x00;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
nT = *sz++;
|
|
|
|
if (nT >= '0' && nT <= '9')
|
|
n[i] = nT - '0';
|
|
else if (nT >= 'A' && nT <= 'F')
|
|
n[i] = nT - 'A' + 10;
|
|
else if (nT >= 'a' && nT <= 'f')
|
|
n[i] = nT - 'a' + 10;
|
|
else
|
|
return (BYTE)0x00;
|
|
}
|
|
|
|
n[0] <<= 4;
|
|
return (BYTE)((BYTE)n[0] | (BYTE)n[1]);
|
|
}
|
|
|
|
|
|
HLH WINAPI LH_Open(DWORD dwMaxHeap)
|
|
{
|
|
_HLH _hlhData = 0;
|
|
_HLH _hlhBlks = 0;
|
|
PLH plh = NULL;
|
|
UINT cch = 0;
|
|
UINT uiT = 0;
|
|
TCHAR szFillByte[8];
|
|
LPSTR lpFillByte = NULL;
|
|
ULONG cbVirtual = 0;
|
|
|
|
// The first thing we must do is create a heap that we will
|
|
// allocate our Allocation Blocks on. We also allocate our
|
|
// debug Heap object on this heap.
|
|
|
|
_hlhBlks = _LH_Open(dwMaxHeap);
|
|
|
|
if (!_hlhBlks)
|
|
{
|
|
DebugTrace(TEXT("LH_Open: Failed to create new heap!\n"));
|
|
goto ret;
|
|
}
|
|
|
|
// Allocate the thing we hand back to the caller on this new heap.
|
|
|
|
plh = _LH_Alloc(_hlhBlks, sizeof(LH));
|
|
|
|
if (!plh)
|
|
{
|
|
DebugTrace(TEXT("LH_Alloc: Failed to allocate heap handle!\n"));
|
|
_LH_Close(_hlhBlks);
|
|
_hlhBlks = 0;
|
|
goto ret;
|
|
}
|
|
|
|
// Initialize all the goodies we store in this thing.
|
|
// Hook this heap into the global list of heaps we've
|
|
// created in this context.
|
|
|
|
memset(plh, 0, sizeof(LH));
|
|
|
|
plh->pfnSetName = (LPLHSETNAME)LH_SetNameFn;
|
|
plh->_hlhBlks = _hlhBlks;
|
|
plh->ulFlags = HEAP_LOCAL;
|
|
|
|
#if defined(WIN32) && !defined(MAC)
|
|
InitializeCriticalSection(&plh->cs);
|
|
#endif
|
|
|
|
// VirtualMemory default is FALSE
|
|
|
|
cbVirtual = GetPrivateProfileInt(szSectionHeap, szKeyUseVirtual, 0,
|
|
szDebugIni);
|
|
|
|
if (cbVirtual)
|
|
{
|
|
plh->ulFlags |= HEAP_USE_VIRTUAL;
|
|
|
|
// We always want virtual allocations on MIPS and PPC to be 4-byte
|
|
// aligned, because all our code assumes that the beginning of an
|
|
// allocation is aligned on machine word boundaries. On other
|
|
// platforms, changing this behavior is non-fatal, but on MIPS and
|
|
// PPC we'll get alignment faults everywhere.
|
|
|
|
#if !defined(_MIPS_) && !defined(_PPC_)
|
|
if (cbVirtual == 4)
|
|
#endif
|
|
plh->ulFlags |= HEAP_USE_VIRTUAL_4;
|
|
}
|
|
|
|
// DumpLeaks default is TRUE
|
|
|
|
if (GetPrivateProfileInt(szSectionHeap, szKeyDumpLeaks, 1, szDebugIni))
|
|
plh->ulFlags |= HEAP_DUMP_LEAKS;
|
|
|
|
// AssertLeaks default is FALSE
|
|
|
|
if (GetPrivateProfileInt(szSectionHeap, szKeyAssertLeaks, 0, szDebugIni))
|
|
plh->ulFlags |= HEAP_ASSERT_LEAKS;
|
|
|
|
// FillMem default is TRUE
|
|
|
|
if (GetPrivateProfileInt(szSectionHeap, szKeyFillMem, 1, szDebugIni))
|
|
plh->ulFlags |= HEAP_FILL_MEM;
|
|
|
|
if (plh->ulFlags & HEAP_FILL_MEM)
|
|
{
|
|
cch = GetPrivateProfileString(
|
|
szSectionHeap,
|
|
szKeyFillByte,
|
|
szEmpty,
|
|
szFillByte,
|
|
CharSizeOf(szFillByte)-1,
|
|
szDebugIni);
|
|
}
|
|
|
|
// Set the memory fill character.
|
|
lpFillByte = ConvertWtoA(szFillByte);
|
|
plh->chFill = (BYTE)(cch ? HexByteToBin(lpFillByte) : chDefaultFill);
|
|
LocalFreeAndNull(&lpFillByte);
|
|
|
|
//
|
|
// Set up artificial failures. If anything is set in our ini file, then
|
|
// HEAP_FAILURES_ENABLED gets set.
|
|
//
|
|
uiT = GetPrivateProfileInt(szAESectionHeap, szAEKeyFailStart, 0, szDebugIni);
|
|
if (uiT)
|
|
{
|
|
plh->ulFlags |= HEAP_FAILURES_ENABLED;
|
|
plh->ulFailStart = (ULONG) uiT;
|
|
|
|
plh->ulFailInterval =
|
|
(ULONG) GetPrivateProfileInt(szAESectionHeap, szAEKeyFailInterval, 0, szDebugIni);
|
|
|
|
plh->uiFailBufSize =
|
|
GetPrivateProfileInt(szAESectionHeap, szAEKeyFailBufSize, 0, szDebugIni);
|
|
}
|
|
|
|
|
|
#ifdef HEAPMON
|
|
// If the user wants Heap Monitor UI, the spin a thread to manage a
|
|
// DialogBox that can display the status of the heap at all times.
|
|
|
|
if (GetPrivateProfileInt(szSectionHeap, szKeyHeapMon, 0, szDebugIni))
|
|
if (FRegisterHeap(plh))
|
|
plh->ulFlags |= HEAP_HEAP_MONITOR;
|
|
#endif
|
|
|
|
// If we are not using virtual memory allocators, then we
|
|
// create another heap to allocate the users data in.
|
|
|
|
if (!(plh->ulFlags & HEAP_USE_VIRTUAL))
|
|
{
|
|
_hlhData = _LH_Open(dwMaxHeap);
|
|
|
|
if (!_hlhData)
|
|
{
|
|
DebugTrace(TEXT("LH_Alloc: Failed to allocate heap handle!\n"));
|
|
_LH_Close(_hlhBlks);
|
|
plh = NULL;
|
|
goto ret;
|
|
}
|
|
|
|
plh->_hlhData = _hlhData;
|
|
}
|
|
#ifndef _WIN64
|
|
LH_SetHeapName1(plh, TEXT("LH %08lX"), plh);
|
|
#else
|
|
LH_SetHeapName1(plh, TEXT("LH %p"), plh);
|
|
#endif // _WIN64
|
|
|
|
ret:
|
|
return (HLH)plh;
|
|
}
|
|
|
|
|
|
void WINAPI LH_Close(HLH hlh)
|
|
{
|
|
_HLH _hlhData = hlh->_hlhData;
|
|
_HLH _hlhBlks = hlh->_hlhBlks;
|
|
|
|
// Dump memory leaks if we're supposed to.
|
|
|
|
if (hlh->ulFlags & HEAP_DUMP_LEAKS)
|
|
LH_DumpLeaks(hlh);
|
|
|
|
// Destroy the HeapMonitor thread and un-load the DLL
|
|
|
|
#ifdef HEAPMON
|
|
UnRegisterHeap(hlh);
|
|
|
|
if ((hlh->ulFlags & HEAP_HEAP_MONITOR) && hlh->hInstHeapMon)
|
|
FreeLibrary(hlh->hInstHeapMon);
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(MAC)
|
|
DeleteCriticalSection(&hlh->cs);
|
|
#endif
|
|
|
|
// Clean-up and leave. Closing frees leaks, so we're cool!
|
|
|
|
if (!(hlh->ulFlags & HEAP_USE_VIRTUAL) && _hlhData)
|
|
_LH_Close(_hlhData);
|
|
|
|
if (_hlhBlks)
|
|
{
|
|
_LH_Free (_hlhBlks, hlh);
|
|
_LH_Close(_hlhBlks);
|
|
}
|
|
}
|
|
|
|
|
|
LPVOID WINAPI LH_Alloc(HLH hlh, UINT cb)
|
|
{
|
|
PLHBLK plhblk = NULL;
|
|
LPVOID pvAlloc = NULL;
|
|
|
|
// Note: To be consistent with other (e.g. system) allocators,
|
|
// we have to return a valid allocation if cb == 0. So, we
|
|
// allow a cb of 0 to actually be allocated. (See bug 3556 in
|
|
// the sqlguest:exchange database.)
|
|
|
|
LH_EnterCriticalSection(hlh);
|
|
|
|
if (hlh->ulFlags & HEAP_FAILURES_ENABLED)
|
|
{
|
|
if (FForceFailure(hlh, cb))
|
|
{
|
|
DebugTrace(TEXT("LH_Alloc: Artificial Failure\n"));
|
|
pvAlloc = NULL;
|
|
hlh->ulAllocNum++;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
|
|
pvAlloc = VMAllocEx(cb, 4);
|
|
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
|
|
pvAlloc = VMAllocEx(cb, 1);
|
|
else if (cb > UINT_MAX)
|
|
plhblk = 0;
|
|
else
|
|
#ifndef _WIN64
|
|
pvAlloc = _LH_Alloc(hlh->_hlhData, (UINT)cb);
|
|
#else
|
|
{
|
|
Assert(hlh->_hlhData);
|
|
Assert(cb);
|
|
Assert(HeapValidate(hlh->_hlhData, 0, NULL));
|
|
pvAlloc = _LH_Alloc(hlh->_hlhData, (UINT)cb);
|
|
}
|
|
#endif
|
|
|
|
if (pvAlloc)
|
|
{
|
|
plhblk = (PLHBLK)_LH_Alloc(hlh->_hlhBlks, sizeof(LHBLK));
|
|
|
|
if (plhblk)
|
|
{
|
|
plhblk->hlh = hlh;
|
|
plhblk->szName[0] = 0;
|
|
plhblk->ulSize = cb;
|
|
plhblk->ulAllocNum = ++hlh->ulAllocNum;
|
|
plhblk->pv = pvAlloc;
|
|
|
|
PlhblkEnqueue(plhblk);
|
|
|
|
#if defined(WIN32) && defined(_X86_) && defined(LEAK_TEST)
|
|
GetCallStack((DWORD *)plhblk->pfnCallers, 0, NCALLERS);
|
|
#endif
|
|
|
|
if (hlh->ulFlags & HEAP_FILL_MEM)
|
|
memset(pvAlloc, hlh->chFill, (size_t)cb);
|
|
}
|
|
else
|
|
{
|
|
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
|
|
VMFreeEx(pvAlloc, 4);
|
|
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
|
|
VMFreeEx(pvAlloc, 1);
|
|
else
|
|
_LH_Free(hlh->_hlhData, pvAlloc);
|
|
|
|
pvAlloc = NULL;
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
LH_LeaveCriticalSection(hlh);
|
|
|
|
return pvAlloc;
|
|
}
|
|
|
|
|
|
LPVOID WINAPI LH_Realloc(HLH hlh, LPVOID pv, UINT cb)
|
|
{
|
|
LPVOID pvNew = NULL;
|
|
|
|
LH_EnterCriticalSection(hlh);
|
|
|
|
if (pv == 0)
|
|
pvNew = LH_Alloc(hlh, cb);
|
|
else if (cb == 0)
|
|
LH_Free(hlh, pv);
|
|
else if (LH_ValidatePv(hlh, pv, "LH_Realloc"))
|
|
{
|
|
PLHBLK plhblk = PvToPlhblk(hlh, pv);
|
|
UINT cbOld = (UINT)CbPlhblkClient(plhblk);
|
|
|
|
PlhblkDequeue(plhblk);
|
|
|
|
|
|
if (cb > cbOld &&
|
|
((hlh->ulFlags & HEAP_FAILURES_ENABLED) && FForceFailure(hlh, cb)))
|
|
{
|
|
hlh->ulAllocNum++;
|
|
pvNew = 0;
|
|
DebugTrace(TEXT("LH_Realloc: Artificial Failure\n"));
|
|
} else if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
|
|
pvNew = VMReallocEx(pv, cb, 4);
|
|
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
|
|
pvNew = VMReallocEx(pv, cb, 1);
|
|
else if (cb > UINT_MAX)
|
|
pvNew = 0;
|
|
else
|
|
pvNew = _LH_Realloc(hlh->_hlhData, pv, (UINT)cb);
|
|
|
|
PlhblkEnqueue(plhblk);
|
|
|
|
|
|
if (pvNew)
|
|
{
|
|
hlh->ulAllocNum++;
|
|
|
|
plhblk->pv = pvNew;
|
|
plhblk->ulSize = cb;
|
|
|
|
if (cb > cbOld)
|
|
memset((LPBYTE)pvNew + cbOld, hlh->chFill, cb - cbOld);
|
|
}
|
|
}
|
|
|
|
LH_LeaveCriticalSection(hlh);
|
|
|
|
return pvNew;
|
|
}
|
|
|
|
|
|
void WINAPI LH_Free(HLH hlh, LPVOID pv)
|
|
{
|
|
PLHBLK plhblk;
|
|
|
|
LH_EnterCriticalSection(hlh);
|
|
|
|
if (pv && LH_ValidatePv(hlh, pv, "LH_Free"))
|
|
{
|
|
plhblk = PvToPlhblk(hlh, pv);
|
|
|
|
PlhblkDequeue(plhblk);
|
|
|
|
memset(pv, 0xDC, (size_t)CbPlhblkClient(plhblk));
|
|
|
|
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
|
|
VMFreeEx(pv, 4);
|
|
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
|
|
VMFreeEx(pv, 1);
|
|
else
|
|
_LH_Free(hlh->_hlhData, pv);
|
|
|
|
_LH_Free(hlh->_hlhBlks, plhblk);
|
|
}
|
|
|
|
LH_LeaveCriticalSection(hlh);
|
|
}
|
|
|
|
|
|
UINT WINAPI LH_GetSize(HLH hlh, LPVOID pv)
|
|
{
|
|
UINT cb = 0;
|
|
|
|
LH_EnterCriticalSection(hlh);
|
|
|
|
if (LH_ValidatePv(hlh, pv, "LH_GetSize"))
|
|
{
|
|
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
|
|
cb = (UINT)VMGetSizeEx(pv, 4);
|
|
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
|
|
cb = (UINT)VMGetSizeEx(pv, 1);
|
|
else
|
|
cb = (UINT) _LH_GetSize(hlh->_hlhData, pv);
|
|
}
|
|
|
|
LH_LeaveCriticalSection(hlh);
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
void __cdecl LH_SetHeapNameFn(HLH hlh, TCHAR *pszFormat, ...)
|
|
{
|
|
TCHAR sz[512];
|
|
va_list vl;
|
|
|
|
va_start(vl, pszFormat);
|
|
wvnsprintf(sz, ARRAYSIZE(sz), pszFormat, vl);
|
|
va_end(vl);
|
|
|
|
StrCpyN(hlh->szHeapName,
|
|
sz,
|
|
CharSizeOf(hlh->szHeapName));
|
|
}
|
|
|
|
void __cdecl EXPORT_16 LH_SetNameFn(HLH hlh, LPVOID pv, TCHAR *pszFormat, ...)
|
|
{
|
|
TCHAR sz[512];
|
|
PLHBLK plhblk;
|
|
va_list vl;
|
|
|
|
plhblk = PvToPlhblk(hlh, pv);
|
|
|
|
if (plhblk)
|
|
{
|
|
va_start(vl, pszFormat);
|
|
wvnsprintf(sz, ARRAYSIZE(sz), pszFormat, vl);
|
|
va_end(vl);
|
|
|
|
StrCpyN(plhblk->szName, sz, CharSizeOf(plhblk->szName));
|
|
}
|
|
}
|
|
|
|
// $MAC - Need WINAPI
|
|
|
|
TCHAR *
|
|
#ifdef MAC
|
|
WINAPI
|
|
#endif
|
|
LH_GetName(HLH hlh, LPVOID pv)
|
|
{
|
|
PLHBLK plhblk;
|
|
|
|
plhblk = PvToPlhblk(hlh, pv);
|
|
|
|
if (plhblk)
|
|
return(plhblk->szName);
|
|
|
|
return(szEmpty);
|
|
}
|
|
|
|
|
|
BOOL FForceFailure(HLH hlh, UINT cb)
|
|
{
|
|
//
|
|
// First, see if we're past our start of failures point
|
|
//
|
|
if (hlh->ulFailStart && (hlh->ulFailStart <= hlh->ulAllocNum))
|
|
{
|
|
//
|
|
// If so, then are we at an interval where we should return errors?
|
|
//
|
|
|
|
if ((hlh->ulFailInterval)
|
|
&& ((hlh->ulAllocNum - hlh->ulFailStart)%hlh->ulFailInterval) == 0)
|
|
{
|
|
//
|
|
// return that we should fail here
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check to see if the alloc size is greater than allowed
|
|
//
|
|
if (hlh->uiFailBufSize && cb >= hlh->uiFailBufSize)
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, no error is returned for this alloc
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
- PvToPlhblk
|
|
-
|
|
* Purpose:
|
|
* Finds the LHBLK for this allocation in the heap's active list.
|
|
*/
|
|
|
|
PLHBLK PvToPlhblk(HLH hlh, LPVOID pv)
|
|
{
|
|
PLHBLK plhblk;
|
|
|
|
LH_EnterCriticalSection(hlh);
|
|
|
|
plhblk = hlh->plhblkHead;
|
|
|
|
while (plhblk)
|
|
{
|
|
if (plhblk->pv == pv)
|
|
break;
|
|
|
|
plhblk = plhblk->plhblkNext;
|
|
}
|
|
|
|
LH_LeaveCriticalSection(hlh);
|
|
|
|
return plhblk;
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
#ifdef MAC // MAC!!
|
|
|
|
#if defined(DEBUG)
|
|
static TCHAR stMemErr[] = TEXT("\pHad a memory error. See above for details");
|
|
#endif
|
|
|
|
|
|
LPVOID WINAPI _LH_Open(DWORD dwMaxHeap)
|
|
{
|
|
Ptr lp;
|
|
|
|
lp = NewPtrClear(sizeof(LHeap));
|
|
if (lp == NULL)
|
|
{
|
|
#if defined(DEBUG)
|
|
DebugTrace(TEXT("_LH_Open had an error. MemError = %d"), MemError());
|
|
DebugStr(stMemErr);
|
|
#endif /* DEBUG */
|
|
return NULL;
|
|
}
|
|
return (LPVOID)lp;
|
|
}
|
|
|
|
|
|
void WINAPI _LH_Close(LPVOID plh)
|
|
{
|
|
LBlkPtr plb, plbNext;
|
|
#if defined(DEBUG)
|
|
short idx = 0;
|
|
#endif
|
|
|
|
if (plh == NULL)
|
|
return;
|
|
|
|
// Walk the block list throwing out remaining mem as we go along.
|
|
plb = ((LHeapPtr)plh)->plb;
|
|
while (plb)
|
|
{
|
|
plbNext = plb->next;
|
|
DisposePtr((Ptr)plb);
|
|
#if defined(DEBUG)
|
|
if (MemError())
|
|
{
|
|
DebugTrace(TEXT("_LH_Close: Had a memory error."));
|
|
DebugTrace(TEXT("Error number = %d"), MemError());
|
|
DebugStr(stMemErr);
|
|
}
|
|
idx ++;
|
|
#endif
|
|
plb = plbNext;
|
|
}
|
|
|
|
// Throw out the heap header.
|
|
DisposePtr((Ptr)plh);
|
|
#if defined(DEBUG)
|
|
if (MemError())
|
|
{
|
|
DebugTrace(TEXT("_LH_Close: Had error throwing out heap head."));
|
|
DebugTrace(TEXT("MemError = %d"), MemError());
|
|
DebugStr(stMemErr);
|
|
}
|
|
if (idx)
|
|
DebugTrace(TEXT("Threw out %d left over local memory blocks\n"), idx);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
|
|
LPVOID WINAPI _LH_Alloc(LPVOID plh, UINT cb)
|
|
{
|
|
LBlkPtr plbNew, plb;
|
|
Ptr lp;
|
|
|
|
if (plh == NULL)
|
|
return NULL;
|
|
|
|
// Get memory for the linked list element. Mem requests are stored in a
|
|
// linked list off a 'heap' head because real heap management is such a
|
|
// pain on the Mac.
|
|
plbNew = (LBlkPtr)NewPtr(sizeof(LBlock));
|
|
if (plbNew == NULL)
|
|
goto trouble;
|
|
|
|
// Memory for the actual request.
|
|
lp = NewPtrClear(cb);
|
|
if (lp == NULL)
|
|
{
|
|
DisposePtr((Ptr)plbNew);
|
|
goto trouble;
|
|
}
|
|
// All members of LBlock are filled in so there's no need to call
|
|
// NewPtrclear() above.
|
|
plbNew->ptr = lp;
|
|
plbNew->next = NULL;
|
|
|
|
// Find the end of the linked list and link this element in.
|
|
if (plb = ((LHeapPtr)plh)->plb)
|
|
{
|
|
while (plb->next)
|
|
plb = plb->next;
|
|
plb->next = plbNew;
|
|
}
|
|
else
|
|
((LHeapPtr)plh)->plb = plbNew;
|
|
// Return the successfully allocated memory.
|
|
return lp;
|
|
|
|
trouble:
|
|
{
|
|
#if defined(DEBUG)
|
|
DebugTrace(TEXT("_LH_Alloc failed. MemError = %d"), MemError());
|
|
DebugTrace(TEXT("The number of requested bytes = %d"), cb);
|
|
DebugStr(stMemErr);
|
|
#endif /* DEBUG */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
UINT WINAPI _LH_GetSize(LPVOID plh, LPVOID pv)
|
|
{
|
|
long cb;
|
|
|
|
cb = GetPtrSize((Ptr)pv);
|
|
if (MemError())
|
|
{
|
|
#if defined(DEBUG)
|
|
DebugTrace(TEXT("_LH_GetSize had an error. MemError = %d"), MemError());
|
|
DebugStr(stMemErr);
|
|
#endif /* DEBUG */
|
|
return 0;
|
|
}
|
|
return cb;
|
|
}
|
|
|
|
|
|
LPVOID WINAPI _LH_Realloc(LPVOID plh, LPVOID pv, UINT cb)
|
|
{
|
|
Ptr lp;
|
|
UINT cbOld;
|
|
|
|
// Get rid of schizo cases.
|
|
if (pv == NULL)
|
|
{
|
|
lp = _LH_Alloc(plh, cb);
|
|
if (lp == NULL)
|
|
goto err;
|
|
return lp;
|
|
}
|
|
else if (cb == 0)
|
|
{
|
|
_LH_Free(plh, pv);
|
|
return NULL;
|
|
}
|
|
|
|
// Get the size of the block the old ptr pointed to.
|
|
cbOld = _LH_GetSize(plh, pv);
|
|
if (cbOld == 0)
|
|
goto err;
|
|
|
|
// Get memory for the new pointer.
|
|
lp = _LH_Alloc(plh, cb);
|
|
if (lp == NULL)
|
|
goto err;
|
|
|
|
// Copy the old info into the new pointer, throw out the old mem and
|
|
// return the result.
|
|
BlockMove(pv, lp, cbOld <= cb ? cbOld : cb);
|
|
_LH_Free(plh, pv);
|
|
return lp;
|
|
|
|
err:
|
|
#if defined(DEBUG)
|
|
DebugStr("\p_LH_Realloc failed");
|
|
#endif /* DEBUG */
|
|
return 0;
|
|
}
|
|
|
|
|
|
void WINAPI _LH_Free(LPVOID plh, LPVOID pv)
|
|
{
|
|
LBlkPtr plb, plbPrev = NULL;
|
|
|
|
if (pv == NULL)
|
|
return;
|
|
|
|
// Remove the memory from the linked list.
|
|
plb = ((LHeapPtr)plh)->plb;
|
|
while (plb)
|
|
{
|
|
if (plb->ptr == pv)
|
|
break;
|
|
plbPrev = plb;
|
|
plb = plb->next;
|
|
}
|
|
if (plb)
|
|
{
|
|
if (plbPrev)
|
|
plbPrev->next = plb->next;
|
|
else
|
|
((LHeapPtr)plh)->plb = plb->next;
|
|
}
|
|
else
|
|
{
|
|
#if defined(DEBUG)
|
|
DebugStr("\p_LH_Free: Did not find requested <plb> in linked list");
|
|
#endif /* DEBUG */
|
|
return;
|
|
}
|
|
|
|
// Throw out the linked list element.
|
|
DisposePtr((Ptr)plb);
|
|
#if defined(DEBUG)
|
|
if (MemError())
|
|
goto err;
|
|
#endif /* DEBUG */
|
|
|
|
// Throw out the memory itself.
|
|
DisposePtr((Ptr)pv);
|
|
#if defined(DEBUG)
|
|
if (MemError())
|
|
err:
|
|
{
|
|
DebugTrace(TEXT("_LH_Free: Error disposing ptr. MemError = %d"), MemError());
|
|
DebugStr(stMemErr);
|
|
}
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
#endif /* MAC */
|