Leaked source code of windows server 2003
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.
 
 
 
 
 
 

4100 lines
83 KiB

/*
- E X C H M E M . C
-
* Purpose:
*
*
*
*
* Copyright (C) 1995-96, Microsoft Corporation.
*/
#define _CRTIMP __declspec(dllexport)
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
#ifdef DEBUG
#include <imagehlp.h>
#endif
#include <limits.h>
#include <exchmem.h>
#include "_exchmem.h"
#include "excpt.h"
#include "io.h"
#ifndef DEBUG
#define USEMPHEAP
#endif
#ifdef USEMPHEAP
#include <mpheap.h>
#endif
// Global heap assigned to the process by NT
HANDLE hProcessHeap = NULL;
#ifdef USEMPHEAP
HANDLE hMpHeap = NULL;
ULONG cRefHeap = -1;
#else
LPHEAPTBL pheaptbl = NULL;
CRITICAL_SECTION csMHeap;
#endif
DWORD tlsiHeapHint = 0;
// Debug Support for leak detection and memory usage tracking.
#ifdef DEBUG
static HMODULE hMod;
static HINSTANCE hinstRunTime = NULL;
static BOOL fDbgEnable = FALSE;
static BOOL fCallStacks = FALSE;
static BOOL fSymInitialize = FALSE;
static BOOL fProcessIsService = FALSE;
static LPFMALLOC pfMalloc = NULL;
static LPFREALLOC pfRealloc = NULL;
static LPFFREE pfFree = NULL;
static LPFCALLOC pfCalloc = NULL;
static LPFSTRDUP pfStrDup = NULL;
static LPFMEMSIZE pfMemSize = NULL;
static BOOL fAssertLeaks = FALSE;
static BOOL fDumpLeaks = FALSE;
static BOOL fDumpLeaksDebugger = FALSE;
static BOOL fUseVirtual = FALSE;
static ULONG cbVirtualAlign = 1;
static BOOL fFailuresEnabled = FALSE;
static BOOL fHeapMonitorUI = FALSE;
static BOOL fOverwriteDetect = FALSE;
static BOOL fValidateMemory = FALSE;
static BOOL fTrackFreedMemory = FALSE;
static DWORD cEntriesFree = 512;
static BOOL fAssertValid = FALSE;
static BOOL fTrapOnInvalid = FALSE;
static BOOL fSymbolLookup = FALSE;
static BOOL fFillMemory = FALSE;
static BYTE chAllocFillByte = chDefaultAllocFill;
static BYTE chFreeFillByte = chDefaultFreeFill;
static BOOL fTrackMem = FALSE;
static DWORD cFrames = 0;
static FILE * hTrackLog = NULL;
static CRITICAL_SECTION csTrackLog;
static char rgchExeName[16];
static char rgchLogPath[MAX_PATH];
BOOL fChangeTrackState = FALSE;
static ULONG iAllocationFault = 0;
#define NBUCKETS 8192
#define UlHash(_n) ((ULONG)(((_n & 0x000FFFF0) >> 4) % NBUCKETS))
typedef struct _symcache
{
DWORD_PTR dwAddress;
DWORD_PTR dwOffset;
CHAR rgchSymbol[248];
} SYMCACHE, * PSYMCACHE;
static PSYMCACHE rgsymcacheHashTable = NULL;
static CRITICAL_SECTION csHeapList;
static PHEAP pheapList = NULL;
CHAR * PszGetSymbolFromCache(DWORD_PTR dwAddress, DWORD_PTR * pdwOffset);
VOID AddSymbolToCache(DWORD_PTR dwAddress, DWORD_PTR dwOffset, CHAR * pszSymbol);
BOOL FTrackMem();
VOID StartTrace(BOOL fFresh);
VOID StopTrace();
typedef struct
{
WORD wApi;
DWORD_PTR rgdwCallStack[32];
DWORD_PTR rgdwArgs[5];
DWORD dwTickCount;
DWORD dwThreadId;
} MEMTRACE;
MEMTRACE * rgmemtrace = NULL;
DWORD dwmemtrace = 0;
DWORD dwTrackMemInMem = 0;
#endif // DEBUG
/*
- DllMain
-
* Purpose:
* Entry point called by CRT entry point.
*
*/
BOOL
APIENTRY
DllMain(
HANDLE hModule,
DWORD dwReason,
LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
#ifdef USEMPHEAP
tlsiHeapHint = TlsAlloc();
#else
// Init the CS that protects access to the
// global Multiple Heap data structs.
InitializeCriticalSection(&csMHeap);
// Now, if Debug build then do a lot of initialization
// including creating a debug process heap. If not
// Debug, then just get the ProcessHeap from system.
#endif
#ifdef DEBUG
InitDebugExchMem(hModule);
#else
hProcessHeap = GetProcessHeap();
#endif
}
else if (dwReason == DLL_PROCESS_DETACH)
{
#ifdef USEMPHEAP
TlsFree(tlsiHeapHint);
#else
// Delete the Multiple Heap CS
DeleteCriticalSection(&csMHeap);
#endif
// Tear-down our Debug support
#ifdef DEBUG
UnInitDebugExchMem();
#endif
}
return TRUE;
}
//-----------------------------------------------------------------------------
// The Handle based ExchMem APIs
//-----------------------------------------------------------------------------
HANDLE
WINAPI
ExchHeapCreate(
DWORD dwFlags,
DWORD dwInitialSize,
DWORD dwMaxSize)
{
#ifndef DEBUG
if (dwFlags & HEAP_NO_FREE)
dwFlags &= ~(HEAP_NO_FREE);
#endif
return ExHeapCreate(dwFlags, dwInitialSize, dwMaxSize);
}
BOOL
WINAPI
ExchHeapDestroy(
HANDLE hHeap)
{
return ExHeapDestroy(hHeap);
}
LPVOID
WINAPI
ExchHeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
DWORD dwSize)
{
return ExHeapAlloc(hHeap, dwFlags, dwSize);
}
LPVOID
WINAPI
ExchHeapReAlloc(
HANDLE hHeap,
DWORD dwFlags,
LPVOID pvOld,
DWORD dwSize)
{
if (!pvOld)
return ExchHeapAlloc(hHeap, dwFlags, dwSize);
return ExHeapReAlloc(hHeap, dwFlags, pvOld, dwSize);
}
BOOL
WINAPI
ExchHeapFree(
HANDLE hHeap,
DWORD dwFlags,
LPVOID pvFree)
{
return ExHeapFree(hHeap, dwFlags, pvFree);
}
SIZE_T
WINAPI
ExchHeapCompact(
HANDLE hHeap,
DWORD dwFlags)
{
return ExHeapCompact(hHeap, dwFlags);
}
BOOL
WINAPI
ExchHeapLock(
HANDLE hHeap)
{
return ExHeapLock(hHeap);
}
BOOL
WINAPI
ExchHeapUnlock(
HANDLE hHeap)
{
return ExHeapUnlock(hHeap);
}
BOOL
WINAPI
ExchHeapWalk(
HANDLE hHeap,
LPPROCESS_HEAP_ENTRY lpEntry)
{
return ExHeapWalk(hHeap, lpEntry);
}
SIZE_T
WINAPI
ExchHeapSize(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem)
{
return ExHeapSize(hHeap, dwFlags, lpMem);
}
BOOL
WINAPI
ExchHeapValidate(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem)
{
return ExHeapValidate(hHeap, dwFlags, lpMem);
}
//-----------------------------------------------------------------------------
// The Multiple Heap APIs
//-----------------------------------------------------------------------------
HANDLE
WINAPI
ExchMHeapCreate(
ULONG cHeaps,
DWORD dwFlags,
DWORD dwInitialSize,
DWORD dwMaxSize)
{
#ifndef USEMPHEAP
HANDLE hheap0;
HANDLE * phHeaps;
ULONG iHeap;
EnterCriticalSection(&csMHeap);
// Called twice? The first person in gets to set the number
// of heaps in the table. Subsequent calls result in an AddRef
// to the current table and this table is returned to the caller.
if (pheaptbl)
{
pheaptbl->cRef++;
goto ret;
}
// If they didn't specify or they asked for too few then we'll set this
if (cHeaps == 0)
cHeaps = cHeapsDef;
hheap0 = ExHeapCreate(dwFlags, dwInitialSize, dwMaxSize);
if (!hheap0)
{
DebugTrace("Failed to create initial heap for MHeap APIs!\n");
goto ret;
}
pheaptbl = (LPHEAPTBL)ExHeapAlloc(hheap0, 0,
sizeof(HEAPTBL) + (cHeaps-1)*sizeof(HANDLE));
if (!pheaptbl)
{
DebugTrace("Failed to allocate MHeap Table for MHeap APIs!\n");
ExHeapDestroy(hheap0);
goto ret;
}
memset(pheaptbl, 0, sizeof(HEAPTBL) + (cHeaps-1)*sizeof(HANDLE));
pheaptbl->cRef = 1;
pheaptbl->cHeaps = cHeaps;
pheaptbl->rghheap[0] = hheap0;
// Now, create the remaining heaps for the table.
for (iHeap = 1, phHeaps = &pheaptbl->rghheap[1]; iHeap < cHeaps; iHeap++, phHeaps++)
{
if (!(*phHeaps = ExHeapCreate(dwFlags, dwInitialSize, dwMaxSize)))
{
DebugTrace("Failed to create additional heaps for MHeap APIs!\n");
ExchMHeapDestroy();
goto ret;
}
}
ret:
LeaveCriticalSection(&csMHeap);
return (HANDLE)pheaptbl;
#else
// Called twice? The first person in gets to set the number
// of heaps in the table. Subsequent calls result in an AddRef
// to the current table and this table is returned to the caller.
if (InterlockedIncrement(&cRefHeap) != 0)
{
Assert(hMpHeap);
return hMpHeap;
}
else
{
//
// NB: MpHeap doesn't support max size of heap.
//
return hMpHeap = MpHeapCreate(dwFlags, dwInitialSize, cHeaps);
}
#endif
}
BOOL
WINAPI
ExchMHeapDestroy(void)
{
#ifndef USEMPHEAP
HANDLE hHeap;
ULONG iHeap;
EnterCriticalSection(&csMHeap);
// If we are called too many times, we'll complain in the
// Debug build, but otherwise, just return successfully!
if (!pheaptbl)
{
DebugTrace("ExchMHeapDestroy called on invalid heap table!\n");
goto ret;
}
// When our RefCount goes to zero, we tear-down the MHeap Table.
if (--pheaptbl->cRef == 0)
{
for (iHeap = pheaptbl->cHeaps-1; iHeap > 0; iHeap-- )
{
if (hHeap = pheaptbl->rghheap[iHeap])
ExHeapDestroy(hHeap);
}
hHeap = pheaptbl->rghheap[0];
ExHeapFree(hHeap, 0, pheaptbl);
ExHeapDestroy(hHeap);
pheaptbl = NULL;
}
ret:
LeaveCriticalSection(&csMHeap);
return TRUE;
#else
BOOL fRet = 1;
if (hMpHeap)
{
//
// On last terminate blow away the heap.
//
if (InterlockedDecrement(&cRefHeap) < 0)
{
fRet = MpHeapDestroy(hMpHeap);
hMpHeap = NULL;
}
}
return fRet;
#endif
}
//DWORD GetRetAddr(void)
//{
// DWORD * pdwStack;
//
// __asm mov pdwStack, ebp
//
// pdwStack = (DWORD *)*pdwStack;
// pdwStack = (DWORD *)*pdwStack;
//
// return *(pdwStack + 1);
//}
LPVOID
WINAPI
ExchMHeapAlloc(
DWORD dwSize)
{
#ifdef USEMPHEAP
return MpHeapAlloc(hMpHeap, 0, dwSize);
#else
HANDLE hheap;
LPVOID pv;
hheap = pheaptbl->rghheap[GetCurrentThreadId() & (pheaptbl->cHeaps-1)];
//Raid X5:195963 We never want to allocate/reallocate
//less memory than what we have been requested.
if (dwSize + cbMHeapHeader < dwSize) {
DebugTrace("Trying to allocate a negative amount of memory!\n");
return NULL;
}
pv = ExHeapAlloc(hheap, 0, dwSize + cbMHeapHeader);
if (!pv)
{
DebugTrace("OOM: ExchMHeapAlloc failed to allocate a new block!\n");
return NULL;
}
*(HANDLE *)pv = hheap;
return MHeapPvFromPv(pv);
#endif
}
LPVOID
WINAPI
ExchMHeapAllocDebug(
DWORD dwSize, char *szFile, DWORD dwLine)
{
#ifdef USEMPHEAP
return MpHeapAlloc(hMpHeap, 0, dwSize);
#else
HANDLE hheap;
LPVOID pv;
hheap = pheaptbl->rghheap[GetCurrentThreadId() & (pheaptbl->cHeaps-1)];
//Raid X5:195963 We never want to allocate/reallocate
//less memory than what we have been requested.
if (dwSize + cbMHeapHeader < dwSize) {
DebugTrace("Trying to allocate a negative amount of memory!\n");
return NULL;
}
pv = ExHeapAlloc(hheap, 0, dwSize + cbMHeapHeader);
if (!pv)
{
DebugTrace("OOM: ExchMHeapAlloc failed to allocate a new block!\n");
return NULL;
}
*(HANDLE *)pv = hheap;
if (fDbgEnable)
{
HeapSetName2(hheap, pv, "File: %s, Line: %d", szFile, dwLine);
}
return MHeapPvFromPv(pv);
#endif
}
LPVOID
WINAPI
ExchMHeapReAlloc(
LPVOID pvOld,
DWORD dwSize)
{
#ifdef USEMPHEAP
return MpHeapReAlloc(hMpHeap, pvOld, dwSize);
#else
LPVOID pv;
//Raid X5:195963 We never want to allocate/reallocate
//less memory than what we have been requested.
if (dwSize + cbMHeapHeader < dwSize) {
DebugTrace("Trying to allocate a negative amount of memory!\n");
return NULL;
}
pv = ExHeapReAlloc(
HandleFromMHeapPv(pvOld),
0,
PvFromMHeapPv(pvOld),
dwSize + cbMHeapHeader);
if (!pv)
{
DebugTrace("OOM: ExchMHeapReAlloc failed to reallocate a block!\n");
return NULL;
}
return MHeapPvFromPv(pv);
#endif
}
LPVOID
WINAPI
ExchMHeapReAllocDebug(
LPVOID pvOld,
DWORD dwSize, char *szFile, DWORD dwLine)
{
#ifdef USEMPHEAP
return MpHeapReAlloc(hMpHeap, pvOld, dwSize);
#else
LPVOID pv;
//Raid X5:195963 We never want to allocate/reallocate
//less memory than what we have been requested.
if (dwSize + cbMHeapHeader < dwSize) {
DebugTrace("Trying to allocate a negative amount of memory!\n");
return NULL;
}
pv = ExHeapReAlloc(
HandleFromMHeapPv(pvOld),
0,
PvFromMHeapPv(pvOld),
dwSize + cbMHeapHeader);
if (!pv)
{
DebugTrace("OOM: ExchMHeapReAlloc failed to reallocate a block!\n");
return NULL;
}
if (fDbgEnable)
{
HeapSetName2(HandleFromMHeapPv(MHeapPvFromPv(pv)), pv, "File: %s, Line: %d", szFile, dwLine);
}
return MHeapPvFromPv(pv);
#endif
}
BOOL
WINAPI
ExchMHeapFree(
LPVOID pvFree)
{
#ifdef USEMPHEAP
if (pvFree)
{
return MpHeapFree(hMpHeap, pvFree);
}
else
return FALSE;
#else
if (pvFree)
{
return ExHeapFree(
HandleFromMHeapPv(pvFree),
0,
PvFromMHeapPv(pvFree));
}
else
return FALSE;
#endif
}
SIZE_T
WINAPI
ExchMHeapSize(
LPVOID pvSize)
{
if (pvSize)
{
#ifdef USEMPHEAP
return MpHeapSize(hMpHeap, 0, pvSize);
#else
return ((ExHeapSize(
HandleFromMHeapPv(pvSize),
0,
PvFromMHeapPv(pvSize))) - cbMHeapHeader);
#endif
}
else
return 0;
}
//-----------------------------------------------------------------------------
// The Heap Handle-less APIs
//-----------------------------------------------------------------------------
LPVOID
WINAPI
ExchAlloc(
DWORD dwSize)
{
#ifdef DEBUG
if (!hProcessHeap)
{
hProcessHeap = DebugHeapCreate(0, 0, 0);
HeapSetHeapName(hProcessHeap, "Default ExchMem Heap");
}
#endif // DEBUG
return ExHeapAlloc(hProcessHeap, 0, dwSize);
}
LPVOID
WINAPI
ExchReAlloc(
LPVOID pvOld,
DWORD dwSize)
{
if (!pvOld)
return ExchAlloc(dwSize);
return ExHeapReAlloc(hProcessHeap, 0, pvOld, dwSize);
}
BOOL
WINAPI
ExchFree(
LPVOID pvFree)
{
return ExHeapFree(hProcessHeap, 0, pvFree);
}
SIZE_T
WINAPI
ExchSize(
LPVOID pv)
{
#ifdef DEBUG
if (!hProcessHeap)
{
hProcessHeap = DebugHeapCreate(0, 0, 0);
HeapSetHeapName(hProcessHeap, "Default ExchMem Heap");
}
#endif // DEBUG
return ExHeapSize(hProcessHeap, 0, pv);
}
//-----------------------------------------------------------------------------
// All debug code starts here!
//-----------------------------------------------------------------------------
#ifdef DEBUG
//-----------------------------------------------------------------------------
// Implementaion of C-Runtimes that use malloc memory
//-----------------------------------------------------------------------------
static char szDebugIni[] = "EXCHMEM.INI";
static char szSectionAppNames[] = "Apps To Track";
static char szSectionHeap[] = "Memory Management";
static char szKeyUseVirtual[] = "VirtualMemory";
static char szKeyVirtualAlign[] = "VirtualAlign";
static char szKeyAssertLeaks[] = "AssertLeaks";
static char szKeyDumpLeaks[] = "DumpLeaks";
static char szKeyDumpLeaksDebugger[]= "DumpLeaksToDebugger";
static char szKeyFillMem[] = "FillMemory";
static char szKeyAllocFillByte[] = "AllocFillByte";
static char szKeyFreeFillByte[] = "FreeFillByte";
static char szKeyTrackMem[] = "TrackMemory";
static char szKeyTrackMemInMem[] = "TrackMemoryInMemory";
static char szKeyStackFrames[] = "StackFrames";
static char szKeySymbolLookup[] = "SymbolLookup";
static char szKeyOverwriteDetect[] = "OverwriteDetect";
static char szKeyValidateMemory[] = "ValidateMemory";
static char szKeyTrackFreedMemory[] = "TrackFreedMemory";
static char szKeyFreedMemorySize[] = "FreedMemorySize";
static char szKeyAssertValid[] = "AssertValid";
static char szKeyTrapOnInvalid[] = "TrapOnInvalid";
static char szKeySymPath[] = "SymPath";
static char szKeyLogPath[] = "LogPath";
static char szSectionAF[] = "Heap Resource Failures";
static char szKeyAFEnabled[] = "FailuresEnabled";
static char szKeyAFStart[] = "AllocsToFirstFailure";
static char szKeyAFInterval[] = "FailureInterval";
static char szKeyAFBufSize[] = "FailureSize";
static char szKeyHeapMon[] = "MonitorHeap";
static char szHeapMonDLL[] = "GLHMON32.DLL";
static char szHeapMonEntry[] = "HeapMonitor";
static char szGetSymNameEntry[] = "GetSymbolName";
static char szAllocationFault[] = "FaultingAllocationNumber";
/*
- InitDebugExchMem
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
BOOL InitDebugExchMem(HMODULE hModule)
{
ULONG cch;
char * pch;
char rgchModulePath[MAX_PATH];
// Get the executable name and search look in exchmem.ini
// to see if we are interested in memory tracing for this
// process. The ini section looks like this:
//
// [Apps To Track]
// store=1
// emsmta=0
// dsamain=0
//
// etc. This sample specifies that only the store is to
// be enabled for memory tracking.
GetModuleFileName(NULL, rgchModulePath, MAX_PATH);
RemoveExtension(rgchModulePath);
pch = rgchModulePath + lstrlen(rgchModulePath) - 1;
while (*pch != '\\' && pch >= rgchModulePath)
pch--;
lstrcpy(rgchExeName, ++pch);
fDbgEnable = !!(BOOL)GetPrivateProfileIntA(szSectionAppNames,
rgchExeName, 0, szDebugIni);
// Store module handle in global var
hMod = hModule;
if (!hinstRunTime)
{
hinstRunTime = LoadLibrary("msvcrt.dll");
if (!hinstRunTime)
{
DebugTrace("EXCHMEM: Failed to load the run-time dll!\n");
return FALSE;
}
pfMalloc = (LPFMALLOC)GetProcAddress(hinstRunTime, "malloc");
if (!pfMalloc)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of malloc in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
pfRealloc = (LPFREALLOC)GetProcAddress(hinstRunTime, "realloc");
if (!pfRealloc)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of realloc in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
pfFree = (LPFFREE)GetProcAddress(hinstRunTime, "free");
if (!pfFree)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of free in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
pfCalloc = (LPFCALLOC)GetProcAddress(hinstRunTime, "calloc");
if (!pfCalloc)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of calloc in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
pfStrDup = (LPFSTRDUP)GetProcAddress(hinstRunTime, "_strdup");
if (!pfStrDup)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of _strdup in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
pfMemSize = (LPFMEMSIZE)GetProcAddress(hinstRunTime, "_msize");
if (!pfMemSize)
{
DebugTrace("EXCHMEM: Failed to GetProcAddress of _msize in run-time dll!\n");
FreeLibrary(hinstRunTime);
return FALSE;
}
}
// Lookup symbols or just log addresses?
fSymbolLookup = GetPrivateProfileIntA(szSectionHeap, szKeySymbolLookup, 0, szDebugIni);
if (!fDbgEnable)
{
if (fSymbolLookup && !fSymInitialize)
{
char rgchSymPath[MAX_PATH];
rgsymcacheHashTable = VirtualAlloc(
NULL,
NBUCKETS*sizeof(SYMCACHE),
MEM_COMMIT,
PAGE_READWRITE);
if (rgsymcacheHashTable == NULL)
{
return FALSE;
}
GetPrivateProfileString(szSectionHeap,
szKeySymPath,
"c:\\exchsrvr\\bin;.",
rgchSymPath,
MAX_PATH-1,
szDebugIni);
{
DWORD dwOptions;
dwOptions = SymGetOptions();
SymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS);
}
SymInitialize(GetCurrentProcess(), rgchSymPath, TRUE);
fSymInitialize = TRUE;
}
goto ret;
}
// This CS protects access to a list of all live heaps
InitializeCriticalSection(&csHeapList);
// Initialize support for memory monitoring and leak detection
fDumpLeaks = GetPrivateProfileIntA(szSectionHeap, szKeyDumpLeaks, 0, szDebugIni);
fDumpLeaksDebugger = GetPrivateProfileIntA(szSectionHeap, szKeyDumpLeaksDebugger, 0, szDebugIni);
fAssertLeaks = GetPrivateProfileIntA(szSectionHeap, szKeyAssertLeaks, 0, szDebugIni);
fUseVirtual = GetPrivateProfileIntA(szSectionHeap, szKeyUseVirtual, 0, szDebugIni);
if (fUseVirtual)
cbVirtualAlign = GetPrivateProfileIntA(szSectionHeap, szKeyVirtualAlign, 1, szDebugIni);
fFillMemory = GetPrivateProfileIntA(szSectionHeap, szKeyFillMem, 0, szDebugIni);
if (fFillMemory)
{
char szFillByte[8];
// Set the memory fill characters.
if (GetPrivateProfileString(
szSectionHeap,
szKeyAllocFillByte,
"", szFillByte,
sizeof(szFillByte)-1,
szDebugIni))
chAllocFillByte = HexByteToBin(szFillByte);
if (GetPrivateProfileString(
szSectionHeap,
szKeyFreeFillByte,
"", szFillByte,
sizeof(szFillByte)-1,
szDebugIni))
chFreeFillByte = HexByteToBin(szFillByte);
}
//$ISSUE
// For now, just use virtual to detect overwrites!
// Maybe I'll change this later to use pads on the
// front and back side of a block. -RLS
fOverwriteDetect = GetPrivateProfileIntA(szSectionHeap, szKeyOverwriteDetect, 0, szDebugIni);
fValidateMemory = GetPrivateProfileIntA(szSectionHeap, szKeyValidateMemory, 0, szDebugIni);
fTrackFreedMemory = GetPrivateProfileIntA(szSectionHeap, szKeyTrackFreedMemory, 0, szDebugIni);
cEntriesFree = GetPrivateProfileIntA(szSectionHeap, szKeyFreedMemorySize, 512, szDebugIni);
fAssertValid = GetPrivateProfileIntA(szSectionHeap, szKeyAssertValid, 0, szDebugIni);
fTrapOnInvalid = GetPrivateProfileIntA(szSectionHeap, szKeyTrapOnInvalid, 0, szDebugIni);
fHeapMonitorUI = GetPrivateProfileIntA(szSectionHeap, szKeyHeapMon, 0, szDebugIni);
fFailuresEnabled = GetPrivateProfileIntA(szSectionAF, szKeyAFEnabled, 0, szDebugIni);
// Get file path to write log files into
GetPrivateProfileString(szSectionHeap,
szKeyLogPath,
".\\",
rgchLogPath,
MAX_PATH-1,
szDebugIni);
cch = lstrlen(rgchLogPath);
if (rgchLogPath[cch-1] != '\\')
{
rgchLogPath[cch] = '\\';
rgchLogPath[cch+1] = '\0';
}
// Initialize support for memory usage tracking
fTrackMem = GetPrivateProfileIntA(szSectionHeap, szKeyTrackMem, 0, szDebugIni);
if (fTrackMem)
StartTrace(TRUE);
// This is for keeping track of the last x mem functions in a circular list in memory.
// This doesn't slow things down as much as tracing everything to disk and can be useful
// in finding memory problems that are timing related.
dwTrackMemInMem = GetPrivateProfileIntA(szSectionHeap, szKeyTrackMemInMem, 0, szDebugIni);
if (dwTrackMemInMem)
{
fTrackMem = TRUE;
rgmemtrace = VirtualAlloc(
NULL,
dwTrackMemInMem*sizeof(MEMTRACE),
MEM_COMMIT,
PAGE_READWRITE);
}
// How many Stack Frames does the user want traced?
cFrames = GetPrivateProfileIntA(szSectionHeap, szKeyStackFrames, 0, szDebugIni);
if (cFrames > NSTK)
cFrames = NSTK;
// This is used in the debug build to determine if we will
// allow the HeapMonitor UI or not. We do not allow it if
// the process that is attaching to us is a service.
fProcessIsService = IsProcessRunningAsService();
// Initialize the symbols stuff for imagehlp.dll
fCallStacks = (fDumpLeaks || fAssertLeaks || fTrackMem || fHeapMonitorUI || fValidateMemory);
if (cFrames && fCallStacks && !fSymInitialize)
{
char rgchSymPath[MAX_PATH];
rgsymcacheHashTable = VirtualAlloc(
NULL,
NBUCKETS*sizeof(SYMCACHE),
MEM_COMMIT,
PAGE_READWRITE);
if (rgsymcacheHashTable == NULL)
{
return FALSE;
}
GetPrivateProfileString(szSectionHeap,
szKeySymPath,
"c:\\exchsrvr\\bin;.",
rgchSymPath,
MAX_PATH-1,
szDebugIni);
{
DWORD dwOptions;
dwOptions = SymGetOptions();
SymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS);
}
SymInitialize(GetCurrentProcess(), rgchSymPath, TRUE);
fSymInitialize = TRUE;
}
ret:
return TRUE;
}
/*
- UnInitDebugExchMem
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID UnInitDebugExchMem(VOID)
{
PHEAP pheap = pheapList;
while (pheap)
{
if (fDumpLeaks && (pheap->ulFlags & HEAP_NO_FREE))
HeapDumpLeaks(pheap, TRUE);
pheap = pheap->pNext;
}
if (hProcessHeap)
DebugHeapDestroy(hProcessHeap);
if (hinstRunTime)
{
FreeLibrary(hinstRunTime);
hinstRunTime = NULL;
pfMalloc = NULL;
pfRealloc = NULL;
pfFree = NULL;
pfCalloc = NULL;
pfStrDup = NULL;
pfMemSize = NULL;
}
if (fDbgEnable)
{
if (fSymInitialize)
{
VirtualFree(rgsymcacheHashTable, NBUCKETS*sizeof(SYMCACHE), MEM_DECOMMIT);
VirtualFree(rgsymcacheHashTable, 0, MEM_RELEASE);
SymCleanup(GetCurrentProcess());
fSymInitialize = FALSE;
}
DeleteCriticalSection(&csHeapList);
StopTrace();
if (dwTrackMemInMem)
{
VirtualFree(rgmemtrace, dwTrackMemInMem*sizeof(MEMTRACE), MEM_DECOMMIT);
VirtualFree(rgmemtrace, 0, MEM_RELEASE);
}
}
}
/*
- calloc
-
* Purpose:
* Replace the calloc() function supplied in the c-runtimes. Like
* malloc() except zero fills the memory that is allocated.
*
* Parameters:
* cStructs Number of objects the caller wants room for
* cbStructs Size of an individual object
*
* Returns:
* pv Pointer to zero filled memory of size: cStructs*cbStructs
*
*/
void *
__cdecl
calloc(
size_t cStructs,
size_t cbStructs)
{
void * pv;
pv = pfCalloc(cStructs, cbStructs);
if (fDbgEnable && FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)0x00001000;
rgdwArgs[1] = (DWORD_PTR)(cStructs*cbStructs);
rgdwArgs[2] = (DWORD_PTR)pv;
LogCurrentAPI(API_HEAP_ALLOC, rgdwCallers, cFrames, rgdwArgs, 3);
}
return pv;
}
/*
- free
-
* Purpose:
* To free memory allocated with malloc(0, realloc(), or calloc().
*
* Parameters:
* pv Pointer to memory buffer to free
*
* Returns:
* void
*
*/
void
__cdecl
free(
void *pv)
{
if (fDbgEnable && FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)0x00001000;
rgdwArgs[1] = (DWORD_PTR)pv;
rgdwArgs[2] = (pv ? (DWORD_PTR)pfMemSize(pv) : 0);
LogCurrentAPI(API_HEAP_FREE, rgdwCallers, cFrames, rgdwArgs, 3);
}
pfFree(pv);
}
/*
- malloc
-
* Purpose:
* To allocate a memory buffer of size cb.
*
* Parameters:
* cb Size of memory buffer to allocate
*
* Returns:
* pv Pointer to memory buffer
*
*/
void *
__cdecl
malloc(
size_t cb)
{
void * pv;
pv = pfMalloc(cb);
if (fDbgEnable && FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)0x00001000;
rgdwArgs[1] = (DWORD_PTR)cb;
rgdwArgs[2] = (DWORD_PTR)pv;
LogCurrentAPI(API_HEAP_ALLOC, rgdwCallers, cFrames, rgdwArgs, 3);
}
return pv;
}
/*
- realloc
-
* Purpose:
* To resize a memory buffer allocated with malloc().
*
* Parameters:
* pv Pointer to original memory buffer
* cb New size of memory buffer to be allocated
*
* Returns:
* pvNew Pointer to new memory buffer
*
*/
void *
__cdecl
realloc(
void *pv,
size_t cb)
{
void * pvNew;
DWORD dwSize;
BOOL fTrackMem = FTrackMem();
if (fDbgEnable && fTrackMem)
dwSize = (pv ? pfMemSize(pv) : 0);
pvNew = pfRealloc(pv, cb);
if (fDbgEnable && fTrackMem)
{
DWORD_PTR rgdwArgs[5];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)0x00001000;
rgdwArgs[1] = dwSize;
rgdwArgs[2] = (DWORD_PTR)pv;
rgdwArgs[3] = (DWORD_PTR)cb;
rgdwArgs[4] = (DWORD_PTR)pvNew;
LogCurrentAPI(API_HEAP_REALLOC, rgdwCallers, cFrames, rgdwArgs, 5);
}
return pvNew;
}
/*
- _strdup
-
* Purpose:
* To allocate a memory buffer large enough to hold sz, copy
* the contents of sz into the new buffer and return the new
* buffer to the caller (i.e. make a copy of the string).
*
* Parameters:
* sz Pointer to null terminated string to copy
*
* Returns:
* szNew Pointer to new copy of sz
*
*/
char *
__cdecl
_strdup(
const char *sz)
{
return pfStrDup(sz);
}
//-----------------------------------------------------------------------------
// ExchMem Heap Debug Implementation
//-----------------------------------------------------------------------------
VOID
EnqueueHeap(PHEAP pheap)
{
EnterCriticalSection(&csHeapList);
if (pheapList)
pheap->pNext = pheapList;
pheapList = pheap;
LeaveCriticalSection(&csHeapList);
}
VOID
DequeueHeap(PHEAP pheap)
{
PHEAP pheapPrev = NULL;
PHEAP pheapCurr;
EnterCriticalSection(&csHeapList);
pheapCurr = pheapList;
while (pheapCurr)
{
if (pheapCurr == pheap)
break;
pheapPrev = pheapCurr;
pheapCurr = pheapCurr->pNext;
}
if (pheapCurr)
{
if (pheapPrev)
pheapPrev->pNext = pheapCurr->pNext;
else
pheapList = pheapCurr->pNext;
}
LeaveCriticalSection(&csHeapList);
}
HANDLE
WINAPI
DebugHeapCreate(
DWORD dwFlags,
DWORD dwInitialSize,
DWORD dwMaxSize)
{
HANDLE hDataHeap = 0;
HANDLE hBlksHeap = 0;
PHEAP pheap = NULL;
if (!fDbgEnable)
return HeapCreate(dwFlags, dwInitialSize, dwMaxSize);
// 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.
hBlksHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
if (!hBlksHeap)
{
DebugTrace("HEAP_Open: Failed to create new heap!\n");
goto ret;
}
// Allocate the thing we hand back to the caller on this new heap.
pheap = HeapAlloc(hBlksHeap, 0, sizeof(HEAP));
if (!pheap)
{
DebugTrace("HEAP_Alloc: Failed to allocate heap handle!\n");
HeapDestroy(hBlksHeap);
hBlksHeap = NULL;
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(pheap, 0, sizeof(HEAP));
pheap->pfnSetName = (LPHEAPSETNAME)HeapSetNameFn;
pheap->hBlksHeap = hBlksHeap;
pheap->ulFlags = HEAP_LOCAL;
if (dwFlags & HEAP_NO_FREE)
{
pheap->ulFlags |= HEAP_NO_FREE;
dwFlags &= ~(HEAP_NO_FREE);
}
InitializeCriticalSection(&pheap->cs);
// VirtualMemory default is FALSE
if (fUseVirtual)
{
pheap->ulFlags |= HEAP_USE_VIRTUAL;
// We always want virtual allocations on RISC 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 RISC platforms we'll
// get alignment faults everywhere.
#if defined(_X86_)
if (cbVirtualAlign == 4)
#else
cbVirtualAlign = 4;
#endif
pheap->ulFlags |= HEAP_USE_VIRTUAL_4;
}
// DumpLeaks default is TRUE
if (fDumpLeaks)
pheap->ulFlags |= HEAP_DUMP_LEAKS;
// AssertLeaks default is FALSE
if (fAssertLeaks)
pheap->ulFlags |= HEAP_ASSERT_LEAKS;
// FillMem default is TRUE
if (fFillMemory)
{
pheap->ulFlags |= HEAP_FILL_MEM;
pheap->chFill = chAllocFillByte;
}
// Set up artificial failures. If anything is set in our ini file, then
// HEAP_FAILURES_ENABLED gets set.
if (fFailuresEnabled)
{
pheap->ulFlags |= HEAP_FAILURES_ENABLED;
pheap->ulFailStart = (ULONG)GetPrivateProfileInt(szSectionAF,
szKeyAFStart, 0, szDebugIni);
pheap->ulFailInterval = (ULONG)GetPrivateProfileInt(szSectionAF,
szKeyAFInterval, 0, szDebugIni);
pheap->ulFailBufSize = (ULONG)GetPrivateProfileInt(szSectionAF,
szKeyAFBufSize, 0, szDebugIni);
pheap->iAllocationFault = GetPrivateProfileIntA(szSectionAF,
szAllocationFault, 0, szDebugIni);
}
// 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 (fHeapMonitorUI && !fProcessIsService)
if (FRegisterHeap(pheap))
pheap->ulFlags |= HEAP_HEAP_MONITOR;
// If we are not using virtual memory allocators, then we
// create another heap to allocate the users data in.
if (!fUseVirtual)
{
hDataHeap = HeapCreate(dwFlags, dwInitialSize, dwMaxSize);
if (!hDataHeap)
{
DebugTrace("HeapAlloc: Failed to allocate heap handle!\n");
HeapDestroy(hBlksHeap);
pheap = NULL;
goto ret;
}
pheap->hDataHeap = hDataHeap;
}
// Name heap
HeapSetHeapName1(pheap, "ExchMem Heap: %08lX", pheap);
// Remove heap from list
EnqueueHeap(pheap);
if (FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = dwInitialSize;
rgdwArgs[1] = dwMaxSize;
rgdwArgs[2] = (DWORD_PTR)hDataHeap;
LogCurrentAPI(API_HEAP_CREATE, rgdwCallers, cFrames, rgdwArgs, 3);
}
ret:
return (HANDLE)pheap;
}
BOOL
WINAPI
DebugHeapDestroy(
HANDLE hHeap)
{
PHEAP pheap = (PHEAP)hHeap;
HANDLE hDataHeap = pheap->hDataHeap;
HANDLE hBlksHeap = pheap->hBlksHeap;
if (!fDbgEnable)
return HeapDestroy(hHeap);
// Remove heap from list
DequeueHeap(pheap);
// Dump memory leaks if we're supposed to.
if (fDumpLeaks && !(pheap->ulFlags & HEAP_NO_FREE))
HeapDumpLeaks(pheap, FALSE);
//
// Free the entries in the free list.
//
// This isn't totally necessary, since destroying the heap destroys the free list, but what the
// heck, it's cleaner to do it this way.
//
while (pheap->phblkFree)
{
PHBLK phblk = pheap->phblkFree;
pheap->phblkFree = phblk->phblkFreeNext;
//
// And now free up the block for real, it's too old.
//
if (fUseVirtual)
VMFreeEx((fOverwriteDetect ? PvHeadFromPv(phblk->pv) : phblk->pv), cbVirtualAlign);
else
HeapFree(pheap->hDataHeap, 0,
(fOverwriteDetect ? PvHeadFromPv(phblk->pv) : phblk->pv));
HeapFree(pheap->hBlksHeap, 0, phblk);
}
// Destroy the HeapMonitor thread and un-load the DLL
UnRegisterHeap(pheap);
if (fHeapMonitorUI && pheap->hInstHeapMon)
FreeLibrary(pheap->hInstHeapMon);
DeleteCriticalSection(&pheap->cs);
// Clean-up and leave. Closing frees leaks, so we're cool!
if (!fUseVirtual && hDataHeap)
{
HeapDestroy(hDataHeap);
}
if (hBlksHeap)
{
HeapFree(hBlksHeap, 0, pheap);
HeapDestroy(hBlksHeap);
}
if (FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)hDataHeap;
LogCurrentAPI(API_HEAP_DESTROY, rgdwCallers, cFrames, rgdwArgs, 1);
}
return TRUE;
}
LPVOID
WINAPI
DebugHeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
DWORD dwSize)
{
PHEAP pheap = (PHEAP)hHeap;
PHBLK phblk = NULL;
LPVOID pvAlloc = NULL;
if (!fDbgEnable)
return HeapAlloc(hHeap, dwFlags, dwSize);
// Note: To be consistent with other (e.g. system) allocators,
// we have to return a valid allocation if dwSize == 0. So, we
// allow a dwSize of 0 to actually be allocated. (See bug 3556 in
// the sqlguest:exchange database.)
EnterCriticalSection(&pheap->cs);
if (fFailuresEnabled)
{
if (pheap->ulAllocNum == pheap->iAllocationFault)
{
DebugTrace("HeapRealloc: Allocation Fault hit\n");
DebugBreak();
}
if (FForceFailure(pheap, dwSize))
{
DebugTrace("HeapAlloc: Artificial Failure\n");
pvAlloc = NULL;
pheap->ulAllocNum++;
LeaveCriticalSection(&pheap->cs);
goto ret;
}
}
// We have to leave the CS before calling HeapAlloc in case the user
// created this heap with the HEAP_GENERATE_EXCEPTIONS flag set, which,
// if thrown, would cause us to exit here with our CS held - a bad thing...
LeaveCriticalSection(&pheap->cs);
if (fUseVirtual)
pvAlloc = VMAllocEx((fOverwriteDetect ? (dwSize + 2*cbOWSection) : dwSize), cbVirtualAlign);
else
pvAlloc = HeapAlloc(pheap->hDataHeap, dwFlags,
(fOverwriteDetect ? (dwSize + 2*cbOWSection) : dwSize));
// Now, re-aquire the CS and finish our work. We do not create the
// BlksHeap with the HEAP_GENERATE_EXCEPTIONS flag so we're cool.
EnterCriticalSection(&pheap->cs);
if (pvAlloc)
{
phblk = (PHBLK)HeapAlloc(pheap->hBlksHeap, 0, sizeof(HBLK));
if (phblk)
{
if (fOverwriteDetect)
{
// Fill the Head and Tail overwrite detection
// blocks special fill character: 0xAB.
memset(pvAlloc,
chOWFill,
cbOWSection);
memset(PvTailFromPvHead(pvAlloc, dwSize),
chOWFill,
cbOWSection);
// Now, advance pvAlloc to user portion of buffer
pvAlloc = PvFromPvHead(pvAlloc);
}
phblk->pheap = pheap;
phblk->szName[0] = '\0';
phblk->ulSize = dwSize;
phblk->ulAllocNum = ++pheap->ulAllocNum;
phblk->pv = pvAlloc;
phblk->phblkPrev = NULL;
phblk->phblkNext = NULL;
phblk->phblkFreeNext= NULL;
ZeroMemory(phblk->rgdwCallers, cFrames*sizeof(DWORD));
ZeroMemory(phblk->rgdwFree, cFrames*sizeof(DWORD));
PhblkEnqueue(phblk);
if (fCallStacks)
GetCallStack(phblk->rgdwCallers, cFrames);
if (fFillMemory && !(dwFlags & HEAP_ZERO_MEMORY))
memset(pvAlloc, pheap->chFill, (size_t)dwSize);
if (FTrackMem())
{
DWORD_PTR rgdwArgs[4];
rgdwArgs[0] = (DWORD_PTR)pheap->hDataHeap;
rgdwArgs[1] = dwSize;
rgdwArgs[2] = (DWORD_PTR)pvAlloc;
LogCurrentAPI(API_HEAP_ALLOC, phblk->rgdwCallers, cFrames, rgdwArgs, 3);
}
}
else
{
if (fUseVirtual)
VMFreeEx(pvAlloc, cbVirtualAlign);
else
HeapFree(pheap->hDataHeap, dwFlags, pvAlloc);
pvAlloc = NULL;
}
}
LeaveCriticalSection(&pheap->cs);
ret:
return pvAlloc;
}
LPVOID
WINAPI
DebugHeapReAlloc(
HANDLE hHeap,
DWORD dwFlags,
LPVOID pvOld,
DWORD dwSize)
{
PHEAP pheap = (PHEAP)hHeap;
LPVOID pvNew = NULL;
PHBLK phblk;
UINT cbOld;
if (!fDbgEnable)
return HeapReAlloc(hHeap, dwFlags, pvOld, dwSize);
if (pvOld == 0)
{
pvNew = DebugHeapAlloc(hHeap, dwFlags, dwSize);
}
else
{
EnterCriticalSection(&pheap->cs);
if (fValidateMemory)
{
if (!HeapValidatePv(pheap, pvOld, "DebugHeapReAlloc"))
{
LeaveCriticalSection(&pheap->cs);
goto ret;
}
}
phblk = PvToPhblk(pheap, pvOld);
cbOld = (UINT)CbPhblkClient(phblk);
PhblkDequeue(phblk);
// We have to leave the CS before calling HeapReAlloc in case the user
// created this heap with the HEAP_GENERATE_EXCEPTIONS flag set, which,
// if thrown, would cause us to exit here with our CS held - a bad thing...
LeaveCriticalSection(&pheap->cs);
if (fFailuresEnabled && pheap->ulAllocNum >= pheap->iAllocationFault)
{
DebugTrace("HeapRealloc: Allocation Fault hit\n");
DebugBreak();
}
else if (fFailuresEnabled && FForceFailure(pheap, dwSize) && (dwSize > cbOld))
{
InterlockedIncrement((LPLONG)&pheap->ulAllocNum);
pvNew = 0;
DebugTrace("HeapRealloc: Artificial Failure\n");
}
else if (fUseVirtual)
pvNew = VMReallocEx(fOverwriteDetect ? PvHeadFromPv(pvOld) : pvOld,
(fOverwriteDetect ? (dwSize + 2*cbOWSection) : dwSize),
cbVirtualAlign);
else
pvNew = HeapReAlloc(pheap->hDataHeap, dwFlags,
(fOverwriteDetect ? PvHeadFromPv(pvOld) : pvOld),
(fOverwriteDetect ? (dwSize + 2*cbOWSection) : dwSize));
// Now, re-aquire the CS and finish our work.
EnterCriticalSection(&pheap->cs);
if (pvNew)
{
if (fOverwriteDetect)
{
// Fill the Head and Tail overwrite detection
// blocks special fill character: 0xAB.
memset(pvNew,
chOWFill,
cbOWSection);
memset(PvTailFromPvHead(pvNew, dwSize),
chOWFill,
cbOWSection);
// Now, advance pvNew to user portion of buffer
pvNew = PvFromPvHead(pvNew);
}
if (fCallStacks)
GetCallStack(phblk->rgdwCallers, cFrames);
if (fFillMemory && (dwSize > cbOld) && !(dwFlags & HEAP_ZERO_MEMORY))
memset((LPBYTE)pvNew + cbOld, pheap->chFill, dwSize - cbOld);
phblk->pv = pvNew;
phblk->ulSize = dwSize;
phblk->ulAllocNum = ++pheap->ulAllocNum;
phblk->phblkPrev = NULL;
phblk->phblkNext = NULL;
phblk->phblkFreeNext= NULL;
}
else
{
phblk->phblkPrev = NULL;
phblk->phblkNext = NULL;
phblk->phblkFreeNext= NULL;
}
PhblkEnqueue(phblk);
if (FTrackMem())
{
DWORD_PTR rgdwArgs[5];
rgdwArgs[0] = (DWORD_PTR)pheap->hDataHeap;
rgdwArgs[1] = (DWORD_PTR)cbOld;
rgdwArgs[2] = (DWORD_PTR)pvOld;
rgdwArgs[3] = dwSize;
rgdwArgs[4] = (DWORD_PTR)pvNew;
LogCurrentAPI(API_HEAP_REALLOC, phblk->rgdwCallers, cFrames, rgdwArgs, 5);
}
LeaveCriticalSection(&pheap->cs);
}
ret:
return pvNew;
}
PHBLK
PhblkSearchFreeList(PHEAP pheap, LPVOID pv)
{
PHBLK phblkT = pheap->phblkFree;
//
// Walk the free list looking for this block, and if we find it, free it.
//
while (phblkT != NULL)
{
if (phblkT->pv == pv)
{
return phblkT;
}
phblkT = phblkT->phblkFreeNext;
}
return NULL;
}
BOOL
WINAPI
DebugHeapFree(
HANDLE hHeap,
DWORD dwFlags,
LPVOID pvFree)
{
PHEAP pheap = (PHEAP)hHeap;
BOOL fRet = TRUE;
DWORD dwSize = 0;
if (!fDbgEnable)
return HeapFree(hHeap, dwFlags, pvFree);
EnterCriticalSection(&pheap->cs);
//
// If we're tracking freed memory, then we don't actually free the blocks, we remember where they
// are on the freed block list.
//
if (pvFree)
{
PHBLK phblk;
phblk = PvToPhblk(pheap, pvFree);
dwSize = (size_t)CbPhblkClient(phblk);
if (!fValidateMemory || HeapValidatePv(pheap, pvFree, "DebugHeapFree"))
{
//
// remove this phblk from the list of allocated blocks - as far as the heap is concerned, it's
// no longer allocated.
//
PhblkDequeue(phblk);
//
// And fill the block with the free block pattern if appropriate.
//
if (fFillMemory)
{
memset(pvFree, chFreeFillByte, dwSize);
}
if (fTrackFreedMemory)
{
PHBLK phblkT;
if (fCallStacks)
GetCallStack(phblk->rgdwFree, cFrames);
//
// Now insert this free block onto the head of the free block list
//
phblkT = pheap->phblkFree;
pheap->phblkFree = phblk;
phblk->phblkFreeNext = phblkT;
//
// And then check to see if we have "too many" free entries.
//
if (++pheap->cEntriesFree > cEntriesFree)
{
PHBLK *phblkPrev = &pheap->phblkFree;
//
// There are too many entries on the free list, so we need to remove the last one.
//
phblkT = pheap->phblkFree;
while (phblkT->phblkFreeNext != NULL)
{
phblkPrev = &phblkT->phblkFreeNext;
phblkT = phblkT->phblkFreeNext;
}
Assert(*phblkPrev);
*phblkPrev = NULL;
//
// And now free up the block for real, it's too old.
//
if (fUseVirtual)
VMFreeEx((fOverwriteDetect ? PvHeadFromPv(phblkT->pv) : phblkT->pv), cbVirtualAlign);
else
fRet = HeapFree(pheap->hDataHeap, dwFlags,
(fOverwriteDetect ? PvHeadFromPv(phblkT->pv) : phblkT->pv));
HeapFree(pheap->hBlksHeap, 0, phblkT);
}
}
else // We're not tracking freed memory, so we can really free the memory right now.
{
//
// And now free up the block for real.
//
if (fUseVirtual)
VMFreeEx((fOverwriteDetect ? PvHeadFromPv(pvFree) : pvFree), cbVirtualAlign);
else
fRet = HeapFree(pheap->hDataHeap, dwFlags,
(fOverwriteDetect ? PvHeadFromPv(pvFree) : pvFree));
HeapFree(pheap->hBlksHeap, 0, phblk);
}
}
}
if (FTrackMem())
{
DWORD_PTR rgdwArgs[4];
DWORD_PTR rgdwCallers[NSTK];
GetCallStack(rgdwCallers, cFrames);
rgdwArgs[0] = (DWORD_PTR)pheap->hDataHeap;
rgdwArgs[1] = (DWORD_PTR)pvFree;
rgdwArgs[2] = dwSize;
LogCurrentAPI(API_HEAP_FREE, rgdwCallers, cFrames, rgdwArgs, 3);
}
LeaveCriticalSection(&pheap->cs);
return fRet;
}
BOOL
WINAPI
DebugHeapLock(
HANDLE hHeap)
{
PHEAP pheap = (PHEAP)hHeap;
if (!fDbgEnable)
return HeapLock(hHeap);
EnterCriticalSection(&pheap->cs);
return HeapLock(pheap->hDataHeap);
}
BOOL
WINAPI
DebugHeapUnlock(
HANDLE hHeap)
{
BOOL fRet;
PHEAP pheap = (PHEAP)hHeap;
if (!fDbgEnable)
return HeapUnlock(hHeap);
fRet = HeapUnlock(pheap->hDataHeap);
LeaveCriticalSection(&pheap->cs);
return fRet;
}
BOOL
WINAPI
DebugHeapWalk(
HANDLE hHeap,
LPPROCESS_HEAP_ENTRY lpEntry)
{
BOOL fRet;
PHEAP pheap = (PHEAP)hHeap;
if (!fDbgEnable)
return HeapWalk(hHeap, lpEntry);
EnterCriticalSection(&pheap->cs);
fRet = HeapWalk(pheap->hDataHeap, lpEntry);
LeaveCriticalSection(&pheap->cs);
return fRet;
}
BOOL
WINAPI
DebugHeapValidate(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem)
{
BOOL fRet = TRUE;
PHEAP pheap = (PHEAP)hHeap;
if (!fDbgEnable)
return HeapValidate(hHeap, dwFlags, lpMem);
EnterCriticalSection(&pheap->cs);
if (!fUseVirtual)
fRet = HeapValidate(pheap->hDataHeap, dwFlags,
(lpMem != NULL && fOverwriteDetect ? PvHeadFromPv(lpMem) : lpMem));
LeaveCriticalSection(&pheap->cs);
return fRet;
}
SIZE_T
WINAPI
DebugHeapSize(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem)
{
PHEAP pheap = (PHEAP)hHeap;
SIZE_T cb = 0;
if (!fDbgEnable)
return HeapSize(hHeap, dwFlags, lpMem);
EnterCriticalSection(&pheap->cs);
if ((fValidateMemory ? HeapValidatePv(pheap, (LPVOID)lpMem, "DebugHeapSize") : 1))
{
if (fUseVirtual)
{
cb = (UINT)VMGetSizeEx((LPVOID)lpMem, cbVirtualAlign);
}
else
{
cb = HeapSize(pheap->hDataHeap, dwFlags,
(fOverwriteDetect ? PvHeadFromPv(lpMem) : lpMem));
}
if (fOverwriteDetect)
{
cb -= 2*cbOWSection;
}
}
LeaveCriticalSection(&pheap->cs);
return cb;
}
SIZE_T
WINAPI
DebugHeapCompact(
HANDLE hHeap,
DWORD dwFlags)
{
PHEAP pheap = (PHEAP)hHeap;
SIZE_T cbLargestFreeBlk = 0;
if (!fDbgEnable)
return HeapCompact(hHeap, dwFlags);
EnterCriticalSection(&pheap->cs);
if (!fUseVirtual)
cbLargestFreeBlk = HeapCompact(pheap->hDataHeap, dwFlags);
LeaveCriticalSection(&pheap->cs);
return cbLargestFreeBlk;
}
//-----------------------------------------------------------------------------
// Debug Support routines
//-----------------------------------------------------------------------------
/*
- FRegisterHeap
-
* Purpose:
* If the user wants to monitor the Heap, then load the DLL with
* the HeapMonitor UI.
*/
BOOL FRegisterHeap(PHEAP pheap)
{
HINSTANCE hInst;
LPHEAPMONPROC pfnHeapMon;
LPGETSYMNAMEPROC pfnGetSymName;
pheap->hInstHeapMon = 0;
pheap->pfnGetSymName = NULL;
hInst = LoadLibrary(szHeapMonDLL);
if (!hInst)
{
DebugTrace("FRegisterHeap: Failed to LoadLibrary GLHMON32.DLL.\n");
goto ret;
}
pfnHeapMon = (LPHEAPMONPROC)GetProcAddress(hInst, szHeapMonEntry);
if (!pfnHeapMon)
{
DebugTrace("FRegisterHeap: Failed to GetProcAddress of HeapMonitor.\n");
FreeLibrary(hInst);
goto ret;
}
pfnGetSymName = (LPGETSYMNAMEPROC)GetProcAddress(hInst, szGetSymNameEntry);
if (!pfnGetSymName)
{
DebugTrace("FRegisterHeap: Failed to GetProcAddress of GetSymName.\n");
}
pheap->hInstHeapMon = hInst;
if (!pfnHeapMon(pheap, HEAPMON_LOAD))
{
DebugTrace("FRegisterHeap: Call to HeapMonitor failed.\n");
pheap->hInstHeapMon = 0;
goto ret;
}
pheap->pfnHeapMon = pfnHeapMon;
pheap->pfnGetSymName = pfnGetSymName;
ret:
return (pheap->hInstHeapMon ? TRUE : FALSE);
}
VOID UnRegisterHeap(PHEAP pheap)
{
if (pheap->pfnHeapMon)
pheap->pfnHeapMon(pheap, HEAPMON_UNLOAD);
}
/*
- HeapDumpLeaksHeader
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID HeapDumpLeaksHeader(FILE * hf, PHEAP pheap, BOOL fNoFree)
{
char szDate[16];
char szTime[16];
GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, NULL, "MMM dd yy", szDate, 16);
GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, NULL, "hh':'mm':'ss tt", szTime, 16);
fprintf(hf, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
fprintf(hf, "DATE: %s\n", szDate);
fprintf(hf, "TIME: %s\n\n", szTime);
fprintf(hf, "HEAP NAME: %s\n", pheap->szHeapName);
fprintf(hf, "MAX ALLOC: %ld\n", pheap->ulAllocNum);
fprintf(hf, "LEAKED NO_FREE HEAP: %s\n", (fNoFree? "YES" : "NO"));
fprintf(hf, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
fprintf(hf, "AllocNum, BlkName, Size, Address, Frame1, Frame2, Frame3, Frame4, Frame5, Frame6, Frame7, Frame8, Frame9, Frame10, Frame11, Frame12\n");
}
/*
- HeapDumpLeaksFooter
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID HeapDumpLeaksFooter(FILE * hf, DWORD cLeaks, DWORD cbLeaked)
{
fprintf(hf, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
fprintf(hf, "TOTAL NUM OF LEAKS: %ld\n", cLeaks);
fprintf(hf, "TOTAL BYTES LEAKED: %ld\n", cbLeaked);
fprintf(hf, "END\n");
fprintf(hf, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n\n");
}
/*
- HeapDumpLeakedBlock
-
* Purpose:
* To report individual memory leaks through DebugTrace and the
* HeapLeakHook breakpoint function.
*/
VOID HeapDumpLeakedBlock(FILE * hf, PHEAP pheap, PHBLK phblk)
{
char rgchSymbols[4096];
HANDLE hProcess = GetCurrentProcess();
fprintf(hf, "%ld, %s, %ld, %p",
phblk->ulAllocNum,
*phblk->szName ? phblk->szName : "NONAME",
CbPhblkClient(phblk),
PhblkToPv(phblk));
*rgchSymbols = '\0';
GetStackSymbols(hProcess, rgchSymbols, phblk->rgdwCallers, cFrames);
if (hf)
fprintf(hf, "%s\n", rgchSymbols);
if (fDumpLeaksDebugger)
{
char *szSymbol = rgchSymbols;
char *szSymbolNext = rgchSymbols;
int iSymbol = 0;
Trace("Block#%d, %s, %ld, %08lX:\n", phblk->ulAllocNum, *phblk->szName ? phblk->szName : "NONAME",
CbPhblkClient(phblk), PhblkToPv(phblk));
while ((szSymbolNext = strchr(szSymbol, ',')) != NULL)
{
*szSymbolNext++ = '\0';
if (*szSymbol != '\0' && strcmp(szSymbol, "0") != 0)
{
Trace("\t[%d]: %s\n", iSymbol, szSymbol);
}
szSymbol += strlen(szSymbol)+1;
iSymbol += 1;
}
//
// Dump the last entry in the call stack.
//
if (*szSymbol != '\0' && strcmp(szSymbol, "0") != 0)
{
Trace("\t[%d]: %s\n", iSymbol, szSymbol);
}
}
}
/*
- HeapDumpLeaks
-
* Purpose:
* Gets called at HeapClose time to report any memory leaks against
* this heap. There are 2 reporting fascilities used by this routine:
*
* => Asserts (via TrapSz)
* => Trace files
* => 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 HeapDumpLeaks(PHEAP pheap, BOOL fNoFree)
{
PHBLK phblk;
BOOL fDump = !!(pheap->ulFlags & HEAP_DUMP_LEAKS);
BOOL fAssert = !!(pheap->ulFlags & HEAP_ASSERT_LEAKS);
char szLeakLog[MAX_PATH];
DWORD cLeaks = 0;
DWORD cbLeaked = 0;
FILE * hLeakLog = NULL;
GetLogFilePath(rgchLogPath, ".mem", szLeakLog);
hLeakLog = fopen(szLeakLog, "a");
if (!hLeakLog)
goto ret;
if (pheap->phblkHead != NULL)
{
if (fAssert)
{
AssertSz(FALSE, "Memory Leak Detected, dumping leaks");
}
if (!fSymInitialize)
{
rgsymcacheHashTable = VirtualAlloc(
NULL,
NBUCKETS*sizeof(SYMCACHE),
MEM_COMMIT,
PAGE_READWRITE);
if (rgsymcacheHashTable == NULL)
{
return;
}
SymInitialize(GetCurrentProcess(), NULL, TRUE);
fSymInitialize = TRUE;
}
HeapDumpLeaksHeader(hLeakLog, pheap, fNoFree);
if (fDump)
{
for (phblk = pheap->phblkHead; phblk; phblk = phblk->phblkNext)
{
HeapDumpLeakedBlock(hLeakLog, pheap, phblk);
cLeaks++;
cbLeaked += phblk->ulSize;
}
}
}
HeapDumpLeaksFooter(hLeakLog, cLeaks, cbLeaked);
ret:
if (hLeakLog)
fclose(hLeakLog);
}
/*
- HeapValidatePhblk
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*
*/
BOOL HeapValidatePhblk(PHEAP pheap, PHBLK phblk, char ** pszReason)
{
if (IsBadWritePtr(phblk, sizeof(HBLK)))
{
*pszReason = "Block header cannot be written to";
goto err;
}
if (phblk->pheap != pheap)
{
*pszReason = "Block header does not have correct pointer back to heap";
goto err;
}
if (phblk->phblkNext)
{
if (IsBadWritePtr(phblk->phblkNext, sizeof(HBLK)))
{
*pszReason = "Block header has invalid next link pointer";
goto err;
}
if (phblk->phblkNext->phblkPrev != phblk)
{
*pszReason = "Block header points to a next block which doesn't "
"point back to it";
goto err;
}
}
if (phblk->phblkPrev)
{
if (IsBadWritePtr(phblk->phblkPrev, sizeof(HBLK))) {
*pszReason = "Block header has invalid prev link pointer";
goto err;
}
if (phblk->phblkPrev->phblkNext != phblk)
{
*pszReason = "Block header points to a prev block which doesn't "
"point back to it";
goto err;
}
}
else if (pheap->phblkHead != phblk)
{
*pszReason = "Block header has a zero prev link but the heap doesn't "
"believe it is the first block";
goto err;
}
if (phblk->ulAllocNum > pheap->ulAllocNum)
{
*pszReason = "Block header has an invalid internal allocation number";
goto err;
}
return TRUE;
err:
return FALSE;
}
/*
- HeapDidAlloc
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*
*/
BOOL HeapDidAlloc(PHEAP pheap, LPVOID pv)
{
PHBLK phblk;
char * pszReason;
BOOL fDidAlloc = FALSE;
for (phblk = pheap->phblkHead; phblk; phblk = phblk->phblkNext)
{
AssertSz(HeapValidatePhblk(pheap, phblk, &pszReason),
"Invalid block header in ExchMem");
if (!HeapValidatePhblk(pheap, phblk, &pszReason))
DebugTrace2("Block header (phblk=%08lX) is invalid\n%s", phblk, pszReason);
if (PhblkToPv(phblk) == pv)
{
fDidAlloc = TRUE;
break;
}
}
return fDidAlloc;
}
/*
- DumpFailedValidate
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID DumpFailedValidate(char * szFailed, DWORD_PTR * rgdwStack)
{
FILE * hLog = NULL;
char szValidateLog[MAX_PATH];
char rgchBuff[2048];
lstrcpy(rgchBuff, "Stack Trace: ");
GetStackSymbols(GetCurrentProcess(), rgchBuff, rgdwStack, cFrames);
// Create validate log file name
GetLogFilePath(rgchLogPath, ".val", szValidateLog);
// Open the Log File and write results
hLog = fopen(szValidateLog, "a");
if (hLog)
{
fprintf(hLog, "%s", szFailed);
fprintf(hLog, "%s\n\n", rgchBuff);
fclose(hLog);
}
}
/*
- HeapValidatePv
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*
*/
BOOL HeapValidatePv(PHEAP pheap, LPVOID pv, char * pszFunc)
{
PHBLK phblk;
char * pszReason;
char szBuff[1024];
DWORD_PTR rgdwStack[NSTK];
LPBYTE pb;
phblk = PvToPhblk(pheap, pv);
if (!phblk)
{
//
// Let's see if this block is on the free list.
//
if (fTrackFreedMemory && (phblk = PhblkSearchFreeList(pheap, pv)))
{
char rgchStackFree[2048];
char rgchStackAlloc[2048];
strcpy(szBuff, "Attempt to free already freed memory");
if (fAssertValid)
AssertSz(0, szBuff);
//
// Dump call stack that corresponds to the earlier free.
//
GetStackSymbols(GetCurrentProcess(), rgchStackFree, phblk->rgdwFree, cFrames);
GetStackSymbols(GetCurrentProcess(), rgchStackAlloc, phblk->rgdwCallers, cFrames);
Trace("Call stack of freeing routine: \n");
Trace("%s\n", rgchStackFree);
Trace("Call stack of allocating routine: \n");
Trace("%s\n", rgchStackAlloc);
if (fTrapOnInvalid)
DebugBreak();
}
else
{
wsprintf(szBuff, "%s detected a memory block (%08lX) which was either "
"not allocated in heap '%s' or has already been freed but is not on the free list.\n",
pszFunc, pv, pheap->szHeapName);
if (fAssertValid)
AssertSz(0, szBuff);
if (fTrapOnInvalid)
DebugBreak();
GetCallStack(rgdwStack, cFrames);
DumpFailedValidate(szBuff, rgdwStack);
DebugTrace(szBuff);
}
return FALSE;
}
if (fOverwriteDetect)
{
pb = (LPBYTE)PvHeadFromPv(pv);
if ((pb[0] != chOWFill) || (pb[1] != chOWFill) ||
(pb[2] != chOWFill) || (pb[3] != chOWFill))
{
wsprintf(szBuff, "%s detected a memory block (%08lX) from heap '%s' "
"which appears to have been under-written.\n",
pszFunc, pv, pheap->szHeapName);
if (fAssertValid)
AssertSz(0, szBuff);
if (fTrapOnInvalid)
DebugBreak();
GetCallStack(rgdwStack, cFrames);
DumpFailedValidate(szBuff, rgdwStack);
DebugTrace(szBuff);
return FALSE;
}
pb = (LPBYTE)PvTailFromPv(pv, phblk->ulSize);
if ((pb[0] != chOWFill) || (pb[1] != chOWFill) ||
(pb[2] != chOWFill) || (pb[3] != chOWFill))
{
wsprintf(szBuff, "%s detected a memory block (%08lX) from heap '%s' "
"which appears to have been over-written.\n",
pszFunc, pv, pheap->szHeapName);
if (fAssertValid)
AssertSz(0, szBuff);
if (fTrapOnInvalid)
DebugBreak();
GetCallStack(rgdwStack, cFrames);
DumpFailedValidate(szBuff, rgdwStack);
DebugTrace(szBuff);
return FALSE;
}
}
if (!HeapValidatePhblk(pheap, phblk, &pszReason))
{
wsprintf(szBuff, "%s detected an invalid memory block (%08lX) in heap '%s'. %s.\n",
pszFunc, pv, pheap->szHeapName, pszReason);
if (fAssertValid)
AssertSz(0, szBuff);
if (fTrapOnInvalid)
DebugBreak();
GetCallStack(rgdwStack, cFrames);
DumpFailedValidate(szBuff, rgdwStack);
DebugTrace(szBuff);
return FALSE;
}
return TRUE;
}
/*
- PhblkEnqueue
-
* 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 PhblkEnqueue(PHBLK phblk)
{
phblk->phblkNext = phblk->pheap->phblkHead;
if (phblk->phblkNext)
phblk->phblkNext->phblkPrev = phblk;
phblk->pheap->phblkHead = phblk;
// I am going to disable the InsertSorted behavior for now for performance
// reasons. It is only done this way because of GLHMON which I don't believe
// to be widely used at this point anyway. I'm not even sure if this is
// important to GLHMON since it has the ability to sort blocks by other fields.
/* PHBLK phblkCurr = NULL;
PHBLK phblkNext = phblk->pheap->phblkHead;
while (phblkNext)
{
if (phblkNext > phblk)
break;
phblkCurr = phblkNext;
phblkNext = phblkCurr->phblkNext;
}
if (phblkNext)
{
phblk->phblkNext = phblkNext;
phblk->phblkPrev = phblkCurr;
phblkNext->phblkPrev = phblk;
}
else
{
phblk->phblkNext = NULL;
phblk->phblkPrev = phblkCurr;
}
if (phblkCurr)
phblkCurr->phblkNext = phblk;
else
phblk->pheap->phblkHead = phblk;
*/
}
/*
- PhblkDequeue
-
* Purpose:
* To remove a freed block from the list of allocations hanging
* off the heap.
*/
VOID PhblkDequeue(PHBLK phblk)
{
//
// We should never be dequeuing an already freed block.
//
Assert(phblk->phblkFreeNext == NULL);
if (phblk->phblkNext)
phblk->phblkNext->phblkPrev = phblk->phblkPrev;
if (phblk->phblkPrev)
phblk->phblkPrev->phblkNext = phblk->phblkNext;
else
phblk->pheap->phblkHead = phblk->phblkNext;
}
/*
- 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]);
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
void __cdecl HeapSetHeapNameFn(PHEAP pheap, char *pszFormat, ...)
{
char sz[512];
va_list vl;
if (fDbgEnable)
{
va_start(vl, pszFormat);
wvsprintf(sz, pszFormat, vl);
va_end(vl);
lstrcpyn(pheap->szHeapName, sz, sizeof(pheap->szHeapName));
}
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID __cdecl HeapSetNameFn(PHEAP pheap, LPVOID pv, char *pszFormat, ...)
{
char sz[512];
PHBLK phblk;
va_list vl;
phblk = PvToPhblk(pheap, pv);
if (phblk)
{
va_start(vl, pszFormat);
wvsprintf(sz, pszFormat, vl);
va_end(vl);
lstrcpyn(phblk->szName, sz, sizeof(phblk->szName));
}
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
char * HeapGetName(PHEAP pheap, LPVOID pv)
{
PHBLK phblk;
phblk = PvToPhblk(pheap, pv);
if (phblk)
return(phblk->szName);
return("");
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
BOOL FForceFailure(PHEAP pheap, ULONG cb)
{
// First, see if we're past our start of failures point
if (pheap->ulFailStart && (pheap->ulFailStart <= pheap->ulAllocNum))
{
// If so, then are we at an interval where we should return errors?
if ((pheap->ulFailInterval)
&& ((pheap->ulAllocNum - pheap->ulFailStart)%pheap->ulFailInterval) == 0)
{
// return that we should fail here
return TRUE;
}
// Check to see if the alloc size is greater than allowed
if (pheap->ulFailBufSize && cb >= pheap->ulFailBufSize)
return TRUE;
}
// Otherwise, no error is returned for this alloc
return FALSE;
}
/*
- PvToPhblk
-
* Purpose:
* Finds the HBLK for this allocation in the heap's active list.
*/
PHBLK PvToPhblk(PHEAP pheap, LPVOID pv)
{
PHBLK phblk;
EnterCriticalSection(&pheap->cs);
phblk = pheap->phblkHead;
while (phblk)
{
if (phblk->pv == pv)
break;
phblk = phblk->phblkNext;
}
LeaveCriticalSection(&pheap->cs);
return phblk;
}
/*
- IsRunningAsService
-
* Purpose:
* Determine if the process that attached to us is running as a
* service or not.
*
* Parameters:
* VOID
*
* Returns:
* fService TRUE if a service, FALSE if not
*
*/
BOOL IsProcessRunningAsService(VOID)
{
HANDLE hProcessToken = NULL;
DWORD dwGroupLength = 50;
PTOKEN_GROUPS ptokenGroupInfo = NULL;
PSID psidInteractive = NULL;
PSID psidService = NULL;
SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
BOOL fService = FALSE;
DWORD i;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
goto ret;
ptokenGroupInfo = (PTOKEN_GROUPS)LocalAlloc(0, dwGroupLength);
if (ptokenGroupInfo == NULL)
goto ret;
if (!GetTokenInformation(hProcessToken, TokenGroups, ptokenGroupInfo,
dwGroupLength, &dwGroupLength))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto ret;
LocalFree(ptokenGroupInfo);
ptokenGroupInfo = NULL;
ptokenGroupInfo = (PTOKEN_GROUPS)LocalAlloc(0, dwGroupLength);
if (ptokenGroupInfo == NULL)
goto ret;
if (!GetTokenInformation(hProcessToken, TokenGroups, ptokenGroupInfo,
dwGroupLength, &dwGroupLength))
{
goto ret;
}
}
// We now know the groups associated with this token. We want to look
// to see if the interactive group is active in the token, and if so,
// we know that this is an interactive process.
//
// We also look for the "service" SID, and if it's present, we know
// we're a service.
//
// The service SID will be present iff the service is running in a
// user account (and was invoked by the service controller).
if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_INTERACTIVE_RID, 0, 0,
0, 0, 0, 0, 0, &psidInteractive))
{
goto ret;
}
if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_SERVICE_RID, 0, 0, 0,
0, 0, 0, 0, &psidService))
{
goto ret;
}
for (i = 0; i < ptokenGroupInfo->GroupCount ; i += 1)
{
PSID psid = ptokenGroupInfo->Groups[i].Sid;
// Check to see if the group we're looking at is one of
// the 2 groups we're interested in.
if (EqualSid(psid, psidInteractive))
{
// This process has the Interactive SID in its token.
// This means that the process is running as an EXE.
goto ret;
}
else if (EqualSid(psid, psidService))
{
// This process has the Service SID in its token. This means that
// the process is running as a service running in a user account.
fService = TRUE;
goto ret;
}
}
// Neither Interactive or Service was present in the current
// users token. This implies that the process is running as
// a service, most likely running as LocalSystem.
fService = TRUE;
ret:
if (psidInteractive)
FreeSid(psidInteractive);
if (psidService)
FreeSid(psidService);
if (ptokenGroupInfo)
LocalFree(ptokenGroupInfo);
if (hProcessToken)
CloseHandle(hProcessToken);
return fService;
}
/*
- DebugTraceFn
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
void __cdecl DebugTraceFn(char *pszFormat, ...)
{
char sz[4096];
va_list vl;
va_start(vl, pszFormat);
wvsprintfA(sz, pszFormat, vl);
va_end(vl);
OutputDebugStringA(sz);
OutputDebugStringA("\r");
}
/*
- AssertFn
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
void AssertFn(char * szFile, int nLine, char * szMsg)
{
int nRet;
char szInfo[1024];
wsprintf(szInfo, "File %.64s @ line %d%s%s",
szFile,
nLine,
(szMsg) ? (": ") : (""),
(szMsg) ? (szMsg) : (""));
// OK to continue, CANCEL to break.
nRet = MessageBox(NULL, szInfo, "ExchMem Assert", MB_OKCANCEL | MB_ICONSTOP | MB_SERVICE_NOTIFICATION | MB_TOPMOST);
if (nRet == IDCANCEL)
DebugBreak();
}
void
ExchmemGetCallStack(DWORD_PTR *rgdwCaller, DWORD cFind)
{
if (fSymbolLookup)
{
GetCallStack(rgdwCaller, cFind);
}
else
{
ZeroMemory(rgdwCaller, cFind*sizeof(DWORD));
}
}
BOOL FTrackMem()
{
if (InterlockedCompareExchange(&fChangeTrackState,FALSE,TRUE))
{
fTrackMem = !fTrackMem;
if (fTrackMem)
StartTrace(FALSE);
else
StopTrace();
}
return fTrackMem;
}
void
StartTrace(BOOL fFresh)
{
char szTrackLog[MAX_PATH];
GetLogFilePath(rgchLogPath, ".trk", szTrackLog);
InitializeCriticalSection(&csTrackLog);
// Open the Log File
hTrackLog = fopen(szTrackLog, fFresh ? "wt" : "at");
if (!hTrackLog)
{
DeleteCriticalSection(&csTrackLog);
fTrackMem = FALSE;
}
}
void
StopTrace()
{
DeleteCriticalSection(&csTrackLog);
if (hTrackLog)
{
fclose(hTrackLog);
hTrackLog = NULL;
}
fTrackMem = FALSE;
}
//-------------------------------------------------------------------------------------
// Description:
// Copies szAppend to szBuf and updates szBuf to point to the terminating NULL of
// of the copied bytes. cbMaxBuf is max chars available in szBuf. cbAppend is
// strlen(szAppend). If cbAppend > cbMaxBuf, as many characters as possible are
// copied to szBuf (including terminating NULL).
//-------------------------------------------------------------------------------------
#define ExchmemSafeAppend(szBuf, cbMaxBuf, szAppend, cbAppend) { \
int iWritten; \
\
if(NULL != lstrcpyn(szBuf, szAppend, cbMaxBuf)) { \
iWritten = ((int)cbMaxBuf < (int)cbAppend) ? cbMaxBuf : cbAppend; \
szBuf += iWritten; \
cbMaxBuf -= iWritten; \
} \
}
void
ExchmemFormatSymbol(HANDLE hProcess, DWORD_PTR dwAddress, char rgchSymbol[], DWORD cbSymbol)
{
CHAR rgchModuleName[16];
BOOL fSym;
IMAGEHLP_MODULE mi = {0};
PIMAGEHLP_SYMBOL psym = NULL;
LPSTR pszSymName = NULL;
CHAR rgchLine[256];
LPSTR pszT = NULL;
DWORD_PTR dwOffset = 0;
int cbAppend = 0;
PCHAR pchSymbol = rgchSymbol;
mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
pszSymName = pfMalloc(256);
if (!pszSymName)
goto ret;
psym = pfMalloc(sizeof(IMAGEHLP_SYMBOL) + 256);
if (!psym)
goto ret;
ZeroMemory(psym, sizeof(IMAGEHLP_SYMBOL) + 256);
psym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
psym->MaxNameLength = 256;
rgchSymbol[0] = '\0';
if (SymGetModuleInfo(hProcess, dwAddress, &mi))
{
lstrcpy(rgchModuleName, mi.ModuleName);
RemoveExtension(rgchModuleName);
cbAppend = wsprintf(rgchLine, "(%s)", rgchModuleName);
ExchmemSafeAppend(pchSymbol, cbSymbol, rgchLine, cbAppend);
}
else
ExchmemSafeAppend(pchSymbol, cbSymbol, "none", sizeof("none") - 1);
if (fSymbolLookup)
{
//
// Make sure we always get the address of the symbol, since the symbol lookup isn't accurate for
// all modules.
//
cbAppend = wsprintf(rgchLine, "(0x%p):", dwAddress);
ExchmemSafeAppend(pchSymbol, cbSymbol, rgchLine, cbAppend);
pszT = PszGetSymbolFromCache(dwAddress, &dwOffset);
if (!pszT)
{
fSym = SymGetSymFromAddr(hProcess,
dwAddress,
&dwOffset,
psym);
if (fSym)
{
if (!SymUnDName(psym, pszSymName, 248))
lstrcpyn(pszSymName, &(psym->Name[1]), 248);
AddSymbolToCache(dwAddress, dwOffset, pszSymName);
pszT = pszSymName;
}
}
if (pszT)
{
ExchmemSafeAppend(pchSymbol, cbSymbol, pszT, lstrlen(pszT));
cbAppend = wsprintf(rgchLine, "+0x%x", dwOffset);
ExchmemSafeAppend(pchSymbol, cbSymbol, rgchLine, cbAppend);
}
else
{
cbAppend = wsprintf(rgchLine, "(0x%08x)", dwAddress);
ExchmemSafeAppend(pchSymbol, cbSymbol, rgchLine, cbAppend);
}
}
else
{
cbAppend = wsprintf(rgchLine, "(0x%08x)", dwAddress);
ExchmemSafeAppend(pchSymbol, cbSymbol, rgchLine, cbAppend);
}
ret:
if (psym)
pfFree(psym);
if (pszSymName)
pfFree(pszSymName);
}
/*
- GetCallStack
-
* Purpose:
* Uses the imagehlp APIs to get the call stack.
*
* Parameters:
* pdwCaller An array of return addresses
* cFind Count of stack frames to get
*
* Returns:
* VOID
*
*/
VOID GetCallStack(DWORD_PTR *rgdwCaller, DWORD cFind)
{
BOOL fMore;
STACKFRAME stkfrm = {0};
CONTEXT ctxt;
HANDLE hThread;
HANDLE hProcess;
if (!cFind)
return;
hThread = GetCurrentThread();
hProcess = GetCurrentProcess();
ZeroMemory(&ctxt, sizeof(CONTEXT));
ZeroMemory(rgdwCaller, cFind * sizeof(DWORD));
ctxt.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(hThread, &ctxt))
{
stkfrm.AddrPC.Offset = 0;
}
else
{
#if defined(_M_IX86)
_asm
{
mov stkfrm.AddrStack.Offset, esp
mov stkfrm.AddrFrame.Offset, ebp
mov stkfrm.AddrPC.Offset, offset DummyLabel
DummyLabel:
}
#elif defined(_M_MRX000)
stkfrm.AddrPC.Offset = ctxt.Fir;
stkfrm.AddrStack.Offset = ctxt.IntSp;
stkfrm.AddrFrame.Offset = ctxt.IntSp;
#elif defined(_M_ALPHA)
stkfrm.AddrPC.Offset = (ULONG_PTR)ctxt.Fir;
stkfrm.AddrStack.Offset = (ULONG_PTR)ctxt.IntSp;
stkfrm.AddrFrame.Offset = (ULONG_PTR)ctxt.IntSp;
#elif defined(_M_PPC)
stkfrm.AddrPC.Offset = ctxt.Iar;
stkfrm.AddrStack.Offset = ctxt.Gpr1;
stkfrm.AddrFrame.Offset = ctxt.Gpr1;
#else
stkfrm.AddrPC.Offset = 0;
#endif
}
stkfrm.AddrPC.Mode = AddrModeFlat;
stkfrm.AddrStack.Mode = AddrModeFlat;
stkfrm.AddrFrame.Mode = AddrModeFlat;
// Eat the first one
fMore = StackWalk(
#ifdef _M_IX86
IMAGE_FILE_MACHINE_I386,
#elif defined(_M_MRX000)
IMAGE_FILE_MACHINE_R4000,
#elif defined(_M_ALPHA)
#if !defined(_M_AXP64)
IMAGE_FILE_MACHINE_ALPHA,
#else
IMAGE_FILE_MACHINE_ALPHA64,
#endif
#elif defined(_M_PPC)
IMAGE_FILE_MACHINE_POWERPC,
#else
IMAGE_FILE_MACHINE_UNKNOWN,
#endif
hProcess,
hThread,
&stkfrm,
&ctxt,
(PREAD_PROCESS_MEMORY_ROUTINE)ReadProcessMemory,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
while (fMore && (cFind > 0))
{
fMore = StackWalk(
#ifdef _M_IX86
IMAGE_FILE_MACHINE_I386,
#elif defined(_M_MRX000)
IMAGE_FILE_MACHINE_R4000,
#elif defined(_M_ALPHA)
IMAGE_FILE_MACHINE_ALPHA,
#elif defined(_M_AXP64)
IMAGE_FILE_MACHINE_ALPHA64,
#elif defined(_M_PPC)
IMAGE_FILE_MACHINE_POWERPC,
#else
IMAGE_FILE_MACHINE_UNKNOWN,
#endif
hProcess,
hThread,
&stkfrm,
&ctxt,
(PREAD_PROCESS_MEMORY_ROUTINE)ReadProcessMemory,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
if (!fMore)
break;
*rgdwCaller++ = stkfrm.AddrPC.Offset;
cFind -= 1;
}
}
/*
- RemoveExtension
-
* Purpose:
* Strips the file extension from a file path.
*
* Parameters:
* psz File path to strip extension from
*
* Returns:
* void
*/
VOID RemoveExtension(LPSTR psz)
{
LPSTR szLast = NULL;
while (*psz)
{
if (*psz == '.')
{
szLast = psz;
}
psz++;
}
if (szLast)
{
*szLast = '\0';
}
}
/*
- GetLogFilePath
-
* Purpose:
* Build a log file path from a supplied path, the current
* executables name, and a supplied file extension.
*
* Parameters:
* szPath [in] Path to new log file
* szExt [in] New log file extension
* szFilePath [out] Newly constructed log file path
*
* Returns:
* void
*/
VOID GetLogFilePath(LPSTR szPath, LPSTR szExt, LPSTR szFilePath)
{
lstrcpy(szFilePath, szPath);
lstrcat(szFilePath, rgchExeName);
lstrcat(szFilePath, szExt);
}
/*
- PszGetSymbolFromCache
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
CHAR * PszGetSymbolFromCache(DWORD_PTR dwAddress, DWORD_PTR * pdwOffset)
{
ULONG ulBucket = UlHash(dwAddress);
if (rgsymcacheHashTable[ulBucket].dwAddress == dwAddress)
{
*pdwOffset = rgsymcacheHashTable[ulBucket].dwOffset;
return rgsymcacheHashTable[ulBucket].rgchSymbol;
}
return NULL;
}
/*
- AddSymbolToCache
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID AddSymbolToCache(DWORD_PTR dwAddress, DWORD_PTR dwOffset, CHAR * pszSymbol)
{
ULONG ulBucket = UlHash(dwAddress);
rgsymcacheHashTable[ulBucket].dwAddress = dwAddress;
rgsymcacheHashTable[ulBucket].dwOffset = dwOffset;
lstrcpy(rgsymcacheHashTable[ulBucket].rgchSymbol, pszSymbol);
}
/*
- GetStackSymbols
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*
*/
VOID
GetStackSymbols(
HANDLE hProcess,
CHAR * rgchBuff,
DWORD_PTR * rgdwStack,
DWORD cFrames)
{
DWORD i;
DWORD_PTR dwAddress;
LPSTR pszSymName = NULL;
pszSymName = pfMalloc(256);
if (!pszSymName)
goto ret;
for (i = 0; i < cFrames; i++)
{
if ((dwAddress = rgdwStack[i]) != 0)
{
ExchmemFormatSymbol(hProcess, dwAddress, pszSymName, 256);
lstrcat(rgchBuff, ",");
lstrcat(rgchBuff, pszSymName);
}
else
lstrcat(rgchBuff, ",0");
}
ret:
if (pszSymName)
pfFree(pszSymName);
return;
}
/*
- LogCurrentAPI
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*
*/
VOID LogCurrentAPI(
WORD wApi,
DWORD_PTR *rgdwCallStack,
DWORD cFrames,
DWORD_PTR *rgdwArgs,
DWORD cArgs)
{
CHAR rgchT[64];
CHAR rgchKeys[8] = "CDARF";
CHAR rgchBuff[8192];
DWORD cbWritten;
if (dwTrackMemInMem)
{
long lCurr;
// Instead of writing out to a file, just maintain the data in a circular memory list.
// The overflow check is not thread safe, but if we lose an entry or two after two billion
// memory functions, oh well.
if (dwmemtrace == 0xefffffff)
{
dwmemtrace = 1;
lCurr = 0;
}
else
lCurr = (InterlockedIncrement((LONG *)&dwmemtrace) - 1) % dwTrackMemInMem;
memset(&rgmemtrace[lCurr],0,sizeof(MEMTRACE));
rgmemtrace[lCurr].wApi = wApi;
memcpy(rgmemtrace[lCurr].rgdwCallStack,rgdwCallStack,cFrames*sizeof(DWORD_PTR));
memcpy(rgmemtrace[lCurr].rgdwArgs,rgdwArgs,cArgs*sizeof(DWORD_PTR));
rgmemtrace[lCurr].dwTickCount = GetTickCount();
rgmemtrace[lCurr].dwThreadId = GetCurrentThreadId();
return;
}
sprintf(rgchBuff, "%c,%lu,%lu", rgchKeys[wApi], GetTickCount(), GetCurrentThreadId());
if (cFrames)
GetStackSymbols(GetCurrentProcess(), rgchBuff, rgdwCallStack, cFrames);
switch (wApi)
{
case API_HEAP_CREATE:
sprintf(rgchT, ",%ld", rgdwArgs[0]); // cbInitSize
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",%ld", rgdwArgs[1]); // cbMaxSize
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",0x%08X\n", rgdwArgs[2]); // hHeap
lstrcat(rgchBuff, rgchT);
break;
case API_HEAP_DESTROY:
sprintf(rgchT, ",0x%08X\n", rgdwArgs[0]); // hHeap
lstrcat(rgchBuff, rgchT);
break;
case API_HEAP_FREE:
sprintf(rgchT, ",0x%08X", rgdwArgs[0]); // hHeap
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",0x%08X", rgdwArgs[1]); // pvFree
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",%ld\n", rgdwArgs[2]); // cbFree
lstrcat(rgchBuff, rgchT);
break;
case API_HEAP_ALLOC:
sprintf(rgchT, ",0x%08X", rgdwArgs[0]); // hHeap
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",%ld", rgdwArgs[1]); // cbAlloc
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",0x%08X\n", rgdwArgs[2]); // pvAlloc
lstrcat(rgchBuff, rgchT);
break;
case API_HEAP_REALLOC:
sprintf(rgchT, ",0x%08X", rgdwArgs[0]); // hHeap
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",%ld", rgdwArgs[1]); // cbOld
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",0x%08X", rgdwArgs[2]); // pvOld
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",%ld", rgdwArgs[3]); // cbNew
lstrcat(rgchBuff, rgchT);
sprintf(rgchT, ",0x%08X\n", rgdwArgs[4]); // pvNew
lstrcat(rgchBuff, rgchT);
break;
}
EnterCriticalSection(&csTrackLog);
WriteFile((HANDLE)_get_osfhandle(_fileno(hTrackLog)), rgchBuff, strlen(rgchBuff), &cbWritten, NULL);
LeaveCriticalSection(&csTrackLog);
}
//-----------------------------------------------------------------------------
// Virtual Memory Support
//-----------------------------------------------------------------------------
#define PAGE_SIZE 4096
#define PvToVMBase(pv) ((LPVOID)((ULONG_PTR)pv & ~0xFFFF))
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
BOOL
VMValidatePvEx(
LPVOID pv,
ULONG cbCluster)
{
LPVOID pvBase;
LPBYTE pb;
pvBase = PvToVMBase(pv);
pb = (BYTE *)pvBase + sizeof(ULONG);
while (pb < (BYTE *)pv)
{
if (*pb++ != 0xAD)
{
char szBuff[1024];
wsprintf(szBuff, "VMValidatePvEx(pv=%08lX): Block leader has been overwritten", pv);
AssertSz(0, szBuff);
return FALSE;
}
}
if (cbCluster != 1)
{
ULONG cb = *((ULONG *)pvBase);
ULONG cbPad = 0;
if (cb % cbCluster)
cbPad = (cbCluster - (cb % cbCluster));
if (cbPad)
{
BYTE *pbMac;
pb = (BYTE *)pv + cb;
pbMac = pb + cbPad;
while (pb < pbMac)
{
if (*pb++ != 0xBC)
{
char szBuff[1024];
wsprintf(szBuff, "VMValidatePvEx(pv=%08lX): Block trailer has been overwritten", pv);
AssertSz(0, szBuff);
return FALSE;
}
}
}
}
return TRUE;
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
LPVOID
WINAPI
VMAllocEx(
ULONG cb,
ULONG cbCluster)
{
ULONG cbAlloc;
LPVOID pvR;
LPVOID pvC;
ULONG cbPad = 0;
// a cluster size of 0 means don't use the virtual allocator.
AssertSz(cbCluster != 0, "Cluster size is zero.");
if (cb > 0x400000)
return NULL;
if (cb % cbCluster)
cbPad = (cbCluster - (cb % cbCluster));
cbAlloc = sizeof(ULONG) + cb + cbPad + PAGE_SIZE - 1;
cbAlloc -= cbAlloc % PAGE_SIZE;
cbAlloc += PAGE_SIZE;
pvR = VirtualAlloc(0, cbAlloc, MEM_RESERVE, PAGE_NOACCESS);
if (pvR == 0)
return NULL;
pvC = VirtualAlloc(pvR, cbAlloc - PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (pvC != pvR)
{
VirtualFree(pvR, 0, MEM_RELEASE);
return NULL;
}
*(ULONG *)pvC = cb;
memset((BYTE *)pvC + sizeof(ULONG), 0xAD,
(UINT) cbAlloc - cb - cbPad - sizeof(ULONG) - PAGE_SIZE);
if (cbPad)
memset((BYTE *)pvC + cbAlloc - PAGE_SIZE - cbPad, 0xBC,
(UINT) cbPad);
return ((BYTE *)pvC + (cbAlloc - cb - cbPad - PAGE_SIZE));
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
VOID
WINAPI
VMFreeEx(
LPVOID pv,
ULONG cbCluster)
{
VMValidatePvEx(pv, cbCluster);
if (!VirtualFree(PvToVMBase(pv), 0, MEM_RELEASE))
{
char szBuff[1024];
wsprintf(szBuff, "VMFreeEx(pv=%08lX): VirtualFree failed (%08lX)",
pv, GetLastError());
AssertSz(0, szBuff);
}
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
LPVOID
WINAPI
VMReallocEx(
LPVOID pv,
ULONG cb,
ULONG cbCluster)
{
LPVOID* pvNew = 0;
ULONG cbCopy;
VMValidatePvEx(pv, cbCluster);
cbCopy = *(ULONG *)PvToVMBase(pv);
if (cbCopy > cb)
cbCopy = cb;
pvNew = VMAllocEx(cb, cbCluster);
if (pvNew)
{
memcpy(pvNew, pv, cbCopy);
VMFreeEx(pv, cbCluster);
}
return pvNew;
}
/*
- Function
-
* Purpose:
*
*
* Parameters:
*
*
* Returns:
*
*/
ULONG
WINAPI
VMGetSizeEx(
LPVOID pv,
ULONG cbCluster)
{
return (*(ULONG *)PvToVMBase(pv));
}
#ifdef DEBUG
BOOL
__stdcall
FReloadSymbolsCallback(PSTR szModuleName, ULONG_PTR ulBaseOfDLL, ULONG cbSizeOfDLL, void *pvContext)
{
if (SymGetModuleBase(GetCurrentProcess(), ulBaseOfDLL) == 0)
{
if (!SymLoadModule(GetCurrentProcess(),
NULL,
szModuleName,
NULL,
ulBaseOfDLL,
cbSizeOfDLL))
{
Trace("Error loading module %s: %d", szModuleName, GetLastError());
return FALSE;
}
}
return TRUE;
}
DWORD
WINAPI
ExchmemReloadSymbols(void)
{
if (!EnumerateLoadedModules(GetCurrentProcess(), FReloadSymbolsCallback, NULL))
{
DWORD ec = GetLastError();
Trace("SymEnumerateModules failed: %d", ec);
return ec;
}
return 0;
}
#endif
#else // !DEBUG
void
ExchmemGetCallStack(DWORD_PTR *rgdwCaller, DWORD cFind)
{
//
// Fill the stack with 0s on a retail EXCHMEM.
//
ZeroMemory(rgdwCaller, cFind*sizeof(DWORD));
}
void
ExchmemFormatSymbol(HANDLE hProcess, DWORD_PTR dwCaller, char rgchSymbol[], DWORD cbSymbol)
{
//
// Fill the stack with 0s on a retail EXCHMEM.
//
strncpy(rgchSymbol, "Unknown", cbSymbol);
}
DWORD
ExchmemReloadSymbols(void)
{
return 0;
}
#endif // DEBUG