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.
933 lines
27 KiB
933 lines
27 KiB
#include "stdinc.h"
|
|
#include "fusionheap.h"
|
|
#include "fusionbuffer.h"
|
|
#include "debmacro.h"
|
|
#include "numberof.h"
|
|
|
|
#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)";
|
|
|
|
// NTRAID#NTBUG9 - 589824 - 2002/03/26 - xiaoyuw:
|
|
// This is used to support uitl.lib on win98 because HeapXXX is not avaiable on win98.
|
|
// FusionpHeapLock is defined to replace HeapLock to work around for certain codepath,
|
|
// and there would be some problem if debug heap is enabled, for example, HeapWalk is
|
|
// always upsupported at win9x.
|
|
//
|
|
// we could consider to remove this someday. and just set FUSION_DEBUG_HEAP to be 0 if
|
|
// the system is a win9x. FUSION_DISABLE_DEBUG_HEAP_ON_WIN98 is has already been provided.
|
|
|
|
#if defined(FUSION_WIN2000)
|
|
CRITICAL_SECTION g_FusionpWin9xHeapLock;
|
|
#endif
|
|
|
|
//
|
|
// 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 defined(FUSION_WIN2000)
|
|
if (!FusionpInitializeCriticalSection(&g_FusionpWin9xHeapLock))
|
|
{
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
#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)
|
|
{
|
|
DWORD dwLen;
|
|
dwLen = ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
|
|
if (dwLen >= NUMBER_OF(DllName))
|
|
dwLen = NUMBER_OF(DllName) - 1;
|
|
|
|
DllName[dwLen] = UNICODE_NULL;
|
|
}
|
|
|
|
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);
|
|
|
|
g_hHeap = NULL;
|
|
g_hDebugInfoHeap = NULL;
|
|
|
|
#if defined(FUSION_WIN2000)
|
|
DeleteCriticalSection(&g_FusionpWin9xHeapLock);
|
|
#endif
|
|
#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" ";
|
|
DWORD dwLen;
|
|
|
|
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;
|
|
dwLen = ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
|
|
if (dwLen >= NUMBER_OF(DllName))
|
|
dwLen = NUMBER_OF(DllName) - 1;
|
|
DllName[dwLen] = UNICODE_NULL;
|
|
|
|
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 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)
|
|
{
|
|
#if defined(FUSION_WIN2000)
|
|
if ((GetVersion() & 0x80000000) != 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
{
|
|
DllNamePointer = L"Win9x..";
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
::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);
|
|
}
|
|
|
|
#if defined(FUSION_WIN)
|
|
|
|
#define FusionpHeapLock HeapLock
|
|
#define FusionpHeapUnlock HeapUnlock
|
|
|
|
#elif defined(FUSION_WIN2000)
|
|
|
|
BOOL
|
|
FusionpHeapLock(
|
|
HANDLE hHeap
|
|
)
|
|
{
|
|
if ((GetVersion() & 0x80000000) == 0)
|
|
{ // NT
|
|
return HeapLock(hHeap);
|
|
}
|
|
else
|
|
{ // Win9x
|
|
EnterCriticalSection(&g_FusionpWin9xHeapLock);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
FusionpHeapUnlock(
|
|
HANDLE hHeap
|
|
)
|
|
{
|
|
if ((GetVersion() & 0x80000000) == 0)
|
|
{ // NT
|
|
return HeapUnlock(hHeap);
|
|
}
|
|
else
|
|
{ // Win9x
|
|
LeaveCriticalSection(&g_FusionpWin9xHeapLock);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
#error
|
|
|
|
#endif
|
|
|
|
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...
|
|
// NTRAID#NTBUG9 - 589824 - 2002/03/26 - xiaoyuw:
|
|
// this call is very reasonable except costly, why we disable it?
|
|
// ::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(FUSION_WIN32_ALLOCFAILED_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
// lock the debug info heap to allocate memory for pTracker
|
|
if (!::FusionpHeapLock(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(FUSION_WIN32_ALLOCFAILED_ERROR);
|
|
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);
|
|
|
|
// poison the allocation...
|
|
memset((pPrefix + 1), g_FusionHeapAllocationPoisonChar, cb);
|
|
|
|
// NTRAID#NTBUG9 - 589824 - 2002/03/26 - xiaoyuw:
|
|
// shouldn't pPrefix be reset after memset is called?
|
|
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){
|
|
// NTRAID#NTBUG9 - 589824 - 2002/03/26 - xiaoyuw:
|
|
// (1) use CSxsPreserveLastError instead,
|
|
// (2) in the case of failure call of FusionpHeapUnlock, we stil want to keep the error if fSuccess == TRUE at this moment.
|
|
DWORD dwLastError = ::FusionpGetLastWin32Error();
|
|
::FusionpHeapUnlock(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;
|
|
}
|
|
|
|
#else // FUSION_DEBUG_HEAP
|
|
|
|
LPVOID
|
|
WINAPI
|
|
FusionpHeapAlloc(
|
|
IN HANDLE hHeap,
|
|
IN DWORD dwFlags,
|
|
IN SIZE_T dwBytes
|
|
)
|
|
{
|
|
LPVOID p = ::HeapAlloc(hHeap, dwFlags, dwBytes);
|
|
if (p == NULL)
|
|
{
|
|
::SetLastError(FUSION_WIN32_ALLOCFAILED_ERROR);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
LPVOID
|
|
WINAPI
|
|
FusionpHeapReAlloc(
|
|
IN HANDLE hHeap,
|
|
IN DWORD dwFlags,
|
|
IN LPVOID lpMem,
|
|
IN SIZE_T dwBytes
|
|
)
|
|
{
|
|
LPVOID p = ::HeapReAlloc(hHeap, dwFlags, lpMem, dwBytes);
|
|
if (p == NULL)
|
|
{
|
|
::SetLastError(FUSION_WIN32_ALLOCFAILED_ERROR);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
#endif // FUSION_DEBUG_HEAP
|