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