Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1032 lines
20 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
hcttools.c
Abstract:
This module contains helpful functions and wrappers for debugging and
tools commonly used throughout hct programs.
Environment:
User mode
Revision History:
05-Sep-1997 : Jason Allor (jasonall)
--*/
#include "hcttools.h"
#ifdef DEBUG
static PBLOCKINFO g_pbiHead;
static USHORT g_usMalloc;
static USHORT g_usFree;
#endif
/*++
Routine Description: InitializeMemoryManager
Initializes globals used by this module
Arguments:
none
Return Value:
VOID
--*/
VOID InitializeMemoryManager()
{
#ifdef DEBUG
g_pbiHead = NULL;
g_usMalloc = 0;
g_usFree = 0;
#else
return;
#endif
} // InitializeMemoryManager //
#ifdef DEBUG
/*++
Routine Description: GetBlockInfo
Searches the memory log to find the block that pb points into and
returns a pointer to the corresponding blockinfo structure of the
memory log. Note: pb must point into an allocated block or you
will get an assertion failure. The function either asserts or
succeeds -- it never returns an error.
Arguments:
pb: block to get info about
Return Value:
BLOCKINFO: returns the information
--*/
static PBLOCKINFO GetBlockInfo(IN PBYTE pb)
{
PBLOCKINFO pbi;
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
{
PBYTE pbStart = pbi->pb;
PBYTE pbEnd = pbi->pb + pbi->size - 1;
if (PtrGrtrEq(pb, pbStart) && PtrLessEq(pb, pbEnd))
{
break;
}
}
//
// Couldn't find pointer? It is garbage, pointing to a block that was
// freed, or pointing to a block that moved when it was resized?
//
__ASSERT(pbi != NULL);
return (pbi);
} // GetBlockInfo //
/*++
Routine Description: CreateBlockInfo
Creates a log entry for the memory block defined by pbNew:sizeNew.
Arguments:
pbNew: new block
sizeNew: the size of the new block
cszFile: the file name the code is located in \ these tell what code
iLine: the line number of the assertion / called the malloc
Return Value:
BOOL: TRUE if it successfully creates the log information,
FALSE otherwise
--*/
BOOL CreateBlockInfo(OUT PBYTE pbNew,
IN size_t sizeNew,
IN PCHAR cszFile,
IN UINT iLine)
{
PBLOCKINFO pbi;
__ASSERT(pbNew != NULL && sizeNew != 0);
pbi = (PBLOCKINFO)malloc(sizeof(BLOCKINFO));
if (pbi != NULL)
{
pbi->pb = pbNew;
pbi->size = sizeNew;
pbi->pbiNext = g_pbiHead;
pbi->iLine = iLine;
strcpy(pbi->cszFile, cszFile);
g_pbiHead = pbi;
}
return (pbi != NULL);
} // CreateBlockInfo //
/*++
Routine Description: FreeBlockInfo
Destroys the log entry for the memory block that pbToFree
points to. pbToFree must point to the start of an allocated
block or you will get an assertion failure
Arguments:
pbToFree: the block to free
Return Value:
void
--*/
void FreeBlockInfo(IN PBYTE pbToFree)
{
PBLOCKINFO pbi, pbiPrev;
pbiPrev = NULL;
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
{
if (PtrEqual(pbi->pb, pbToFree))
{
if (pbiPrev == NULL)
{
g_pbiHead = pbi->pbiNext;
}
else
{
pbiPrev->pbiNext = pbi->pbiNext;
}
break;
}
pbiPrev = pbi;
}
//
// If pbi is NULL, the pbToFree is invalid
//
__ASSERT(pbi != NULL);
//
// Destroy the contents of *pbi before freeing them
//
memset(pbi, GARBAGE, sizeof(BLOCKINFO));
free(pbi);
} // FreeBlockInfo //
/*++
Routine Description: UpdateBlockInfo
UpdateBlockInfo looks up the log information for the memory
block that pbOld points to. The function then updates the log
information to reflect the fact that the block new lives at pbNew
and is "sizeNew" bytes long. pbOld must point to the start of an
allocated block or you will get an assertion failure
Arguments:
pbOld: old location
pbNew: new location
sizeNew: the new size
Return Value:
void
--*/
void UpdateBlockInfo(IN PBYTE pbOld,
IN PBYTE pbNew,
IN size_t sizeNew)
{
PBLOCKINFO pbi;
__ASSERT(pbNew != NULL && sizeNew);
pbi = GetBlockInfo(pbOld);
__ASSERT(pbOld == pbi->pb);
pbi->pb = pbNew;
pbi->size = sizeNew;
} // UpdateBlockInfo //
/*++
Routine Description: SizeOfBlock
Returns the size of the block that pb points to. pb must point to
the start of an allocated block or you will get an assertion failure
Arguments:
pb: the block to find the size of
Return Value:
size_t: returns the size
--*/
size_t SizeOfBlock(IN PBYTE pb)
{
PBLOCKINFO pbi;
pbi = GetBlockInfo(pb);
__ASSERT(pb == pbi->pb);
return (pbi->size);
return 1;
} // SizeOfBlock //
/*++
Routine Description: ClearMemoryRefs
ClearMemoryRefs marks all blocks in the memory log as
being unreferenced
Arguments:
void
Return Value:
void
--*/
void ClearMemoryRefs()
{
PBLOCKINFO pbi;
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
{
pbi->boolReferenced = FALSE;
}
} // ClearMemoryRefs //
/*++
Routine Description: NoteMemoryRef
Marks the block that pv points into as being referenced.
Note: pv does not have to point to the start of a block; it may
point anywhere withing an allocated block
Arguments:
pv: the block to mark
Return Value:
void
--*/
void NoteMemoryRef(IN PVOID pv)
{
PBLOCKINFO pbi;
pbi = GetBlockInfo((PBYTE)pv);
pbi->boolReferenced = TRUE;
} // NoteMemoryRef //
/*++
Routine Description: CheckMemoryRefs
Scans the memory log looking for blocks that have not been
marked with a call to NoteMemoryRef. It this function finds an
unmarked block, it asserts.
Arguments:
void
Return Value:
void
--*/
void CheckMemoryRefs()
{
PBLOCKINFO pbi;
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
{
//
// A simple check for block integrity. If this assert fires, it
// means that something is wrong with the debug code that manages
// blockinfo or, possibly, that a wild memory store has thrashed
// the data structure. Either way, there's a bug
//
__ASSERT(pbi->pb != NULL && pbi->size);
//
// A check for lost or leaky memory. If this assert fires, it means
// that the app has either lost track of this block or that not all
// global pointers have been accounted for with NoteMemoryRef.
//
__ASSERT(pbi->boolReferenced);
}
} // CheckMemoryRefs //
/*++
Routine Description: ValidPointer
Verifies that pv points into an allocated memory block and that there
are at least "size" allocated bytes from pv to the end of a block. If
either condition is not met, ValidPointer will assert.
Arguments:
pv: the block to check out
size: the size to match against
Return Value:
BOOL: Always returns TRUE.
The reason this always returns TRUE is to allow you to call the
function within an __ASSERT macro. While this isn't the most efficient
method to use, using the macro neatly handles the debug-vs-ship
version control issue without your having to resort to #ifdef
DEBUGS or to introducing other __ASSERT-like macros.
--*/
BOOL ValidPointer(IN PVOID pv,
IN size_t size)
{
PBLOCKINFO pbi;
PBYTE pb = (PBYTE)pv;
__ASSERT(pv != NULL && size);
pbi = GetBlockInfo(pb);
//
// size isn't valid if pb+size overflows the block
//
__ASSERT(PtrLessEq(pb + size, pbi->pb + pbi->size));
return TRUE;
} // ValidPointer //
/*++
Routine Description: _Assert
My assert function. Simply outputs the file name and line number
to a MessageBox and then kills the program. Note: this should only
be called by the __ASSERT macro above
Arguments:
cszFile: the file name the code is located in
iLine: the line number of the assertion
Return Value:
void
--*/
void MyAssert(IN PCHAR cszFile,
IN UINT iLine)
{
WCHAR wszFile[100];
TCHAR tszMessage[100];
fflush(NULL);
_stprintf(tszMessage, TEXT("Assertion failed: %s, line %u"),
ConvertAnsi(cszFile, wszFile, 100), iLine);
MessageBox(NULL, tszMessage, NULL, MB_OK);
abort();
} // MyAssert //
#endif // DEBUG
/*++
Routine Description: __MALLOC
Don't call this function. Instead, call the macro MyMalloc,
which calls this function.
Wrapper for malloc function. Provides better calling convention
and debug syntax. Calling example:
malloc: pbBlock = (byte *)malloc(sizeof(pbBlock));
MyMalloc: MyMalloc(&pbBlock, sizeof(pbBlock));
Arguments:
ppv: variable to be malloced
size: the memory size to use
cszFile: the file name the code is located in \ these tell what code
iLine: the line number of the assertion / called the malloc
Return Value:
BOOL: TRUE if malloc succeeds, FALSE if it does not
--*/
BOOL __MALLOC(IN OUT void **ppv,
IN size_t size,
IN PCHAR cszFile,
IN UINT iLine)
{
BYTE **ppb = (BYTE **)ppv;
__ASSERT(ppv != NULL && size != 0);
*ppb = (BYTE *)malloc(size);
#ifdef DEBUG
g_usMalloc++;
if (*ppb != NULL)
{
//
// Shred the memory
//
memset(*ppb, GARBAGE, size);
//
// Record information about this block in memory
//
if (!CreateBlockInfo(*ppb, size, cszFile, iLine))
{
free(*ppb);
*ppb = NULL;
}
}
#endif
return (*ppb != NULL);
} // __MALLOC //
/*++
Routine Description: __FREE
Wrapper for free function. Provides debug syntax.
Sets the pointer to NULL after it is done freeing it.
This should always be called in a format such as:
Old syntax: free(pVariable);
New syntax: MyFree(&pVariable);
Arguments:
ppv: variable to be freed
Return Value:
void
--*/
void __FREE(IN void **ppv)
{
//
// *ppv should never be NULL. This is technically legal
// but it's not good behavior
//
__ASSERT (*ppv != NULL);
#ifdef DEBUG
g_usFree++;
//
// Shred the memory
//
memset(*ppv, GARBAGE, SizeOfBlock(*ppv));
FreeBlockInfo(*ppv);
#endif
free(*ppv);
*ppv = NULL;
} // __FREE //
/*++
Routine Description: CheckAllocs
Checks to make sure that everything has been freed. This
should be called right before the program ends.
Arguments:
none
Return Value:
VOID: If this function finds any allocated memory that
has not been freed, it will __ASSERT.
--*/
VOID CheckAllocs()
{
#ifndef DEBUG
return;
#else
PBLOCKINFO pbi;
TCHAR tszInvalidMemoryLocations[10000];
USHORT usCounter = 0;
BOOL bBadMemFound = FALSE;
WCHAR wszUnicode[100];
_stprintf(tszInvalidMemoryLocations,
TEXT("Unfreed Malloc Locations: \n\n"));
_stprintf(tszInvalidMemoryLocations,
TEXT("%sMallocs = %d Frees = %d\n\n"),
tszInvalidMemoryLocations, g_usMalloc, g_usFree);
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
{
bBadMemFound = TRUE;
//
// Only print out the first 20 unfreed blocks we
// find, so the messagebox won't be too huge
//
if (usCounter++ < 20)
{
_stprintf(tszInvalidMemoryLocations,
TEXT("%sFile = %s \t Line = %d\n"),
tszInvalidMemoryLocations,
ConvertAnsi(pbi->cszFile, wszUnicode, 100),
pbi->iLine);
}
}
if (bBadMemFound)
{
MessageBox(NULL, tszInvalidMemoryLocations, NULL,
MB_OK | MB_ICONERROR);
__ASSERT(TRUE);
}
#endif
} // CheckAllocs //
/*++
Routine Description: StrNCmp
Compares two TCHAR strings. Both strings length must be >= ulLength
Arguments:
tszString1: the first string
tszString2: the second string
ulLength: the length to compare
Return Value:
BOOL: TRUE if strings are equal, FALSE if not
--*/
BOOL StrNCmp(IN PTCHAR tszString1,
IN PTCHAR tszString2,
IN ULONG ulLength)
{
ULONG ulIndex;
//
// Both strings must be valid and ulLength must be > 0
//
__ASSERT(tszString1 != NULL);
__ASSERT(tszString2 != NULL);
__ASSERT(ulLength > 0);
if (_tcslen(tszString1) < ulLength ||
_tcslen(tszString2) < ulLength)
{
goto RetFALSE;
}
for (ulIndex = 0; ulIndex < ulLength; ulIndex++)
{
if (tszString1[ulIndex] != tszString2[ulIndex])
{
goto RetFALSE;
}
}
return TRUE;
RetFALSE:
return FALSE;
} // StrNCmp //
/*++
Routine Description: StrCmp
Compares two TCHAR strings
Arguments:
tszString1: the first string
tszString2: the second string
Return Value:
BOOL: TRUE if strings are equal, FALSE if not
--*/
BOOL StrCmp(IN PTCHAR tszString1,
IN PTCHAR tszString2)
{
ULONG ulIndex;
ULONG ulLength;
//
// Both strings must be valid
//
__ASSERT(tszString1 != NULL);
__ASSERT(tszString2 != NULL);
ulLength = _tcslen(tszString1);
if (ulLength != _tcslen(tszString2))
{
goto RetFALSE;
}
for (ulIndex = 0; ulIndex < ulLength; ulIndex++)
{
if (tszString1[ulIndex] != tszString2[ulIndex])
{
goto RetFALSE;
}
}
return TRUE;
RetFALSE:
return FALSE;
} // StrCmp //
/*++
Routine Description: AnsiToUnicode
Converts an Ansi string to a Unicode string.
Arguments:
cszAnsi: the Ansi string to convert
wszUnicode: unicode buffer to store new string. Must not be NULL
ulSize: must be set to the size of the wszUnicode buffer
Return Value:
PWCHAR: returns the wszUnicode buffer
--*/
PWCHAR AnsiToUnicode(IN PCHAR cszAnsi,
OUT PWCHAR wszUnicode,
IN ULONG ulSize)
{
USHORT i;
USHORT usAnsiLength;
CHAR cszTemp[2000];
CHAR cszTemp2[2000];
//
// Clear out the new Unicode string
//
ZeroMemory(wszUnicode, ulSize);
//
// Record the length of the original Ansi string
//
usAnsiLength = strlen(cszAnsi);
//
// Copy the unicode string to a temporary buffer
//
strcpy(cszTemp, cszAnsi);
for (i = 0; i < usAnsiLength && i < ulSize - 1; i++)
{
//
// Copy the current character
//
wszUnicode[i] = (WCHAR)cszTemp[i];
}
wszUnicode[i] = '\0';
return wszUnicode;
} // AnsiToUnicode //
/*++
Routine Description: UnicodeToAnsi
Converts a Unicode string to an Ansi string.
Arguments:
wszUnicode: the Unicode string to convert
cszAnsi: Ansi buffer to store new string. Must not be NULL
ulSize: must be set to the size of the cszAnsi buffer
Return Value:
PCHAR: returns the cszAnsi buffer
--*/
PCHAR UnicodeToAnsi(IN PWCHAR wszUnicode,
OUT PCHAR cszAnsi,
IN ULONG ulSize)
{
USHORT i;
USHORT usUnicodeLength;
WCHAR wszTemp[2000];
//
// Record the length of the original Unicode string
//
usUnicodeLength = wcslen(wszUnicode);
//
// Copy the unicode string to a temporary buffer
//
wcscpy(wszTemp, wszUnicode);
for (i = 0; i < usUnicodeLength && i < ulSize - 1; i++)
{
//
// Copy the current character
//
cszAnsi[i] = (CHAR)wszTemp[0];
//
// Shift the unicode string up by one position
//
wcscpy(wszTemp, wszTemp + 1);
}
//
// Add a null terminator to the end of the new ansi string
//
cszAnsi[i] = '\0';
return cszAnsi;
} // UnicodeToAnsi //
/*++
Routine Description: ConvertAnsi
Receives an Ansi string. Returns the equivalent string in Ansi or
Unicode, depending on the current environment
Arguments:
cszAnsi: the Ansi string to convert
wszUnicode: unicode buffer to store new string (if needed).
Must not be NULL
ulSize: must be set to the size of the wszUnicode buffer
Return Value:
TCHAR: returns the ANSI or Unicode string
--*/
PTCHAR ConvertAnsi(IN OUT PCHAR cszAnsi,
IN OUT PWCHAR wszUnicode,
IN ULONG ulSize)
{
//
// If Unicode, we need to convert the string
//
#ifdef UNICODE
return AnsiToUnicode(cszAnsi, wszUnicode, ulSize);
//
// If not Unicode, just return the original Ansi string
//
#else
return cszAnsi;
#endif
} // ConvertAnsi //
/*++
Routine Description: ConvertUnicode
Receives a Unicode string. Returns the equivalent string in Ansi or
Unicode, depending on the current environment
Arguments:
wszUnicode: the Unicode string to convert
cszAnsi: ANSI buffer to store new string (if needed).
Must not be NULL
ulSize: must be set to the size of the cszAnsi buffer
Return Value:
TCHAR: returns the ANSI or Unicode string
--*/
PTCHAR ConvertUnicode(IN OUT PWCHAR wszUnicode,
IN OUT PCHAR cszAnsi,
IN ULONG ulSize)
{
//
// If Unicode, just return the original Unicode string
//
#ifdef UNICODE
return wszUnicode;
//
// If not Unicode, need to convert to Ansi
//
#else
return UnicodeToAnsi(wszUnicode, cszAnsi, ulSize);
#endif
} // ConvertUnicode //
/*++
Routine Description: ErrorMsg
Converts a numerical winerror into it's string value
Arguments:
ulError: the error number
tszBuffer: this buffer is used to return the string interpretation
must be declared of size MAX_ERROR_LEN
Return Value:
PTCHAR: returns the string value of the message stored in tszBuffer
--*/
PTCHAR ErrorMsg(IN ULONG ulError,
IN OUT PTCHAR tszBuffer)
{
USHORT i, usLen;
ULONG ulSuccess;
__ASSERT(tszBuffer != NULL);
ZeroMemory(tszBuffer, MAX_ERROR_LEN);
ulSuccess = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ulError,
0,
tszBuffer,
MAX_ERROR_LEN,
NULL);
if (!ulSuccess)
{
//
// Couldn't get a description of this error. Just return the number
//
_itot(ulError, tszBuffer, MAX_ERROR_LEN);
}
else
{
//
// Strip out returns from tszBuffer string
//
for (usLen = _tcslen(tszBuffer), i = 0; i < usLen; i++)
{
if (tszBuffer[i] == RETURN_CHAR1 || tszBuffer[i] == RETURN_CHAR2)
{
tszBuffer[i] = SPACE;
}
}
}
return tszBuffer;
} // ErrorMsg //