#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; iHeap == 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(::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(::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; iPostAllocPoisonArea = 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