windows-nt/Source/XPSP1/NT/base/win32/fusion/utils/fusionheap.cpp

818 lines
24 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "stdinc.h"
#include "fusionheap.h"
#include "fusionbuffer.h"
#include "debmacro.h"
#define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0]))
#define MAX_VTBL_ENTRIES 256
FUSION_HEAP_HANDLE g_hHeap = NULL;
#if FUSION_DEBUG_HEAP
FUSION_HEAP_HANDLE g_hDebugInfoHeap = NULL;
LONG g_FusionHeapAllocationCount = 0;
LONG g_FusionHeapAllocationToBreakOn = 0;
PVOID g_FusionHeapDeallocationPtrToBreakOn = NULL;
PVOID g_FusionHeapAllocationPtrToBreakOn = NULL;
WCHAR g_FusionModuleNameUnknown[] = L"(Unknown module)";
//
// g_FusionHeapOperationCount is used to keep track of the total number of
// allocations and deallocations; the heap is verified each
// g_FusionHeapCheckFrequency operations. Set it to zero to disable any
// non-explicit checks.
//
LONG g_FusionHeapOperationCount = 0;
LONG g_FusionHeapCheckFrequency = 1;
// Set g_FusionUsePrivateHeap to TRUE in your DllMain prior to
// calling FusionpInitializeHeap() to get a private heap for this DLL.
BOOL g_FusionUsePrivateHeap = FALSE;
//
// Setting this boolean enables stack-back-tracing for allocations. This
// will make your life infinitely easier when trying to debug leaks. However,
// it will eat piles more debug heap. Set it via a breakin or in your DllMain.
//
// ABSOLUTELY DO NOT CHECK THIS IN WITH STACK TRACKING ENABLED!!!!!
//
BOOL g_FusionHeapTrackStackTraces = FALSE;
// g_FusionHeapPostAllocationBytes is the number of extra bytes
// to allocate and write a pattern on to watch for people overwriting
// their allocations.
LONG g_FusionHeapPostAllocationBytes = 8;
UCHAR g_FusionHeapPostAllocationChar = 0xf0;
UCHAR g_FusionHeapAllocationPoisonChar = 0xfa;
UCHAR g_FusionHeapDeallocationPoisonChar = 0xfd;
// HINSTANCE used when initializing the heap; we use it later to report the
// dll name.
HINSTANCE g_FusionHeapHInstance;
#endif // FUSION_DEBUG_HEAP
BOOL
FusionpInitializeHeap(
HINSTANCE hInstance
)
{
#if FUSION_DEBUG_HEAP
BOOL fSuccess = FALSE;
#if FUSION_PRIVATE_HEAP
g_hHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
if (g_hHeap == NULL)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"SXS: Failed to create private heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
goto Exit;
}
#else
if (g_FusionUsePrivateHeap)
{
g_hHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
if (g_hHeap == NULL)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"SXS: Failed to create private heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
goto Exit;
}
}
else
{
g_hHeap = (FUSION_HEAP_HANDLE) ::GetProcessHeap();
if (g_hHeap == NULL)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"SXS: Failed to get process default heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
goto Exit;
}
}
#endif
g_hDebugInfoHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
if (g_hDebugInfoHeap == NULL)
{
goto Exit;
}
g_FusionHeapHInstance = hInstance;
fSuccess = TRUE;
Exit:
return fSuccess;
#else
g_hHeap = (FUSION_HEAP_HANDLE) ::GetProcessHeap();
return TRUE;
#endif
}
VOID
FusionpUninitializeHeap()
{
#if FUSION_DEBUG_HEAP
BOOL fHeapLocked = FALSE;
BOOL fDebugHeapLocked = FALSE;
PROCESS_HEAP_ENTRY phe;
WCHAR DllName[MAX_PATH / sizeof(WCHAR)]; // keep stack frame to ~MAX_PATH bytes
if (g_hHeap == NULL || g_hDebugInfoHeap == NULL)
goto Exit;
DllName[0] = 0;
if (g_FusionHeapHInstance != NULL)
::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
if (!::HeapLock(g_hHeap))
goto ReportError;
fHeapLocked = TRUE;
if (!::HeapLock(g_hDebugInfoHeap))
goto ReportError;
fDebugHeapLocked = TRUE;
// Walk the debug heap looking for allocations...
phe.lpData = NULL;
while (::HeapWalk(g_hDebugInfoHeap, &phe))
{
if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
continue;
PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
if (pTracker == NULL)
continue;
if (pTracker->Prefix == NULL)
continue;
// Stop the prefix from pointing at the debug info; we're doing to destroy the debug heap.
pTracker->Prefix->Tracker = NULL;
}
//
// On invalid function, meaning HeapWalk is not defined, just exit.
// Same for no-more-items, meaning the end of the list is nigh. We
// make the assumption that none of the other functions in the loop
// can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
//
switch (::FusionpGetLastWin32Error())
{
case ERROR_INVALID_FUNCTION:
case ERROR_NO_MORE_ITEMS:
goto Exit;
default:
goto ReportError;
}
// Original code:
//
// if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
// goto ReportError;
//
goto Exit;
ReportError:
FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%s: FusionpUninitializeHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllName, ::FusionpGetLastWin32Error());
Exit:
if (fDebugHeapLocked)
::HeapUnlock(g_hDebugInfoHeap);
if (fHeapLocked)
::HeapUnlock(g_hHeap);
if (g_hDebugInfoHeap != NULL)
::HeapDestroy(g_hDebugInfoHeap);
#if 0 // should do this, but need to test it, and no diff due to #ifdefs
if (g_hHeap != NULL && g_Heap != ::GetProcessHeap())
::HeapDestroy(g_hHeap);
#endif
g_hHeap = NULL;
g_hDebugInfoHeap = NULL;
#endif
}
#if FUSION_DEBUG_HEAP
VOID
FusionpDumpHeap(
PCWSTR PerLinePrefixWithoutSpaces
)
{
BOOL fHeapLocked = FALSE;
BOOL fDebugHeapLocked = FALSE;
PROCESS_HEAP_ENTRY phe;
WCHAR DllName[MAX_PATH / sizeof(WCHAR) / 2];
WCHAR PerLinePrefix[MAX_PATH / sizeof(WCHAR) / 2]; // only MAX_PATH bytes for prev two variables
const static WCHAR PerLineSpacesPrefix[] = L" ";
if (g_hHeap == NULL || g_hDebugInfoHeap == NULL)
goto Exit;
// sprintf is overkill, but convenient, and it lets us reuse the buffer size support..
::_snwprintf(PerLinePrefix, NUMBER_OF(PerLinePrefix), L"%S%S", PerLinePrefixWithoutSpaces, PerLineSpacesPrefix);
PerLinePrefix[NUMBER_OF(PerLinePrefix) - 1] = L'\0';
DllName[0] = 0;
::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
try
{
if (!::HeapLock(g_hHeap))
goto ReportError;
fHeapLocked = TRUE;
if (!::HeapLock(g_hDebugInfoHeap))
goto ReportError;
fDebugHeapLocked = TRUE;
// Walk the debug heap looking for allocations...
phe.lpData = NULL;
while (::HeapWalk(g_hDebugInfoHeap, &phe))
{
PCSTR HeapString;
SIZE_T cbToShow;
if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
continue;
PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
if (pTracker == NULL)
continue;
if (pTracker->Prefix == NULL)
continue;
#if 0 // as long as the buffer isn't heap allocated, this check isn't needed
// Skip the buffer we're using for formatting the output...
if (((PCWSTR) (((ULONG_PTR) pTracker->Prefix) + sizeof(FUSION_HEAP_PREFIX))) == PerLinePrefix)
continue;
#endif
// If the caller wanted us to not report this allocation as being leaky, don't.
if (pTracker->Flags & FUSION_HEAP_DO_NOT_REPORT_LEAKED_ALLOCATION)
continue;
if (pTracker->Heap == g_hHeap)
HeapString = "default heap";
else
HeapString = "custom heap";
cbToShow = pTracker->RequestedSize;
if (cbToShow > 64)
cbToShow = 64;
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%s(%u): Memory allocation leaked!\n", pTracker->FileName, pTracker->Line);
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%SLeaked %S allocation #%u (0x%lx) at %p \"%s\" (tracked by %p; allocated from heap %p - %s)\n"
"%S Requested bytes/Allocated bytes: %Iu / %Iu (dumping %Iu bytes)\n",
PerLinePrefix, DllName, pTracker->SequenceNumber, pTracker->SequenceNumber, pTracker->Prefix, pTracker->Expression, pTracker, pTracker->Heap, HeapString,
PerLinePrefix, pTracker->RequestedSize, pTracker->AllocationSize, cbToShow);
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%s Allocated at line %u of %s\n",
PerLinePrefix, pTracker->Line, pTracker->FileName);
#if FUSION_ENABLE_FROZEN_STACK
if (pTracker->pvFrozenStack)
::FusionpOutputFrozenStack(
FUSION_DBG_LEVEL_ERROR,
"SXS",
(PFROZEN_STACK)pTracker->pvFrozenStack);
#endif
FusionpDbgPrintBlob(
FUSION_DBG_LEVEL_ERROR,
pTracker->Prefix + 1,
cbToShow,
PerLinePrefix);
}
//
// On invalid function, meaning HeapWalk is not defined, just exit.
// Same for no-more-items, meaning the end of the list is nigh. We
// make the assumption that none of the other functions in the loop
// can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
//
switch (::FusionpGetLastWin32Error())
{
case ERROR_INVALID_FUNCTION:
case ERROR_NO_MORE_ITEMS:
goto Exit;
default:
goto ReportError;
}
// Original code:
//
// if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
// goto ReportError;
//
}
catch(...)
{
}
goto Exit;
ReportError:
FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%S: FusionpDumpHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllName, ::FusionpGetLastWin32Error());
Exit:
if (fDebugHeapLocked)
::HeapUnlock(g_hDebugInfoHeap);
if (fHeapLocked)
::HeapUnlock(g_hHeap);
}
VOID
FusionpValidateHeap(
FUSION_HEAP_HANDLE hFusionHeap
)
{
FN_TRACE();
BOOL fHeapLocked = FALSE;
BOOL fDebugHeapLocked = FALSE;
PROCESS_HEAP_ENTRY phe;
SIZE_T i;
WCHAR DllName[MAX_PATH / sizeof(WCHAR)]; // keep stack frame to ~MAX_PATH bytes
PCWSTR DllNamePointer = DllName;
DWORD dwCallStatus;
HANDLE hHeap = (HANDLE) hFusionHeap;
DllName[0] = 0;
if (g_hDebugInfoHeap == NULL)
goto Exit;
//
// Get the current module's name, but don't print garbage if it fails.
//
dwCallStatus = ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
if (!dwCallStatus)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"FusionpValidateHeap() was unable to get the current module name, code = %d\n",
::FusionpGetLastWin32Error());
//
// Blank the name, insert something relevant.
//
DllNamePointer = g_FusionModuleNameUnknown;
}
try
{
if (hHeap != NULL)
{
if (!::HeapLock(hHeap))
goto ReportError;
fHeapLocked = TRUE;
}
if (!::HeapLock(g_hDebugInfoHeap))
goto ReportError;
fDebugHeapLocked = TRUE;
// Walk the debug heap looking for allocations...
phe.lpData = NULL;
while (::HeapWalk(g_hDebugInfoHeap, &phe))
{
PCSTR HeapString;
SIZE_T cbToShow;
if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
continue;
PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
if (pTracker == NULL)
continue;
if (pTracker->Prefix == NULL)
continue;
// If we're checking only a particular heap, skip...
if ((hHeap != NULL) && (pTracker->Heap != hHeap))
continue;
if (pTracker->PostAllocPoisonArea == NULL)
continue;
// The area should have been NULL if the count of bytes was nonzero...
ASSERT(pTracker->PostAllocPoisonBytes != 0);
PUCHAR PostAllocPoisonArea = pTracker->PostAllocPoisonArea;
const UCHAR PostAllocPoisonChar = pTracker->PostAllocPoisonChar;
const ULONG PostAllocPoisonBytes = pTracker->PostAllocPoisonBytes;
for (i=0; i<PostAllocPoisonBytes; i++)
{
if (PostAllocPoisonArea[i] != PostAllocPoisonChar)
break;
}
// The poison area looks good; skip...
if (i == PostAllocPoisonBytes)
continue;
if (pTracker->Heap == g_hHeap)
HeapString = "default heap";
else
HeapString = "custom heap";
cbToShow = pTracker->RequestedSize;
if (cbToShow > 64)
cbToShow = 64;
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"Wrote past end of %S allocation #%u (0x%lx) at %p \"%s\" (tracked by %p; allocated from heap %p - %s)\n"
" Requested bytes/Allocated bytes: %Iu / %Iu (dumping %Iu bytes)\n",
DllNamePointer, pTracker->SequenceNumber, pTracker->SequenceNumber, pTracker->Prefix, pTracker->Expression, pTracker, pTracker->Heap, HeapString,
pTracker->RequestedSize, pTracker->AllocationSize, cbToShow);
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
" Allocated at line %u of %s\n",
pTracker->Line, pTracker->FileName);
FusionpDbgPrintBlob(
FUSION_DBG_LEVEL_ERROR,
pTracker->Prefix + 1,
cbToShow,
L"");
FusionpDbgPrintBlob(
FUSION_DBG_LEVEL_ERROR,
pTracker->PostAllocPoisonArea,
pTracker->PostAllocPoisonBytes,
L"");
}
//
// On invalid function, meaning HeapWalk is not defined, just exit.
// Same for no-more-items, meaning the end of the list is nigh. We
// make the assumption that none of the other functions in the loop
// can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
//
switch (::FusionpGetLastWin32Error())
{
case ERROR_INVALID_FUNCTION:
case ERROR_NO_MORE_ITEMS:
goto Exit;
default:
goto ReportError;
}
// Original code:
//
// if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
// goto ReportError;
//
}
catch(...)
{
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%S: Exception while validating heap.\n", DllNamePointer);
}
goto Exit;
ReportError:
FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%S: FusionpValidateHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllNamePointer, ::FusionpGetLastWin32Error());
Exit:
if (fDebugHeapLocked)
::HeapUnlock(g_hDebugInfoHeap);
if (fHeapLocked)
::HeapUnlock(hHeap);
}
PVOID
FusionpDbgHeapAlloc(
FUSION_HEAP_HANDLE hHeap,
DWORD dwHeapAllocFlags,
SIZE_T cb,
PCSTR pszFile,
INT nLine,
PCSTR pszExpression,
DWORD dwFusionFlags
)
{
FN_TRACE();
BOOL fSuccess = FALSE;
BOOL fDebugHeapLocked = FALSE;
SIZE_T cbAdditionalBytes = 0;
#if FUSION_ENABLE_FROZEN_STACK
// BOOL bShouldTraceStack = (g_FusionHeapTrackStackTraces && (::TlsGetValue(g_FusionHeapTrackingDisabledDepthTLSIndex) == 0));
BOOL bShouldTraceStack = g_FusionHeapTrackStackTraces;
FROZEN_STACK Prober = { 0 };
#endif
ASSERT(hHeap != NULL);
LONG lAllocationSequenceNumber = ::InterlockedIncrement(&g_FusionHeapAllocationCount);
if ((g_FusionHeapAllocationToBreakOn != 0) &&
(lAllocationSequenceNumber == g_FusionHeapAllocationToBreakOn))
{
// Break in to the debugger, even if we're not in a checked build.
FUSION_DEBUG_BREAK_IN_FREE_BUILD();
}
LONG lOperationSequenceNumber = ::InterlockedIncrement(&g_FusionHeapOperationCount);
if ((g_FusionHeapCheckFrequency != 0) && ((lOperationSequenceNumber % g_FusionHeapCheckFrequency) == 0))
{
// Check the active heap allocations for correct post-block signatures...
// ::FusionpValidateHeap(NULL);
}
PSTR psz = NULL;
SIZE_T cbFile = (pszFile == NULL) ? 0 : ::strlen(pszFile) + 1;
SIZE_T cbExpression = (pszExpression == NULL) ? 0 : ::strlen(pszExpression) + 1;
PFUSION_HEAP_ALLOCATION_TRACKER pTracker = NULL;
// Make a copy of the global variable so that if someone breaks in in the debugger
// and changes it while we're in the middle of this code we don't die horribly.
const ULONG cbPostAllocationBytes = g_FusionHeapPostAllocationBytes;
const UCHAR chPostAllocationChar = g_FusionHeapPostAllocationChar;
const SIZE_T cbToAllocate = (sizeof(FUSION_HEAP_PREFIX) + cb + cbPostAllocationBytes);
const PFUSION_HEAP_PREFIX pPrefix = reinterpret_cast<PFUSION_HEAP_PREFIX>(::HeapAlloc(hHeap, dwHeapAllocFlags, cbToAllocate));
if (pPrefix == NULL)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%s(%d): [SXS.DLL] Heap allocation failure allocating %Iu (really %Iu) bytes\n", pszFile, nLine, cb, cbToAllocate);
::SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
// lock the debug info heap to allocate memory for pTracker
if (!::HeapLock(g_hDebugInfoHeap))
goto Exit;
fDebugHeapLocked = TRUE;
//
// Are we tracing the stack? If so, then we need to allocate some extra bytes
// on the end of this tracker to store the context.
//
#if FIXBEFORECHECKIN
if (bShouldTraceStack)
{
BOOL bSuccess = ::FusionpFreezeStack(NULL, 0, &Prober);
if (!bSuccess && (::FusionpGetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)) {
cbAdditionalBytes = sizeof(FROZEN_STACK) + (sizeof(TRACECONTEXT) * Prober.ulMaxDepth);
} else {
cbAdditionalBytes = 0;
bShouldTraceStack = FALSE;
}
}
else
#endif // FIXBEFORECHECKIN
cbAdditionalBytes = 0;
pTracker = reinterpret_cast<PFUSION_HEAP_ALLOCATION_TRACKER>(::HeapAlloc(
g_hDebugInfoHeap,
0,
sizeof(FUSION_HEAP_ALLOCATION_TRACKER)
+ FUSION_HEAP_ROUND_SIZE(cbFile)
+ FUSION_HEAP_ROUND_SIZE(cbExpression)
+ FUSION_HEAP_ROUND_SIZE(cbAdditionalBytes)));
if (pTracker == NULL)
{
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"%s(%d): [SXS.DLL] Heap allocation failure allocating tracker for %lu bytes\n", pszFile, nLine, cb);
::HeapFree(hHeap, 0, pPrefix);
::SetLastError(ERROR_OUTOFMEMORY);
goto Exit;
}
pPrefix->Tracker = pTracker;
pTracker->Prefix = pPrefix;
pTracker->Heap = hHeap;
pTracker->SequenceNumber = lAllocationSequenceNumber;
pTracker->PostAllocPoisonBytes = cbPostAllocationBytes;
if (cbPostAllocationBytes != 0)
{
const PUCHAR pb = (UCHAR *) (((ULONG_PTR) (pPrefix + 1)) + cb);
ULONG i;
pTracker->PostAllocPoisonArea = (PUCHAR) pb;
pTracker->PostAllocPoisonChar = chPostAllocationChar;
for (i=0; i<cbPostAllocationBytes; i++)
pb[i] = chPostAllocationChar;
}
else
{
pTracker->PostAllocPoisonArea = NULL;
}
psz = (PSTR) (pTracker + 1);
if (cbFile != 0)
{
pTracker->FileName = psz;
memcpy(psz, pszFile, cbFile);
psz += FUSION_HEAP_ROUND_SIZE(cbFile);
}
else
pTracker->FileName = NULL;
if (cbExpression != 0)
{
pTracker->Expression = psz;
memcpy(psz, pszExpression, cbExpression);
psz += FUSION_HEAP_ROUND_SIZE(cbExpression);
}
else
pTracker->Expression = NULL;
#if FUSION_ENABLE_FROZEN_STACK
//
// Set up our stack tracker
//
if (bShouldTraceStack)
{
PFROZEN_STACK pStack = (PFROZEN_STACK)psz;
pTracker->pvFrozenStack = pStack;
pStack->ulDepth = 0;
pStack->ulMaxDepth = Prober.ulMaxDepth;
if (!::FusionpFreezeStack(0, pStack))
pTracker->pvFrozenStack = NULL;
}
//
// Otherwise, no stack for you.
//
else
{
pTracker->pvFrozenStack = NULL;
}
#endif
pTracker->Line = nLine;
pTracker->Flags = dwFusionFlags;
pTracker->RequestedSize = cb;
pTracker->AllocationSize = cb + sizeof(FUSION_HEAP_PREFIX);
#if 0
if (::TlsGetValue(g_FusionHeapTrackingDisabledDepthTLSIndex) != 0)
pTracker->Flags |= FUSION_HEAP_DO_NOT_REPORT_LEAKED_ALLOCATION;
#endif
// poison the allocation...
memset((pPrefix + 1), g_FusionHeapAllocationPoisonChar, cb);
if ((g_FusionHeapAllocationPtrToBreakOn != 0) &&
((pPrefix + 1) == g_FusionHeapAllocationPtrToBreakOn))
{
// Break in to the debugger, even if we're not in a checked build.
FUSION_DEBUG_BREAK_IN_FREE_BUILD();
}
fSuccess = TRUE;
Exit:
if (fDebugHeapLocked){
DWORD dwLastError = ::FusionpGetLastWin32Error();
::HeapUnlock(g_hDebugInfoHeap);
::SetLastError(dwLastError);
}
if (fSuccess)
return (PVOID) (pPrefix + 1);
else
return NULL;
}
BOOL
FusionpDbgHeapFree(
FUSION_HEAP_HANDLE hHeap,
DWORD dwHeapFreeFlags,
PVOID pv
)
{
FN_TRACE();
PFUSION_HEAP_ALLOCATION_TRACKER pTracker;
BOOL fResult = FALSE;
ASSERT(hHeap != NULL);
if (pv == NULL)
return FALSE;
if ((g_FusionHeapDeallocationPtrToBreakOn != NULL) &&
(pv == g_FusionHeapDeallocationPtrToBreakOn))
{
// Break in to the debugger, even if we're not in a checked build.
FUSION_DEBUG_BREAK_IN_FREE_BUILD();
}
// Let's see if its one of our funky ones...
PFUSION_HEAP_PREFIX p = (PFUSION_HEAP_PREFIX) (((ULONG_PTR) pv) - sizeof(FUSION_HEAP_PREFIX));
if (!::HeapValidate(hHeap, 0, p)) {
// HeapValidate failed. Fatal. Just leak the memory for now...
// ASSERT(0);
return FALSE;
}
if (!::HeapValidate(g_hDebugInfoHeap, 0, p->Tracker)) {
// HeapValidate failed. Fatal. Just leak the memory for now...
// ASSERT(0);
return FALSE;
}
pTracker = p->Tracker;
ASSERT(pTracker->Heap == hHeap);
p->Tracker->Prefix = NULL;
// poison the deallocation...
memset(p, g_FusionHeapDeallocationPoisonChar, pTracker->AllocationSize);
::HeapFree(g_hDebugInfoHeap, 0, pTracker);
fResult = ::HeapFree(hHeap, dwHeapFreeFlags, p);
return fResult;
}
VOID
FusionpDeallocateTracker(
PFUSION_HEAP_PREFIX p
)
{
CSxsPreserveLastError ple;
PFUSION_HEAP_ALLOCATION_TRACKER pTracker = p->Tracker;
::HeapFree(g_hDebugInfoHeap, 0, pTracker);
p->Tracker = NULL;
ple.Restore();
}
VOID *
FusionpGetFakeVTbl()
{
VOID *pvHeap;
// Always allocate the fake vtbl from the process heap so that it survives us nomatter what.
pvHeap = HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_VTBL_ENTRIES * sizeof(void *));
return pvHeap;
}
VOID
FusionpDontTrackBlk(
VOID *pv
)
{
PFUSION_HEAP_PREFIX p;
p = (PFUSION_HEAP_PREFIX) (((ULONG_PTR)pv) - sizeof(FUSION_HEAP_PREFIX));
FusionpDeallocateTracker(p);
p->Tracker = NULL;
}
#endif // FUSION_DEBUG_HEAP