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.
2797 lines
72 KiB
2797 lines
72 KiB
/*++
|
|
|
|
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 <wininetp.h>
|
|
#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)
|