/*++ Copyright (c) 1995 Microsoft Corporation Module Name: debugmem.cxx Abstract: Debug memory allocator Contents: InternetDebugMemInitialize InternetDebugMemTerminate InternetDebugAllocMem InternetDebugFreeMem InternetDebugReAllocMem InternetDebugSizeMem InternetDebugCheckMemFreed InternetDebugMemReport (InternetDebugCheckMemBlock) (DebugFillMem) (InternetAlloc) (InternetFree) (InternetReAlloc) (InternetSize) (InternetHeapAlloc) (InternetHeapReAlloc) (InternetHeapFree) (InternetHeapSize) (InternetDebugMemTest) (ReportMemoryUsage) (ReportMemoryBlocks) (DumpDeferredFreeList) (DumpMemoryList) (FindAndDumpDeferredBlock) (DumpBlock) (DumpDebugMemoryHeader) (DumpDebugMemoryFooter) (DumpUserData) (MapLastAccessOperation) (MapMemoryFlags) (DbgMemGetDebugSymbol) Author: Richard L Firth (rfirth) 02-Feb-1995 Environment: Win32 user mode Revision History: 02-Feb-1995 Created --*/ #include #include "rprintf.h" #if defined(USE_DEBUG_MEMORY) // // manifests // #define DEFAULT_INITIAL_HEAP_SIZE (64 K) #define DEFAULT_MAXIMUM_HEAP_SIZE (1 M) #define DEFAULT_HEADER_GUARD_SIZE 32 #define DEFAULT_FOOTER_GUARD_SIZE 32 #define DEFAULT_ALLOC_ALIGNMENT 4 #define HEADER_SIGNATURE 0x414d454d // "MEMA" #define FOOTER_SIGNATURE 0x434f4c4c // "LLOC" #define DWORD_ALLOC_FILL 0xc5c5c5c5 #define BYTE_ALLOC_FILL 0xc5 #define BYTE_ALLOC_FILL_EXTRA 0x88 #define GUARD_DWORD_FILL 0x44524147 // "GARD" #define DWORD_FREE_FILL 0xb7b7b7b7 #define BYTE_FREE_FILL 0xb7 #define DEFAULT_MAX_BLOCKS_DUMPED 1024 #define DEFAULT_MAX_DATA_DUMPED 65536 #define DEFAULT_BACKTRACE_DEPTH 2 // // only perform stack dump for x86 (or other stack-based processors) // #if defined(i386) #define DUMP_STACK 1 #else #define DUMP_STACK 0 #endif // // just using one stack these days // #define ONE_STACK 1 // // private types // typedef enum { MemAllocate = 0x6f6c6c41, // "Allo" MemReallocate = 0x6c416552, // "ReAl" MemLock = 0x6b636f4c, // "Lock" MemUnlock = 0x6f6c6e55, // "Unlo" MemFree = 0x65657246, // "Free" MemSize = 0x657a6953 // "Size" } MEMORY_ACTION; typedef enum { HEAP_COMPACT_NEVER = 0, HEAP_COMPACT_ON_ALLOC_FAIL, HEAP_COMPACT_ON_FREE } HEAP_COMPACT_TYPE; typedef enum { HEAP_VALIDATE_NEVER = 0, HEAP_VALIDATE_ON_ALLOC, HEAP_VALIDATE_ON_FREE } HEAP_VALIDATE_TYPE; // // DEBUG_MEMORY_HEADER - keeps debug memory on list // typedef struct { LIST_ENTRY List; DWORD ThreadId; LPSTR CreatedFile; DWORD CreatedLine; LPSTR AccessedFile; DWORD AccessedLine; SIZE_T RequestedLength; SIZE_T BlockLength; SIZE_T ActualLength; DWORD Signature; DWORD Flags; DWORD TimeDeferred; LONG ClashTest; MEMORY_ACTION LastAccessOperation; #if DUMP_STACK #if ONE_STACK LPVOID Stack[8]; // should be variable #else LPVOID CreateStack[4]; LPVOID LastAccessStack[4]; #endif // ONE_STACK #endif // DUMP_STACK DWORD Guard[2]; // // sizeof(MEMORY_SIGNATURE) currently 24 DWORDs in Win32 // } DEBUG_MEMORY_HEADER, *LPDEBUG_MEMORY_HEADER; // // DEBUG_MEMORY_FOOTER - used to check for overwrites // typedef struct { DWORD Guard[4]; DWORD Signature; SIZE_T BlockLength; // should be the same as the header DWORD Guard2[2]; // // sizeof(DEBUG_MEMORY_FOOTER) currently 8 DWORDs in Win32 // } DEBUG_MEMORY_FOOTER, *LPDEBUG_MEMORY_FOOTER; // // private data // PRIVATE BOOL MemoryPackageInitialized = FALSE; // // InternetDebugMemFlags - bitfield of flags controlling debug memory usage. // The default is no debug alloc (don't create header + footers) and to use // LocalAlloc() etc. // // // BUGBUG - I'm making an assumption that the compiler thinks the bits have the // same values as I think they have. If not, it could mess up the // registry/environment flags // PRIVATE struct { // default value DWORD bNoDebugAlloc : 1; // 0x00000001 TRUE DWORD bUseLocalAlloc : 1; // 0x00000002 TRUE DWORD bUseSymbols : 1; // 0x00000004 FALSE DWORD bAssertOnMemoryErrors : 1; // 0x00000008 FALSE DWORD bFillMemoryOnAlloc : 1; // 0x00000010 FALSE DWORD bFillMemoryOnFree : 1; // 0x00000020 FALSE DWORD bReportMemoryUsage : 1; // 0x00000040 FALSE DWORD bReportUnfreedBlocks : 1; // 0x00000080 FALSE DWORD bReportMemoryFooters : 1; // 0x00000100 FALSE DWORD bReportUserData : 1; // 0x00000200 FALSE DWORD bStopDumpIfBadBlock : 1; // 0x00000400 FALSE DWORD bLimitUnfreedBlocks : 1; // 0x00000800 FALSE DWORD bLimitUserData : 1; // 0x00001000 FALSE DWORD bDumpAsDwords : 1; // 0x00002000 FALSE DWORD bHeapNoSerialize : 1; // 0x00004000 FALSE DWORD bHeapGenerateExceptions : 1; // 0x00008000 FALSE DWORD bHeapIsGrowable : 1; // 0x00010000 FALSE DWORD bDeferFree : 1; // 0x00020000 FALSE DWORD bDumpToFile : 1; // 0x00040000 FALSE } InternetDebugMemFlags = { TRUE, // no debug alloc TRUE, // use LocalAlloc() FALSE, // don't load debug symbols FALSE, // don't assert on memory errors FALSE, // don't fill memory on alloc FALSE, // don't fill memory on free FALSE, // don't report memory usage (stats) FALSE, // don't report unfreed blocks FALSE, // don't report memory footers (irrelevant) FALSE, // don't report user data (irrelevant) FALSE, // don't stop dump if bad block (irrelevant) FALSE, // don't limit dump of unfreed blocks (irrelevant) FALSE, // don't limit dump of user data (irrelevant) FALSE, // don't dump user data as DWORDs (irrelevant) FALSE, // serialize access to heap (irrelevant) FALSE, // don't generate heap exceptions (irrelevant) TRUE, // heap is growable (irrelevant) FALSE, // don't defer frees FALSE // don't dump to wininet log file }; // // defines to make using InternetDebugMemFlags easier // #define bNoDebugAlloc InternetDebugMemFlags.bNoDebugAlloc #define bUseLocalAlloc InternetDebugMemFlags.bUseLocalAlloc #define bUseSymbols InternetDebugMemFlags.bUseSymbols #define bAssertOnMemoryErrors InternetDebugMemFlags.bAssertOnMemoryErrors #define bFillMemoryOnAlloc InternetDebugMemFlags.bFillMemoryOnAlloc #define bFillMemoryOnFree InternetDebugMemFlags.bFillMemoryOnFree #define bReportMemoryUsage InternetDebugMemFlags.bReportMemoryUsage #define bReportUnfreedBlocks InternetDebugMemFlags.bReportUnfreedBlocks #define bReportMemoryFooters InternetDebugMemFlags.bReportMemoryFooters #define bReportUserData InternetDebugMemFlags.bReportUserData #define bStopDumpIfBadBlock InternetDebugMemFlags.bStopDumpIfBadBlock #define bLimitUnfreedBlocks InternetDebugMemFlags.bLimitUnfreedBlocks #define bLimitUserData InternetDebugMemFlags.bLimitUserData #define bDumpAsDwords InternetDebugMemFlags.bDumpAsDwords #define bHeapNoSerialize InternetDebugMemFlags.bHeapNoSerialize #define bHeapGenerateExceptions InternetDebugMemFlags.bHeapGenerateExceptions #define bHeapIsGrowable InternetDebugMemFlags.bHeapIsGrowable #define bDeferFree InternetDebugMemFlags.bDeferFree #define bDumpToFile InternetDebugMemFlags.bDumpToFile PRIVATE DWORD MaxBlocksDumped = DEFAULT_MAX_BLOCKS_DUMPED; PRIVATE DWORD MaxUserDataDumped = DEFAULT_MAX_DATA_DUMPED; PRIVATE DWORD StackBacktraceDepth = DEFAULT_BACKTRACE_DEPTH; // // heap variables // PRIVATE HANDLE hDebugHeap = NULL; PRIVATE DWORD InitialHeapSize = DEFAULT_INITIAL_HEAP_SIZE; PRIVATE DWORD MaximumHeapSize = DEFAULT_MAXIMUM_HEAP_SIZE; PRIVATE HEAP_COMPACT_TYPE HeapCompactControl = HEAP_COMPACT_NEVER; PRIVATE HEAP_VALIDATE_TYPE HeapValidateControl = HEAP_VALIDATE_NEVER; // // debug mem signatures etc. // PRIVATE DWORD AllocAlignment = DEFAULT_ALLOC_ALIGNMENT; PRIVATE DWORD HeaderGuardSize = DEFAULT_HEADER_GUARD_SIZE; PRIVATE DWORD FooterGuardSize = DEFAULT_FOOTER_GUARD_SIZE; PRIVATE DWORD AllocMemoryFiller = DWORD_ALLOC_FILL; PRIVATE DWORD FreeMemoryFiller = DWORD_FREE_FILL; // // usage variables - access using some sort of lock (critsec/interlocked) // PRIVATE CRITICAL_SECTION MemoryVarsCritSec; PRIVATE SIZE_T TotalActualMemoryAllocated = 0; // cumulative PRIVATE SIZE_T TotalBlockMemoryAllocated = 0; // " PRIVATE SIZE_T TotalRealMemoryAllocated = 0; // " PRIVATE SIZE_T TotalActualMemoryFreed = 0; // " PRIVATE SIZE_T TotalBlockMemoryFreed = 0; // " PRIVATE SIZE_T TotalRealMemoryFreed = 0; // " PRIVATE SIZE_T ActualMemoryAllocated = 0; // difference PRIVATE SIZE_T BlockLengthAllocated = 0; // " PRIVATE SIZE_T RealLengthAllocated = 0; // " PRIVATE DWORD MemoryAllocations = 0; // cumulative PRIVATE DWORD GoodMemoryAllocations = 0; // " PRIVATE DWORD MemoryReAllocations = 0; // " PRIVATE DWORD GoodMemoryReAllocations = 0; // " PRIVATE DWORD MemoryFrees = 0; // " PRIVATE DWORD GoodMemoryFrees = 0; // " PRIVATE SIZE_T LargestBlockRequested = 0; PRIVATE SIZE_T LargestBlockAllocated = 0; PRIVATE LPSTR LargestBlockRequestedFile = NULL; PRIVATE DWORD LargestBlockRequestedLine = 0; PRIVATE SIZE_T SmallestBlockRequested = (SIZE_T)-1; PRIVATE SIZE_T SmallestBlockAllocated = (SIZE_T)-1; PRIVATE LPSTR SmallestBlockRequestedFile = NULL; PRIVATE DWORD SmallestBlockRequestedLine = 0; PRIVATE DWORD DeferFreeTime = 0; // // lists // PRIVATE SERIALIZED_LIST AllocatedBlockList; PRIVATE SERIALIZED_LIST DeferredFreeList; // // macros // #define MEMORY_ASSERT(x) \ if (bAssertOnMemoryErrors) { \ INET_ASSERT(x); \ } else { \ /* NOTHING */ \ } // // private prototypes // PRIVATE VOID DebugFillMem( IN LPVOID Pointer, IN SIZE_T Size, IN DWORD dwFiller ); PRIVATE HLOCAL InternetAlloc( IN UINT Flags, IN SIZE_T Size ); PRIVATE HLOCAL InternetFree( IN HLOCAL hLocal ); PRIVATE HLOCAL InternetReAlloc( IN HLOCAL hLocal, IN SIZE_T Size, IN UINT Flags ); PRIVATE SIZE_T InternetSize( IN HLOCAL hLocal ); PRIVATE HLOCAL InternetHeapAlloc( IN UINT Flags, IN SIZE_T Size ); PRIVATE HLOCAL InternetHeapReAlloc( IN HLOCAL hLocal, IN SIZE_T Size, IN UINT Flags ); PRIVATE HLOCAL InternetHeapFree( IN HLOCAL hLocal ); PRIVATE SIZE_T InternetHeapSize( IN HLOCAL hLocal ); PRIVATE BOOL InternetDebugCheckMemBlock( IN LPDEBUG_MEMORY_HEADER lpHeader ); PRIVATE VOID InternetDebugMemTest( VOID ); PRIVATE VOID ReportMemoryUsage( VOID ); PRIVATE VOID ReportMemoryBlocks( VOID ); PRIVATE VOID DumpDeferredFreeList( VOID ); PRIVATE VOID DumpMemoryList( IN LPSERIALIZED_LIST lpList ); PRIVATE VOID FindAndDumpDeferredBlock( IN HLOCAL hLocal ); PRIVATE BOOL DumpBlock( IN LPDEBUG_MEMORY_HEADER lpHeader ); PRIVATE BOOL DumpDebugMemoryHeader( LPDEBUG_MEMORY_HEADER lpHeader ); PRIVATE BOOL DumpDebugMemoryFooter( LPDEBUG_MEMORY_FOOTER lpFooter ); PRIVATE VOID DumpUserData( LPDEBUG_MEMORY_HEADER lpHeader ); PRIVATE LPSTR MapLastAccessOperation( MEMORY_ACTION Action ); PRIVATE LPSTR MapMemoryFlags( DWORD Flags, LPSTR Buffer ); PRIVATE LPSTR DbgMemGetDebugSymbol( DWORD Address, LPDWORD Offset ); // // functions // VOID InternetDebugMemInitialize( VOID ) /*++ Routine Description: Initializes debug memory allocator Arguments: None. Return Value: None. --*/ { BOOL init; init = (BOOL)InterlockedExchange((LPLONG)&MemoryPackageInitialized, TRUE); if (init) { DEBUG_PRINT(MEMALLOC, ERROR, ("Memory package already initialized\n" )); DEBUG_BREAK(MEMALLOC); return; } InitializeSerializedList(&AllocatedBlockList); InitializeSerializedList(&DeferredFreeList); InitializeCriticalSection(&MemoryVarsCritSec); // // sleaze: disable any debug output until we finish this. Debug log // routines want to allocate memory(!). InternetReadRegistryDword() // (called from InternetGetDebugVariable()) wants to perform DEBUG_ENTER // etc. // DWORD debugControlFlags = InternetDebugControlFlags; InternetDebugControlFlags = DBG_NO_DEBUG; // // if "WininetMem" is set then we set up to use debug memory - we use our // own heap, full debugging & reporting etc. (basically max memory debugging // as defined by me) // DWORD useDefaultDebugMemoryFlags = FALSE; InternetGetDebugVariable("WininetMem", &useDefaultDebugMemoryFlags ); if (useDefaultDebugMemoryFlags) { bNoDebugAlloc = FALSE; // use full debug allocator (header + footers, etc.) bUseLocalAlloc = FALSE; // use our own heap bUseSymbols = FALSE; // don't load debug symbols bAssertOnMemoryErrors = TRUE; // assert to debugger/log if memory errors bFillMemoryOnAlloc = TRUE; // fill user data w/ signature if not zeroinit bFillMemoryOnFree = TRUE; // fill freed memory (useful on Win95/non-debug on NT) bReportMemoryUsage = TRUE; // dump memory usage stats bReportUnfreedBlocks = TRUE; // dump unfreed blocks (headers) bReportMemoryFooters = TRUE; // dump unfreed block footers bReportUserData = TRUE; // dump unfreed block user data bStopDumpIfBadBlock = TRUE; // stop dumping if error occurs bLimitUnfreedBlocks = TRUE; // limit block dump in case of loop in list bLimitUserData = TRUE; // limit user data dump in case of bad length bDumpAsDwords = TRUE; // dump data in dc format vs. db bHeapNoSerialize = FALSE; // heap functions are serialized bHeapGenerateExceptions = FALSE;// heap functions return errors bHeapIsGrowable = FALSE; // limit heap to maximum size (1 Meg) if (useDefaultDebugMemoryFlags == 2) { bDumpToFile = TRUE; } } else { // // no use-debug-mem override, see if there are any specific flags set // InternetGetDebugVariable("WininetDebugMemFlags", (LPDWORD)&InternetDebugMemFlags ); } // // we used to load IMAGEHLP.DLL here and not use its functions until we were // dumping still in-use memory during DLL shutdown. Problem is that the // system has probably already freed IMAGEHLP.DLL by the time we come to use // it, resulting in GPF, so now we only load it at the time we're about to // use it // //if (bUseSymbols) { // InitSymLib(); //} if (!bUseLocalAlloc) { // // not using LocalAlloc(), using HeapAlloc(). Create heap // InitialHeapSize = DEFAULT_INITIAL_HEAP_SIZE; InternetGetDebugVariable("WininetDebugHeapInitialSize", &InitialHeapSize ); MaximumHeapSize = DEFAULT_MAXIMUM_HEAP_SIZE; InternetGetDebugVariable("WininetDebugHeapMaximumSize", &MaximumHeapSize ); if (bHeapIsGrowable) { MaximumHeapSize = 0; } hDebugHeap = HeapCreate((bHeapGenerateExceptions ? HEAP_GENERATE_EXCEPTIONS : 0) | (bHeapNoSerialize ? HEAP_NO_SERIALIZE : 0), InitialHeapSize, MaximumHeapSize ); if (hDebugHeap == NULL) { DEBUG_PUT(("HeapCreate() failed - %d\n", GetLastError() )); bUseLocalAlloc = TRUE; } else { HeapCompactControl = HEAP_COMPACT_NEVER; InternetGetDebugVariable("WininetDebugHeapCompactControl", (LPDWORD)&HeapCompactControl ); HeapValidateControl = HEAP_VALIDATE_NEVER; InternetGetDebugVariable("WininetDebugHeapValidateControl", (LPDWORD)&HeapValidateControl ); DEBUG_PUT(("Wininet heap = %#x\n", hDebugHeap )); } } // // restore default debug flags // InternetDebugControlFlags = debugControlFlags; //InternetDebugMemTest(); } VOID InternetDebugMemTerminate( IN BOOL bReport ) /*++ Routine Description: Frees resources allocated in InternetDebugMemInitialize, after checking that all memory is freed Arguments: bReport - TRUE if in-use blocks reported at termination Return Value: None. --*/ { BOOL bOpened = bReport ? InternetDebugMemReport(TRUE, FALSE) : FALSE; InternetDebugCheckMemFreed(FALSE); DeleteCriticalSection(&MemoryVarsCritSec); TerminateSerializedList(&AllocatedBlockList); TerminateSerializedList(&DeferredFreeList); if (hDebugHeap != NULL) { // // any future allocations(!) must use process heap // bUseLocalAlloc = TRUE; if (!HeapDestroy(hDebugHeap)) { DWORD error = GetLastError(); DEBUG_PRINT(MEMALLOC, ERROR, ("HeapDestroy(%#x) returns %s (%d)\n", hDebugHeap, InternetMapError(error), error )); MEMORY_ASSERT(FALSE); } } if (bOpened) { InternetCloseDebugFile(); } MemoryPackageInitialized = FALSE; } HLOCAL InternetDebugAllocMem( IN UINT Flags, IN UINT Size, IN LPSTR File, IN DWORD Line ) /*++ Routine Description: Debug memory allocator. If this succeeds, then the real block is put on our list and has its head & tail (& possibly contents) initialized. The caller gets an pointer which is an offset to the user area in the block Arguments: Flags - controlling flags (normally passed to LocalAlloc) Size - of block to allocate File - from where alloc called Line - in File Return Value: HLOCAL Success - pointer to caller's start of allocated block Failure - NULL --*/ { if (!MemoryPackageInitialized) { return NULL; } //dprintf("InternetDebugAllocMem(%#x, %d) = ", Flags, Size); InterlockedIncrement((LPLONG)&MemoryAllocations); // // keep these tests separate so we don't have to look up the flags #defines // INET_ASSERT(!(Flags & LMEM_MOVEABLE)); INET_ASSERT(!(Flags & LMEM_DISCARDABLE)); if (Size == 0) { DEBUG_PRINT(MEMALLOC, WARNING, ("InternetDebugAllocMem(%#x, %d)\n", Flags, Size )); MEMORY_ASSERT(FALSE); } SIZE_T blockLength; if (bNoDebugAlloc) { blockLength = Size; } else { if (Size > LargestBlockRequested) { LargestBlockRequested = Size; LargestBlockRequestedFile = File; LargestBlockRequestedLine = Line; } else if (Size < SmallestBlockRequested) { SmallestBlockRequested = Size; SmallestBlockRequestedFile = File; SmallestBlockRequestedLine = Line; } blockLength = ROUND_UP_DWORD(Size) + sizeof(DEBUG_MEMORY_HEADER) + sizeof(DEBUG_MEMORY_FOOTER); } // // possible problem: if Size + signatures would overflow UINT. Only really // problematic on 16-bit platforms // if (blockLength < Size) { DEBUG_PRINT(MEMALLOC, ERROR, ("can't allocate %lu bytes: would overflow\n", (DWORD)Size )); DEBUG_BREAK(MEMALLOC); //dprintf("NULL\n"); return NULL; } // // BUGBUG - allocating 0 bytes? // HLOCAL hLocal = InternetAlloc(Flags, blockLength); if (hLocal != NULL) { InterlockedIncrement((LPLONG)&GoodMemoryAllocations); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("failed to allocate %u bytes memory\n", blockLength )); DEBUG_BREAK(MEMALLOC); //dprintf("NULL\n"); return NULL; } SIZE_T actualLength = InternetSize(hLocal); SIZE_T requestedLength; if (bNoDebugAlloc) { blockLength = actualLength; requestedLength = actualLength; } else { requestedLength = Size; if (actualLength > LargestBlockAllocated) { LargestBlockAllocated = actualLength; } else if (actualLength < SmallestBlockAllocated) { SmallestBlockAllocated = actualLength; } } EnterCriticalSection(&MemoryVarsCritSec); TotalActualMemoryAllocated += actualLength; TotalBlockMemoryAllocated += blockLength; TotalRealMemoryAllocated += requestedLength; ActualMemoryAllocated += actualLength; BlockLengthAllocated += blockLength; RealLengthAllocated += requestedLength; LeaveCriticalSection(&MemoryVarsCritSec); if (bNoDebugAlloc || (hLocal == NULL)) { if ((hLocal != NULL) && !(Flags & LMEM_ZEROINIT) && bFillMemoryOnAlloc) { DebugFillMem(hLocal, Size, AllocMemoryFiller); } //dprintf("%#x\n", hLocal); return hLocal; } LPDEBUG_MEMORY_HEADER lpHeader = (LPDEBUG_MEMORY_HEADER)hLocal; //InitializeListHead(&lpHeader->List); lpHeader->ThreadId = GetCurrentThreadId(); lpHeader->CreatedFile = File; lpHeader->CreatedLine = Line; lpHeader->AccessedFile = File; lpHeader->AccessedLine = Line; lpHeader->RequestedLength = Size; lpHeader->BlockLength = blockLength; lpHeader->ActualLength = actualLength; lpHeader->Signature = HEADER_SIGNATURE; lpHeader->Flags = Flags; lpHeader->TimeDeferred = 0; lpHeader->ClashTest = -1; lpHeader->LastAccessOperation = MemAllocate; #if DUMP_STACK #if ONE_STACK memset(lpHeader->Stack, 0, sizeof(lpHeader->Stack)); GET_CALL_STACK(lpHeader->Stack); #else GET_CALLERS_ADDRESS(&lpHeader->CreateStack[0], &lpHeader->CreateStack[1] ); memset(lpHeader->CreateStack, 0, sizeof(lpHeader->CreateStack)); GET_CALL_STACK(lpHeader->CreateStack); memcpy(lpHeader->LastAccessStack, lpHeader->CreateStack, sizeof(lpHeader->LastAccessStack) ); #endif // ONE_STACK #endif // DUMP_STACK UINT i; for (i = 0; i < ARRAY_ELEMENTS(lpHeader->Guard); ++i) { lpHeader->Guard[i] = GUARD_DWORD_FILL; } // // BUGBUG - should be using AllocAlignment - could be > sizeof(DWORD) // if (!(Flags & LMEM_ZEROINIT) && bFillMemoryOnAlloc) { DebugFillMem(lpHeader + 1, Size, AllocMemoryFiller); } UINT bFillLength2 = (Size % sizeof(DWORD)) ? (sizeof(DWORD) - (Size % sizeof(DWORD))) : 0; LPBYTE lpbUserPointer = (LPBYTE)(lpHeader + 1) + Size; for (i = 0; i < bFillLength2; ++i) { *lpbUserPointer++ = BYTE_ALLOC_FILL_EXTRA; } LPDEBUG_MEMORY_FOOTER lpFooter = (LPDEBUG_MEMORY_FOOTER)lpbUserPointer; for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard); ++i) { lpFooter->Guard[i] = GUARD_DWORD_FILL; } lpFooter->BlockLength = blockLength; lpFooter->Signature = FOOTER_SIGNATURE; for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard2); ++i) { lpFooter->Guard2[i] = GUARD_DWORD_FILL; } if (!CheckEntryOnSerializedList(&AllocatedBlockList, &lpHeader->List, FALSE)) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugAllocMem(%d): %#x already on list?\n", Size, lpHeader )); MEMORY_ASSERT(FALSE); } // // put at the tail of list so we can view unfreed blocks in chronological // order // InsertAtTailOfSerializedList(&AllocatedBlockList, &lpHeader->List); //dprintf("%#x\n", lpHeader + 1); return (HLOCAL)(lpHeader + 1); } HLOCAL InternetDebugFreeMem( IN HLOCAL hLocal, IN LPSTR File, IN DWORD Line ) /*++ Routine Description: Frees a block of memory allocated by InternetDebugAllocMem(). Checks that the block is on our allocated block list, and that the header and footer areas are still intact Arguments: hLocal - handle (pointer) of block to free File - from where alloc called Line - in File Return Value: HLOCAL Success - NULL Failure - hLocal --*/ { if (!MemoryPackageInitialized) { return NULL; } //dprintf("InternetDebugFreeMem(%#x)\n", hLocal); InterlockedIncrement((LPLONG)&MemoryFrees); if (hLocal == NULL) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugFreeMem(NULL)\n" )); MEMORY_ASSERT(FALSE); return InternetFree(hLocal); } HLOCAL hLocalOriginal = hLocal; SIZE_T actualLength; SIZE_T blockLength; SIZE_T realLength; if (bNoDebugAlloc) { actualLength = InternetSize(hLocal); blockLength = actualLength; realLength = actualLength; } else { hLocal = (HLOCAL)((LPDEBUG_MEMORY_HEADER)hLocal - 1); actualLength = InternetSize(hLocal); LPDEBUG_MEMORY_HEADER lpHeader = (LPDEBUG_MEMORY_HEADER)hLocal; if (CheckEntryOnSerializedList(&AllocatedBlockList, &lpHeader->List, TRUE)) { RemoveFromSerializedList(&AllocatedBlockList, &lpHeader->List); if (!((lpHeader->ActualLength == actualLength) && (lpHeader->BlockLength <= actualLength) && !(lpHeader->BlockLength & (sizeof(DWORD) - 1)) && (lpHeader->RequestedLength < lpHeader->BlockLength))) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugFreeMem(%#x): block lengths mismatch\n", hLocalOriginal )); MEMORY_ASSERT(FALSE); } if (InternetDebugCheckMemBlock(lpHeader)) { blockLength = lpHeader->BlockLength; realLength = lpHeader->RequestedLength; } else { blockLength = 0; realLength = 0; } if (bDeferFree) { #if DUMP_STACK #if ONE_STACK memset(lpHeader->Stack, 0, sizeof(lpHeader->Stack)); GET_CALL_STACK(lpHeader->Stack); #else GET_CALLERS_ADDRESS(&lpHeader->CreateStack[0], &lpHeader->CreateStack[1] ); memset(lpHeader->CreateStack, 0, sizeof(lpHeader->CreateStack)); GET_CALL_STACK(lpHeader->CreateStack); memcpy(lpHeader->LastAccessStack, lpHeader->CreateStack, sizeof(lpHeader->LastAccessStack) ); #endif // ONE_STACK #endif // DUMP_STACK InsertAtTailOfSerializedList(&DeferredFreeList, &lpHeader->List); hLocal = NULL; } } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugFreeMem(%#x): can't find %#x\n", hLocalOriginal, &lpHeader->List )); MEMORY_ASSERT(FALSE); FindAndDumpDeferredBlock(hLocal); } } if (hLocal && bFillMemoryOnFree) { DebugFillMem(hLocal, actualLength, FreeMemoryFiller); } hLocal = InternetFree(hLocal); if (hLocal == NULL) { InterlockedIncrement((LPLONG)&GoodMemoryFrees); EnterCriticalSection(&MemoryVarsCritSec); TotalActualMemoryFreed += actualLength; TotalBlockMemoryFreed += blockLength; TotalRealMemoryFreed += realLength; ActualMemoryAllocated -= actualLength; BlockLengthAllocated -= blockLength; RealLengthAllocated -= realLength; LeaveCriticalSection(&MemoryVarsCritSec); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugFreeMem(%#x) failed\n", hLocalOriginal )); MEMORY_ASSERT(FALSE); hLocal = hLocalOriginal; } return hLocal; } HLOCAL InternetDebugReAllocMem( IN HLOCAL hLocal, IN UINT Size, IN UINT Flags, IN LPSTR File, IN DWORD Line ) /*++ Routine Description: Reallocates a debug memory block allocated by InternetDebugAllocMem() Arguments: hLocal - the handle (pointer) of the allocated block Size - requested size of new block; can be larger or smaller than current size Flags - controlling flags (normally passed to LocalReAlloc) File - from where alloc called Line - in File Return Value: HLOCAL Success - pointer to new block. May be same or different than previous pointer, depending on flags Failure - NULL --*/ { if (!MemoryPackageInitialized) { return NULL; } //dprintf("InternetDebugReAllocMem(%#x, %d, %#x)\n", hLocal, Size, Flags); InterlockedIncrement((LPLONG)&MemoryReAllocations); // // we can't handle the following flags // INET_ASSERT(!(Flags & LMEM_MODIFY)); // // can't handle reallocating down to zero // if (Size == 0) { MEMORY_ASSERT(FALSE); } HLOCAL hLocalOriginal = hLocal; LPDEBUG_MEMORY_HEADER lpHeader; SIZE_T actualLength; SIZE_T blockLength; SIZE_T requestedLength; SIZE_T oldRequestedLength; if (bNoDebugAlloc) { actualLength = InternetSize(hLocal); blockLength = actualLength; requestedLength = actualLength; } else { if (Size > LargestBlockRequested) { LargestBlockRequested = Size; LargestBlockRequestedFile = File; LargestBlockRequestedLine = Line; } else if (Size < SmallestBlockRequested) { SmallestBlockRequested = Size; SmallestBlockRequestedFile = File; SmallestBlockRequestedLine = Line; } lpHeader = (LPDEBUG_MEMORY_HEADER)hLocal - 1; hLocal = (HLOCAL)lpHeader; if (!CheckEntryOnSerializedList(&AllocatedBlockList, &lpHeader->List, TRUE)) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugReAllocMem(%#x): can't find %#x\n", hLocalOriginal )); MEMORY_ASSERT(FALSE); return hLocalOriginal; } RemoveFromSerializedList(&AllocatedBlockList, &lpHeader->List); InternetDebugCheckMemBlock(lpHeader); actualLength = InternetSize((HLOCAL)lpHeader); blockLength = lpHeader->BlockLength; requestedLength = lpHeader->RequestedLength; oldRequestedLength = requestedLength; if (!((lpHeader->ActualLength == actualLength) && (lpHeader->BlockLength <= actualLength) && !(lpHeader->BlockLength & (sizeof(DWORD) - 1)) && (lpHeader->RequestedLength < lpHeader->BlockLength))) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugReAllocMem(%#x): block lengths mismatch\n", hLocalOriginal )); MEMORY_ASSERT(FALSE); } } EnterCriticalSection(&MemoryVarsCritSec); ActualMemoryAllocated -= actualLength; BlockLengthAllocated -= blockLength; RealLengthAllocated -= requestedLength; LeaveCriticalSection(&MemoryVarsCritSec); requestedLength = Size; if (bNoDebugAlloc) { blockLength = Size; } else { blockLength = ROUND_UP_DWORD(Size) + sizeof(DEBUG_MEMORY_HEADER) + sizeof(DEBUG_MEMORY_FOOTER); } hLocal = InternetReAlloc(hLocal, blockLength, Flags); if (hLocal != NULL) { InterlockedIncrement((LPLONG)&GoodMemoryReAllocations); actualLength = InternetSize(hLocal); if (bNoDebugAlloc) { blockLength = actualLength; } else { lpHeader = (LPDEBUG_MEMORY_HEADER)hLocal; //InitializeListHead(&lpHeader->List); lpHeader->ThreadId = GetCurrentThreadId(); lpHeader->AccessedFile = File; lpHeader->AccessedLine = Line; lpHeader->RequestedLength = requestedLength; lpHeader->BlockLength = blockLength; lpHeader->ActualLength = actualLength; lpHeader->Flags = Flags; lpHeader->TimeDeferred = 0; lpHeader->ClashTest = -1; lpHeader->LastAccessOperation = MemReallocate; #if DUMP_STACK #if ONE_STACK #else //GET_CALLERS_ADDRESS(&lpHeader->LastAccessStack[0], // &lpHeader->LastAccessStack[1] // ); memset(lpHeader->LastAccessStack, 0, sizeof(lpHeader->LastAccessStack)); GET_CALL_STACK(lpHeader->LastAccessStack); #endif // ONE_STACK #endif // DUMP_STACK LPBYTE extraPointer; UINT dwFillLength; UINT i; if ((requestedLength > oldRequestedLength) && bFillMemoryOnAlloc && !(Flags & LMEM_ZEROINIT)) { extraPointer = (LPBYTE)(lpHeader + 1) + oldRequestedLength; SIZE_T difference = requestedLength - oldRequestedLength; DWORD dwFiller = AllocMemoryFiller; SIZE_T syncLength = oldRequestedLength & (sizeof(DWORD) - 1); if (syncLength) { syncLength = sizeof(DWORD) - syncLength; syncLength = min(syncLength, difference); difference -= syncLength; for (i = 0; i < syncLength; ++i) { *extraPointer++ = ((LPBYTE)&dwFiller)[i]; } } //dwFillLength = difference / sizeof(DWORD); //difference %= sizeof(DWORD); //while (dwFillLength--) { // *(LPDWORD)extraPointer = 0; // extraPointer += sizeof(DWORD); //} //while (difference--) { // *extraPointer++ = 0; //} if (difference) { DebugFillMem(extraPointer, difference, dwFiller); extraPointer += difference; } } else { extraPointer = (LPBYTE)(lpHeader + 1) + requestedLength; } SIZE_T bFillLength = (sizeof(DWORD) - (requestedLength % sizeof(DWORD))) & (sizeof(DWORD) - 1); while (bFillLength--) { *extraPointer++ = BYTE_ALLOC_FILL_EXTRA; } LPDEBUG_MEMORY_FOOTER lpFooter = (LPDEBUG_MEMORY_FOOTER)extraPointer; INET_ASSERT(lpFooter == (LPDEBUG_MEMORY_FOOTER) ((LPBYTE)(lpHeader + 1) + ROUND_UP_DWORD(requestedLength))); for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard); ++i) { lpFooter->Guard[i] = GUARD_DWORD_FILL; } lpFooter->Signature = FOOTER_SIGNATURE; lpFooter->BlockLength = blockLength; for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard2); ++i) { lpFooter->Guard2[i] = GUARD_DWORD_FILL; } InsertAtTailOfSerializedList(&AllocatedBlockList, &lpHeader->List); hLocal = (HLOCAL)(lpHeader + 1); } EnterCriticalSection(&MemoryVarsCritSec); ActualMemoryAllocated += actualLength; BlockLengthAllocated += blockLength; RealLengthAllocated += requestedLength; LeaveCriticalSection(&MemoryVarsCritSec); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("failed to reallocate %u bytes memory. Last error = %d\n", Size, GetLastError() )); DEBUG_BREAK(MEMALLOC); } return hLocal; } SIZE_T InternetDebugSizeMem( IN HLOCAL hLocal, IN LPSTR File, IN DWORD Line ) /*++ Routine Description: Returns actual allocated block size Arguments: hLocal - pointer to allocated block File - from where alloc called Line - in File Return Value: SIZE_T size of allocated block --*/ { if (!MemoryPackageInitialized) { return 0; } //dprintf("InternetDebugSizeMem(%#x)\n", hLocal); SIZE_T size = InternetSize(hLocal); if (!bNoDebugAlloc) { LPDEBUG_MEMORY_HEADER lpHeader = (LPDEBUG_MEMORY_HEADER)hLocal - 1; INET_ASSERT(lpHeader->Signature == HEADER_SIGNATURE); SIZE_T sizeInHeader = lpHeader->BlockLength - (sizeof(DEBUG_MEMORY_HEADER) + sizeof(DEBUG_MEMORY_FOOTER)); INET_ASSERT((sizeInHeader <= size) && (size >= sizeof(DEBUG_MEMORY_HEADER) + sizeof(DEBUG_MEMORY_FOOTER)) && (lpHeader->ActualLength == size) ); size = sizeInHeader; } return size; } BOOL InternetDebugCheckMemFreed( IN BOOL bReport ) /*++ Routine Description: Called when we're about to quit. Checks that all allocated memory has been cleaned up Arguments: bReport - TRUE if in-use blocks reported Return Value: BOOL TRUE - all allocated memory freed FALSE - we failed to clean up --*/ { if (bReport) { if (bReportMemoryUsage) { ReportMemoryUsage(); } if (bReportUnfreedBlocks) { ReportMemoryBlocks(); } } if (ElementsOnSerializedList(&AllocatedBlockList) != 0) { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetDebugCheckMemFreed(): %d memory blocks still allocated\n", MemoryAllocations - MemoryFrees )); MEMORY_ASSERT(FALSE); return FALSE; } return TRUE; } BOOL InternetDebugMemReport( IN BOOL bTerminateSymbols, IN BOOL bCloseFile ) /*++ Routine Description: Dumps in-use blocks to debugger and/or file Arguments: bTerminateSymbols - TRUE if we are to terminate symbols here bCloseFile - TRUE if we are to close debug log file here Return Value: BOOL - TRUE if we opened debug log file --*/ { BOOL bOpened = FALSE; if (bDumpToFile) { bOpened = InternetOpenDebugFile(); if (bOpened) { InternetDebugResetControlFlags(DBG_NO_DEBUG); InternetDebugSetControlFlags(DBG_TO_FILE | DBG_NO_ASSERT_BREAK); } } ReportMemoryUsage(); ReportMemoryBlocks(); if (bUseSymbols && bTerminateSymbols) { TermSymLib(); } if (bOpened && bCloseFile) { InternetCloseDebugFile(); } return bOpened; } // // private functions // PRIVATE VOID DebugFillMem( IN LPVOID Pointer, IN SIZE_T Size, IN DWORD dwFiller ) /*++ Routine Description: Fills memory with repeating debug pattern. Performs DWORD fill then finishes off any remaining bytes with character fill (rep movsd/rep movsb (ideally) (x86!)) Arguments: Pointer - memory to fill Size - of Pointer in bytes dwFiller - DWORD value to use Return Value: None. --*/ { INET_ASSERT(((DWORD_PTR)Pointer & (sizeof(DWORD) - 1)) == 0); SIZE_T dwFillLength = Size / sizeof(DWORD); SIZE_T bFillLength = Size % sizeof(DWORD); // // assume > 0 DWORDs to fill // LPDWORD lpdwPointer = (LPDWORD)Pointer; SIZE_T i; for (i = 0; i < dwFillLength; ++i) { *lpdwPointer++ = dwFiller; } if (bFillLength) { LPBYTE lpbPointer = (LPBYTE)lpdwPointer; for (i = 0; i < bFillLength; ++i) { *lpbPointer++ = ((LPBYTE)&dwFiller)[i]; } } } PRIVATE HLOCAL InternetAlloc( IN UINT Flags, IN SIZE_T Size ) /*++ Routine Description: Allocator - uses process (local) heap or component (debug) heap based on global flag setting Arguments: Flags - LocalAlloc flags Size - of block to allocate Return Value: HLOCAL Success - pointer to allocated block Failure - NULL --*/ { if (bUseLocalAlloc) { return LocalAlloc(Flags, Size); } else { return InternetHeapAlloc(Flags, Size); } } PRIVATE HLOCAL InternetFree( IN HLOCAL hLocal ) /*++ Routine Description: Deallocator - uses process (local) heap or component (debug) heap based on global flag setting Arguments: hLocal - pointer to block to deallocate Return Value: HLOCAL Success - NULL Failure - pointer to still allocated block --*/ { if (bUseLocalAlloc) { return LocalFree(hLocal); } else { return InternetHeapFree(hLocal); } } PRIVATE HLOCAL InternetReAlloc( IN HLOCAL hLocal, IN SIZE_T Size, IN UINT Flags ) /*++ Routine Description: Reallocator - uses process (local) heap or component (debug) heap based on global flag setting Arguments: hLocal - pointer to block to reallocate Flags - LocalAlloc flags Size - of block to allocate Return Value: HLOCAL Success - pointer to allocated block Failure - NULL --*/ { if (bUseLocalAlloc) { return LocalReAlloc(hLocal, Size, Flags); } else { return InternetHeapReAlloc(hLocal, Size, Flags); } } PRIVATE SIZE_T InternetSize( IN HLOCAL hLocal ) /*++ Routine Description: Block sizer - uses process (local) heap or component (debug) heap based on global flag setting Arguments: hLocal - pointer to block to size Return Value: SIZE_T Success - size of block Failure - 0 --*/ { if (bUseLocalAlloc) { return LocalSize(hLocal); } else { return InternetHeapSize(hLocal); } } PRIVATE HLOCAL InternetHeapAlloc( IN UINT Flags, IN SIZE_T Size ) /*++ Routine Description: Allocate memory from debug heap Arguments: Flags - passed to LocalAlloc Size - of block to allocate Return Value: HLOCAL --*/ { HLOCAL hLocal; if (hDebugHeap != NULL) { hLocal = (HLOCAL)HeapAlloc(hDebugHeap, (bHeapNoSerialize ? HEAP_NO_SERIALIZE : 0) | (bHeapGenerateExceptions ? HEAP_GENERATE_EXCEPTIONS : 0) | ((Flags & LMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0), Size ); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetHeapAlloc(): hDebugHeap is NULL\n" )); MEMORY_ASSERT(FALSE); hLocal = LocalAlloc(Flags, Size); } if (hLocal == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return hLocal; } PRIVATE HLOCAL InternetHeapReAlloc( IN HLOCAL hLocal, IN SIZE_T Size, IN UINT Flags ) /*++ Routine Description: Reallocate memory from debug heap Arguments: hLocal - pointer to block to reallocate Size - new size Flags - to LocalReAlloc Return Value: HLOCAL Success - pointer to new block Failure - NULL --*/ { if (hDebugHeap != NULL) { hLocal = (HLOCAL)HeapReAlloc(hDebugHeap, (bHeapNoSerialize ? HEAP_NO_SERIALIZE : 0) | (bHeapGenerateExceptions ? HEAP_GENERATE_EXCEPTIONS : 0) | ((Flags & LMEM_MOVEABLE) ? 0 : HEAP_REALLOC_IN_PLACE_ONLY) | ((Flags & LMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0), (LPVOID)hLocal, Size ); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetHeapReAlloc(): hDebugHeap is NULL\n" )); MEMORY_ASSERT(FALSE); // // block still allocated // hLocal = NULL; } return hLocal; } PRIVATE HLOCAL InternetHeapFree( IN HLOCAL hLocal ) /*++ Routine Description: Free memory to debug heap Arguments: hLocal - to free Return Value: HLOCAL Success - NULL Failure - hLocal --*/ { BOOL ok; if (hDebugHeap != NULL) { ok = HeapFree(hDebugHeap, bHeapNoSerialize ? HEAP_NO_SERIALIZE : 0, (LPVOID)hLocal ); if (!ok) { DWORD error = GetLastError(); DEBUG_PRINT(MEMALLOC, ERROR, ("HeapFree() returns %s (%d)\n", InternetMapError(error), error )); MEMORY_ASSERT(FALSE); } } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetHeapFree(): hDebugHeap is NULL\n" )); MEMORY_ASSERT(FALSE); ok = FALSE; } return ok ? NULL : hLocal; } PRIVATE SIZE_T InternetHeapSize( IN HLOCAL hLocal ) /*++ Routine Description: Determines size of block allocated from debug heap Arguments: hLocal - handle (pointer) of block for which to get size Return Value: SIZE_T Success - size of block Failure - 0 --*/ { SIZE_T size; if (hDebugHeap != NULL) { size = HeapSize(hDebugHeap, bHeapNoSerialize ? HEAP_NO_SERIALIZE : 0, (LPCVOID)hLocal ); } else { DEBUG_PRINT(MEMALLOC, ERROR, ("InternetHeapSize(): hDebugHeap is NULL\n" )); MEMORY_ASSERT(FALSE); size = (SIZE_T)-1; } if (size == (SIZE_T)-1) { SetLastError(ERROR_INVALID_HANDLE); return 0; } else { return size; } } PRIVATE BOOL InternetDebugCheckMemBlock( IN LPDEBUG_MEMORY_HEADER lpHeader ) /*++ Routine Description: Checks the consistency of a debug memory block Arguments: lpHeader - pointer to what we think is DEBUG_MEMORY_HEADER Return Value: None. --*/ { BOOL result; __try { LPDEBUG_MEMORY_FOOTER lpFooter = (LPDEBUG_MEMORY_FOOTER) ((LPBYTE)lpHeader + (lpHeader->BlockLength - sizeof(DEBUG_MEMORY_FOOTER))); BOOL headerGuardOverrun = FALSE; UINT i; for (i = 0; i < ARRAY_ELEMENTS(lpHeader->Guard); ++i) { if (lpHeader->Guard[i] != GUARD_DWORD_FILL) { headerGuardOverrun = TRUE; break; } } BOOL footerGuardOverrun = FALSE; for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard); ++i) { if (lpFooter->Guard[i] != GUARD_DWORD_FILL) { footerGuardOverrun = TRUE; break; } } BOOL footerGuard2Overrun = FALSE; for (i = 0; i < ARRAY_ELEMENTS(lpFooter->Guard2); ++i) { if (lpFooter->Guard2[i] != GUARD_DWORD_FILL) { footerGuard2Overrun = TRUE; break; } } LPBYTE lpExtraMemory = (LPBYTE)(lpHeader + 1) + lpHeader->RequestedLength; BOOL extraMemoryOverrun = FALSE; SIZE_T byteLength = ROUND_UP_DWORD(lpHeader->RequestedLength) - lpHeader->RequestedLength; for (i = 0; i < byteLength; ++i) { if (lpExtraMemory[i] != BYTE_ALLOC_FILL_EXTRA) { extraMemoryOverrun = TRUE; break; } } if (headerGuardOverrun || footerGuardOverrun || footerGuard2Overrun || extraMemoryOverrun || (lpHeader->Signature != HEADER_SIGNATURE) || (lpFooter->Signature != FOOTER_SIGNATURE) || (lpFooter->BlockLength != lpHeader->BlockLength)) { DEBUG_PRINT(MEMALLOC, ERROR, ("Bad block: %#x\n", lpHeader )); MEMORY_ASSERT(FALSE); result = FALSE; } else { result = TRUE; } } __except(EXCEPTION_EXECUTE_HANDLER) { DEBUG_PRINT(MEMALLOC, FATAL, ("Invalid block %#x - exception occurred\n", lpHeader )); MEMORY_ASSERT(FALSE); result = FALSE; } return result; } PRIVATE VOID InternetDebugMemTest( VOID ) { // // test // LPVOID p; p = (LPVOID)ALLOCATE_MEMORY(LMEM_FIXED, 1); *((LPBYTE)p + 1) = 'X'; p = (LPVOID)FREE_MEMORY((HLOCAL)p); INET_ASSERT(p == NULL); p = (LPVOID)ALLOCATE_MEMORY(LMEM_FIXED, 1); p = (LPVOID)REALLOCATE_MEMORY(p, 1111, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 439, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 720, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 256, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 16, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 128, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 32, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 4, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 64, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 63, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 64, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 65, LMEM_MOVEABLE | LMEM_ZEROINIT); p = (LPVOID)REALLOCATE_MEMORY(p, 65, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 64, LMEM_MOVEABLE); p = (LPVOID)REALLOCATE_MEMORY(p, 64, LMEM_MOVEABLE); p = (LPVOID)FREE_MEMORY((HLOCAL)p); INET_ASSERT(p == NULL); p = (LPVOID)ALLOCATE_MEMORY(LMEM_FIXED, 8); p = (LPVOID)REALLOCATE_MEMORY(p, 8, LMEM_FIXED); p = (LPVOID)REALLOCATE_MEMORY(p, 100000, LMEM_FIXED); p = (LPVOID)FREE_MEMORY((HLOCAL)p); INET_ASSERT(p == NULL); InternetDebugCheckMemFreed(TRUE); } PRIVATE VOID ReportMemoryUsage( VOID ) { // // make copies of variables in case debug print functions want to allocate // debug memory (!) // SIZE_T totalActualMemoryAllocated; SIZE_T totalBlockMemoryAllocated; SIZE_T totalRealMemoryAllocated; SIZE_T totalActualMemoryFreed; SIZE_T totalBlockMemoryFreed; SIZE_T totalRealMemoryFreed; SIZE_T actualMemoryAllocated; SIZE_T blockLengthAllocated; SIZE_T realLengthAllocated; DWORD memoryAllocations; DWORD goodMemoryAllocations; DWORD memoryReAllocations; DWORD goodMemoryReAllocations; DWORD memoryFrees; DWORD goodMemoryFrees; SIZE_T largestBlockRequested; SIZE_T largestBlockAllocated; SIZE_T smallestBlockRequested; SIZE_T smallestBlockAllocated; EnterCriticalSection(&MemoryVarsCritSec); totalActualMemoryAllocated = TotalActualMemoryAllocated; totalBlockMemoryAllocated = TotalBlockMemoryAllocated; totalRealMemoryAllocated = TotalRealMemoryAllocated; totalActualMemoryFreed = TotalActualMemoryFreed; totalBlockMemoryFreed = TotalBlockMemoryFreed; totalRealMemoryFreed = TotalRealMemoryFreed; actualMemoryAllocated = ActualMemoryAllocated; blockLengthAllocated = BlockLengthAllocated; realLengthAllocated = RealLengthAllocated; memoryAllocations = MemoryAllocations; goodMemoryAllocations = GoodMemoryAllocations; memoryReAllocations = MemoryReAllocations; goodMemoryReAllocations = GoodMemoryReAllocations; memoryFrees = MemoryFrees; goodMemoryFrees = GoodMemoryFrees; largestBlockRequested = LargestBlockRequested; largestBlockAllocated = LargestBlockAllocated; smallestBlockRequested = SmallestBlockRequested; smallestBlockAllocated = SmallestBlockAllocated; LeaveCriticalSection(&MemoryVarsCritSec); #ifdef _WIN64 char numBuf[64]; #else char numBuf[32]; #endif DEBUG_PUT(("********************************************************************************\n" "\n" "WinInet Debug Memory Usage:\n" "\n" "\tInternetDebugMemFlags = %#08x\n" "\n", InternetDebugMemFlags )); DEBUG_PUT(("\tTotal Memory Allocated. . . , . . . . %s\n", NiceNum(numBuf, totalActualMemoryAllocated, 0))); DEBUG_PUT(("\tTotal Block Length Allocated. . . . . %s\n", NiceNum(numBuf, totalBlockMemoryAllocated, 0))); DEBUG_PUT(("\tTotal User Length Allocated . . . . . %s\n", NiceNum(numBuf, totalRealMemoryAllocated, 0))); DEBUG_PUT(("\tTotal Memory Freed. . . . . . . . . . %s\n", NiceNum(numBuf, totalActualMemoryFreed, 0))); DEBUG_PUT(("\tTotal Block Length Freed. . . . . . . %s\n", NiceNum(numBuf, totalBlockMemoryFreed, 0))); DEBUG_PUT(("\tTotal User Length Freed . . . . . . . %s\n", NiceNum(numBuf, totalRealMemoryFreed, 0))); DEBUG_PUT(("\tMemory Still Allocated. . . . . . . . %s\n", NiceNum(numBuf, actualMemoryAllocated, 0))); DEBUG_PUT(("\tBlock Length Still Allocated. . . . . %s\n", NiceNum(numBuf, blockLengthAllocated, 0))); DEBUG_PUT(("\tUser Length Still Allocated . . . . . %s\n", NiceNum(numBuf, realLengthAllocated, 0))); DEBUG_PUT(("\tAttempted Memory Allocations. . . . . %s\n", NiceNum(numBuf, memoryAllocations, 0))); DEBUG_PUT(("\tGood Memory Allocations . . . . . . . %s\n", NiceNum(numBuf, goodMemoryAllocations, 0))); DEBUG_PUT(("\tAttempted Memory Reallocations. . . . %s\n", NiceNum(numBuf, memoryReAllocations, 0))); DEBUG_PUT(("\tGood Memory Reallocations . . . . . . %s\n", NiceNum(numBuf, goodMemoryReAllocations, 0))); DEBUG_PUT(("\tAttempted Memory Frees. . . . . . . . %s\n", NiceNum(numBuf, memoryFrees, 0))); DEBUG_PUT(("\tGood Memory Frees . . . . . . . . . . %s\n", NiceNum(numBuf, goodMemoryFrees, 0))); DEBUG_PUT(("\tLargest Block Requested . . . . . . . %s\n", NiceNum(numBuf, largestBlockRequested, 0))); DEBUG_PUT(("\tLargest Block Allocated . . . . . . . %s\n", NiceNum(numBuf, largestBlockAllocated, 0))); DEBUG_PUT(("\tLargest Block Requested From. . . . . %s!%d\n", SourceFilename(LargestBlockRequestedFile), LargestBlockRequestedLine)); DEBUG_PUT(("\tSmallest Block Requested. . . . . . . %s\n", NiceNum(numBuf, smallestBlockRequested, 0))); DEBUG_PUT(("\tSmallest Block Allocated. . . . . . . %s\n", NiceNum(numBuf, smallestBlockAllocated, 0))); DEBUG_PUT(("\tSmallest Block Requested From . . . . %s!%d\n", SourceFilename(SmallestBlockRequestedFile), SmallestBlockRequestedLine)); DEBUG_PUT(("\n" "\tBlocks Still Allocated. . . . . . . . %s\n", NiceNum(numBuf, goodMemoryAllocations - goodMemoryFrees, 0))); DEBUG_PUT(("\tMemory Still Allocated. . . . . . . . %s\n", NiceNum(numBuf, totalActualMemoryAllocated - totalActualMemoryFreed, 0))); DEBUG_PUT(("\n" "********************************************************************************\n" "\n")); } PRIVATE VOID ReportMemoryBlocks( VOID ) { DEBUG_PUT(("ReportMemoryBlocks\n\n")); DEBUG_PUT(("AllocatedBlockList:\n\n")); DumpMemoryList(&AllocatedBlockList); if (bDeferFree) { DumpDeferredFreeList(); } } PRIVATE VOID DumpDeferredFreeList( VOID ) { DEBUG_PUT(("DeferredFreeList:\n\n")); DumpMemoryList(&DeferredFreeList); } PRIVATE VOID DumpMemoryList( IN LPSERIALIZED_LIST lpList ) { LPDEBUG_MEMORY_HEADER lpHeader; int counter = 1; if (bUseSymbols) { // // have to load IMAGEHLP.DLL here because we're in DLL_PROCESS_DETACH // and if we loaded it earlier, there's a good chance the system has // already freed it // InitSymLib(); } LockSerializedList(lpList); lpHeader = (LPDEBUG_MEMORY_HEADER)HeadOfSerializedList(lpList); while (lpHeader != (LPDEBUG_MEMORY_HEADER)SlSelf(lpList)) { DEBUG_PUT(("Block # %d\n", counter)); if (!DumpBlock(lpHeader)) { break; } DEBUG_PUT(("********************************************************************************\n\n")); lpHeader = (LPDEBUG_MEMORY_HEADER)(lpHeader->List.Flink); ++counter; } UnlockSerializedList(lpList); } PRIVATE VOID FindAndDumpDeferredBlock( IN HLOCAL hLocal ) { LPDEBUG_MEMORY_HEADER lpHeader; LockSerializedList(&DeferredFreeList); lpHeader = (LPDEBUG_MEMORY_HEADER)HeadOfSerializedList(&DeferredFreeList); while (lpHeader != (LPDEBUG_MEMORY_HEADER)SlSelf(&DeferredFreeList)) { if (hLocal == (HLOCAL)lpHeader) { DumpBlock(lpHeader); break; } lpHeader = (LPDEBUG_MEMORY_HEADER)(lpHeader->List.Flink); } UnlockSerializedList(&DeferredFreeList); } PRIVATE BOOL DumpBlock( IN LPDEBUG_MEMORY_HEADER lpHeader ) { BOOL ok = DumpDebugMemoryHeader(lpHeader); if (!ok && bStopDumpIfBadBlock) { DEBUG_PUT(("*** stopping block dump: header @ %#x is bad\n", lpHeader)); return FALSE; } if (bReportUserData) { DumpUserData(lpHeader); } if (bReportMemoryFooters) { LPDEBUG_MEMORY_FOOTER lpFooter; lpFooter = (LPDEBUG_MEMORY_FOOTER) ((LPBYTE)lpHeader + sizeof(*lpHeader) + ROUND_UP_DWORD(lpHeader->RequestedLength)); ok = DumpDebugMemoryFooter(lpFooter); if (!ok && bStopDumpIfBadBlock) { DEBUG_PUT(("*** stopping block dump: footer @ %#x is bad\n", lpFooter)); return FALSE; } } return TRUE; } PRIVATE BOOL DumpDebugMemoryHeader( LPDEBUG_MEMORY_HEADER lpHeader ) { char numBuf[32]; BOOL result; LPSTR symbol; DWORD offset; int i; char flagsBuf[256]; __try { DEBUG_PUT(("DEBUG_MEMORY_HEADER @ %#x:\n" "\n", lpHeader )); DEBUG_PUT(("\tList. . . . . . . . . F=%#x B=%#x\n", lpHeader->List.Flink, lpHeader->List.Blink )); DEBUG_PUT(("\tThread. . . . . . . . %#x\n", lpHeader->ThreadId )); DEBUG_PUT(("\tAllocated From. . . . %s!%d\n", SourceFilename(lpHeader->CreatedFile), lpHeader->CreatedLine )); DEBUG_PUT(("\tLast Accessed From. . %s!%d\n", SourceFilename(lpHeader->AccessedFile), lpHeader->AccessedLine )); DEBUG_PUT(("\tRequested Length. . . %s\n", NiceNum(numBuf, lpHeader->RequestedLength, 0) )); DEBUG_PUT(("\tBlock Length. . . . . %s\n", NiceNum(numBuf, lpHeader->BlockLength, 0) )); DEBUG_PUT(("\tActual Length . . . . %s\n", NiceNum(numBuf, lpHeader->ActualLength, 0) )); DEBUG_PUT(("\tSignature . . . . . . %x (%s)\n", lpHeader->Signature, (lpHeader->Signature == HEADER_SIGNATURE) ? "Good" : "BAD!!!" )); DEBUG_PUT(("\tFlags . . . . . . . . %08x %s\n", lpHeader->Flags, MapMemoryFlags(lpHeader->Flags, flagsBuf) )); DEBUG_PUT(("\tTime Deferred . . . . %08x\n", lpHeader->TimeDeferred )); DEBUG_PUT(("\tClash Test. . . . . . %d\n", lpHeader->ClashTest )); DEBUG_PUT(("\tLast Operation. . . . %s\n", MapLastAccessOperation(lpHeader->LastAccessOperation) )); #if DUMP_STACK #if ONE_STACK if (lpHeader->Stack[0]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->Stack[0], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\tStack . . . . . . . . %08x %s+%#x\n", lpHeader->Stack[0], symbol, offset )); for (i = 1; i < ARRAY_ELEMENTS(lpHeader->Stack); ++i) { //if (!lpHeader->lpHeader->Stack[i]) { // break; //} if (lpHeader->Stack[i]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->Stack[i], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\t. . . . . . . . . . . %08x %s+%#x\n", lpHeader->Stack[i], symbol, offset )); } #else if (lpHeader->LastAccessStack[0]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->LastAccessStack[0], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\tLastAccessStack . . . %08x %s+%#x\n", lpHeader->LastAccessStack[0], symbol, offset )); for (i = 1; i < ARRAY_ELEMENTS(lpHeader->LastAccessStack); ++i) { //if (!lpHeader->LastAccessStack[i]) { // break; //} if (lpHeader->LastAccessStack[i]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->LastAccessStack[i], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\t. . . . . . . . . . . %08x %s+%#x\n", lpHeader->LastAccessStack[i], symbol, offset )); } if (lpHeader->CreateStack[0]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->CreateStack[0], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\tCreateStack . . . . . %08x %s+%#x\n", lpHeader->CreateStack[0], symbol, offset )); for (i = 1; i < ARRAY_ELEMENTS(lpHeader->CreateStack); ++i) { //if (!lpHeader->lpHeader->CreateStack[i]) { // break; //} if (lpHeader->CreateStack[i]) { symbol = DbgMemGetDebugSymbol((DWORD)lpHeader->CreateStack[i], &offset); } else { symbol = ""; offset = 0; } DEBUG_PUT(("\t. . . . . . . . . . . %08x %s+%#x\n", lpHeader->CreateStack[i], symbol, offset )); } #endif // ONE_STACK #endif // DUMP_STACK DEBUG_PUT(("\tGuard . . . . . . . . %08x\n" "\n", lpHeader->Guard[0] )); result = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { //DEBUG_PUT(("DEBUG_MEMORY_HEADER @ %#x is BAD\n", lpHeader)); result = FALSE; } return result; } PRIVATE BOOL DumpDebugMemoryFooter( LPDEBUG_MEMORY_FOOTER lpFooter ) { char numBuf[32]; BOOL result; _try { DEBUG_PUT(("DEBUG_MEMORY_FOOTER @ %#x:\n" "\n", lpFooter )); DEBUG_PUT(("\tGuard . . . . . . . . %08x %08x %08x %08x\n", lpFooter->Guard[0], lpFooter->Guard[1], lpFooter->Guard[2], lpFooter->Guard[3] )); DEBUG_PUT(("\tSignature . . . . . . %x (%s)\n", lpFooter->Signature, (lpFooter->Signature == FOOTER_SIGNATURE) ? "Good" : "BAD!!!" )); DEBUG_PUT(("\tBlock Length. . . . . %s\n", NiceNum(numBuf, lpFooter->BlockLength, 0) )); DEBUG_PUT(("\tGuard2. . . . . . . . %08x %08x\n" "\n", lpFooter->Guard2[0], lpFooter->Guard2[1] )); result = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { //DEBUG_PUT(("DEBUG_MEMORY_FOOTER @ %#x is BAD\n", lpFooter)); result = FALSE; } return result; } PRIVATE VOID DumpUserData( LPDEBUG_MEMORY_HEADER lpHeader ) { static char spaces[] = " "; // 15 * 3 + 2 SIZE_T userSize = lpHeader->RequestedLength; SIZE_T Size = ROUND_UP_DWORD(userSize); LPBYTE Address = (LPBYTE)(lpHeader + 1); DEBUG_PUT(("\t%d (%#x) bytes of user data (rounded to %d (%#x)) @ %#x\n\n", userSize, userSize, Size, Size, Address )); if (bLimitUserData && (Size > MaxUserDataDumped)) { DEBUG_PUT(("*** User data length %d too large: limited to %d (probably bad block)\n", Size, MaxUserDataDumped )); Size = MaxUserDataDumped; } // // dump out the data, debug style // while (Size) { char buf[128]; int len; int clen; rsprintf(buf, "\t%08x ", Address); clen = (int)min(Size, 16); if (bDumpAsDwords) { len = clen / 4; } else { len = clen; } // // dump the hex representation of each character - up to 16 per line // int i; for (i = 0; i < len; ++i) { if (bDumpAsDwords) { rsprintf(&buf[11 + i * 9], "%08x ", ((LPDWORD)Address)[i]); } else { rsprintf(&buf[11 + i * 3], ((i & 15) == 7) ? "%02.2x-" : "%02.2x ", Address[i] & 0xff ); } } // // write as many spaces as required to tab to ASCII field // int offset; if (bDumpAsDwords) { memcpy(&buf[11 + i * 9], spaces, (4 - len) * 9 + 2); offset = 49; } else { memcpy(&buf[11 + i * 3], spaces, (16 - len) * 3 + 2); offset = 60; } // // dump ASCII representation of each character // for (i = 0; i < clen; ++i) { char ch; ch = Address[i]; buf[offset + i] = ((ch < 32) || (ch > 127)) ? '.' : ch; } buf[offset + i++] = '\r'; buf[offset + i++] = '\n'; buf[offset + i] = 0; // // InternetDebugOut() - no printf expansion (%s in data!), no prefixes // InternetDebugOut(buf, FALSE); Address += clen; Size -= clen; } InternetDebugOut("\r\n", FALSE); } PRIVATE LPSTR MapLastAccessOperation( MEMORY_ACTION Action ) { switch (Action) { case MemAllocate: return "Alloc"; case MemReallocate: return "Realloc"; case MemLock: return "Lock"; case MemUnlock: return "Unlock"; case MemFree: return "Free"; case MemSize: return "Size"; } return "?"; } PRIVATE LPSTR MapMemoryFlags( DWORD Flags, LPSTR Buffer ) { LPSTR buf = Buffer; int i = 0; *buf++ = '('; if (Flags & LMEM_DISCARDABLE) { buf += wsprintf(buf, "DISCARDABLE"); ++i; } if (Flags & LMEM_ZEROINIT) { if (i) { buf += wsprintf(buf, ", "); } ++i; buf += wsprintf(buf, "ZEROINIT"); } if (Flags & LMEM_NODISCARD) { if (i) { buf += wsprintf(buf, ", "); } ++i; buf += wsprintf(buf, "NODISCARD"); } if (Flags & LMEM_NOCOMPACT) { if (i) { buf += wsprintf(buf, ", "); } ++i; buf += wsprintf(buf, "NOCOMPACT"); } if (i) { buf += wsprintf(buf, ", "); } ++i; buf += wsprintf(buf, (Flags & LMEM_MOVEABLE) ? "MOVEABLE" : "FIXED"); *buf++ = ')'; *buf++ = '\0'; return Buffer; } PRIVATE LPSTR DbgMemGetDebugSymbol( DWORD Address, LPDWORD Offset ) { //if (!bUseSymbols) { // return "?"; //} // // RLF 04/14/98 - IMAGEHLP blowing up probably because we are doing this at // process detach time. Just return offset and run convsym // utility on wininet.log // //return GetDebugSymbol(Address, Offset); *Offset = Address; return ""; } #endif // defined(USE_DEBUG_MEMORY)