Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1113 lines
20 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
util.c - Generic Debugger Extension Utilities
Abstract:
Taken from AliD's ndiskd(ndiskd.c).
Revision History:
Who When What
-------- -------- ----------------------------------------------
josephj 03-30-98 Created (taken fron AliD's ndiskd (ndiskd.c).
Notes:
--*/
#include "common.h"
WINDBG_EXTENSION_APIS ExtensionApis;
EXT_API_VERSION ApiVersion = { 5, 0, EXT_API_VERSION_NUMBER, 0 };
#define ERRPRT dprintf
#define NL 1
#define NONL 0
USHORT SavedMajorVersion;
USHORT SavedMinorVersion;
BOOL ChkTarget; // is debuggee a CHK build?
/*
* Print out an optional message, an ANSI_STRING, and maybe a new-line
*/
BOOL
PrintStringA( IN LPSTR msg OPTIONAL, IN PANSI_STRING pStr, IN BOOL nl )
{
PCHAR StringData;
ULONG BytesRead;
if( msg )
dprintf( msg );
if( pStr->Length == 0 ) {
if( nl )
dprintf( "\n" );
return TRUE;
}
StringData = (PCHAR)LocalAlloc( LPTR, pStr->Length + 1 );
if( StringData == NULL ) {
ERRPRT( "Out of memory!\n" );
return FALSE;
}
ReadMemory((ULONG_PTR) pStr->Buffer,
StringData,
pStr->Length,
&BytesRead );
if ( BytesRead ) {
StringData[ pStr->Length ] = '\0';
dprintf("%s%s", StringData, nl ? "\n" : "" );
}
LocalFree((HLOCAL)StringData);
return BytesRead;
}
/*
* Get 'size' bytes from the debuggee program at 'dwAddress' and place it
* in our address space at 'ptr'. Use 'type' in an error printout if necessary
*/
BOOL
GetData( IN LPVOID ptr, IN UINT_PTR dwAddress, IN ULONG size, IN PCSTR type )
{
BOOL b;
ULONG BytesRead;
ULONG count = size;
while( size > 0 ) {
if (count >= 3000)
count = 3000;
b = ReadMemory(dwAddress, ptr, count, &BytesRead );
if (!b || BytesRead != count ) {
ERRPRT( "Unable to read %u bytes at %X, for %s\n", size, dwAddress, type );
return FALSE;
}
dwAddress += count;
size -= count;
ptr = (LPVOID)((ULONG_PTR)ptr + count);
}
return TRUE;
}
/*
* Print out a single HEX character
*/
VOID
PrintHexChar( IN UCHAR c )
{
dprintf( "%c%c", "0123456789abcdef"[ (c>>4)&0xf ], "0123456789abcdef"[ c&0xf ] );
}
/*
* Print out 'buf' of 'cbuf' bytes as HEX characters
*/
VOID
PrintHexBuf( IN PUCHAR buf, IN ULONG cbuf )
{
while( cbuf-- ) {
PrintHexChar( *buf++ );
dprintf( " " );
}
}
/*
* Fetch the null terminated UNICODE string at dwAddress into buf
*/
BOOL
GetString( IN UINT_PTR dwAddress, IN LPWSTR buf, IN ULONG MaxChars )
{
do {
if( !GetData( buf, dwAddress, sizeof( *buf ), "Character" ) )
return FALSE;
dwAddress += sizeof( *buf );
} while( --MaxChars && *buf++ != '\0' );
return TRUE;
}
char *mystrtok ( char *string, char * control )
{
static UCHAR *str;
char *p, *s;
if( string )
str = string;
if( str == NULL || *str == '\0' )
return NULL;
//
// Skip leading delimiters...
//
for( ; *str; str++ ) {
for( s=control; *s; s++ ) {
if( *str == *s )
break;
}
if( *s == '\0' )
break;
}
//
// Was it was all delimiters?
//
if( *str == '\0' ) {
str = NULL;
return NULL;
}
//
// We've got a string, terminate it at first delimeter
//
for( p = str+1; *p; p++ ) {
for( s = control; *s; s++ ) {
if( *p == *s ) {
s = str;
*p = '\0';
str = p+1;
return s;
}
}
}
//
// We've got a string that ends with the NULL
//
s = str;
str = NULL;
return s;
}
VOID
WinDbgExtensionDllInit(
PWINDBG_EXTENSION_APIS lpExtensionApis,
USHORT MajorVersion,
USHORT MinorVersion
)
{
ExtensionApis = *lpExtensionApis;
g_pfnDbgPrintf = dprintf;
SavedMajorVersion = MajorVersion;
SavedMinorVersion = MinorVersion;
ChkTarget = SavedMajorVersion == 0x0c ? TRUE : FALSE;
}
DECLARE_API( version )
{
#if DBG
PCSTR kind = "Checked";
#else
PCSTR kind = "Free";
#endif
dprintf(
"%s IPATM Extension dll for Build %d debugging %s kernel for Build %d\n",
kind,
VER_PRODUCTBUILD,
SavedMajorVersion == 0x0c ? "Checked" : "Free",
SavedMinorVersion
);
}
VOID
CheckVersion(
VOID
)
{
//
// for now don't bother to version check
//
return;
#if DBG
if ((SavedMajorVersion != 0x0c) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
dprintf("\r\n*** Extension DLL(%d Checked) does not match target system(%d %s)\r\n\r\n",
VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
}
#else
if ((SavedMajorVersion != 0x0f) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
dprintf("\r\n*** Extension DLL(%d Free) does not match target system(%d %s)\r\n\r\n",
VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
}
#endif
}
LPEXT_API_VERSION
ExtensionApiVersion(
VOID
)
{
return &ApiVersion;
}
//
// VOID
// PrintName(
// PUNICODE_STRING Name
// );
// print a unicode string
// Note: the Buffer field in unicode string is unmapped
//
VOID
PrintName(
PUNICODE_STRING Name
)
{
USHORT i;
WCHAR ubuf[256];
UCHAR abuf[256];
if (!GetString((UINT_PTR)Name->Buffer, ubuf, (ULONG)Name->Length))
{
return;
}
for (i = 0; i < Name->Length/2; i++)
{
abuf[i] = (UCHAR)ubuf[i];
}
abuf[i] = 0;
dprintf("%s",abuf);
}
MYPWINDBG_OUTPUT_ROUTINE g_pfnDbgPrintf = NULL;
bool
dbgextReadMemory(
UINT_PTR uOffset,
void * pvBuffer,
UINT cb,
char *pszDescription
)
{
UINT cbBytesRead=0;
bool fRet = ReadMemory(
uOffset,
pvBuffer,
cb,
&cbBytesRead
);
if (!fRet || cbBytesRead != cb)
{
ERRPRT("Read failed: 0x%X(%s, %u bytes)\n",uOffset,pszDescription,cb);
fRet = FALSE;
}
return fRet;
}
bool
dbgextWriteMemory(
UINT_PTR uOffset,
void * pvBuffer,
UINT cb,
char *pszDescription
)
{
UINT cbBytesWritten=0;
bool fRet = WriteMemory(
uOffset,
pvBuffer,
cb,
&cbBytesWritten
);
if (!fRet || cbBytesWritten != cb)
{
ERRPRT("Write failed: 0x%X(%s, %u bytes)\n",uOffset,pszDescription,cb);
fRet = FALSE;
}
return 0;
}
bool
dbgextReadUINT_PTR(
UINT_PTR uOffset,
UINT_PTR *pu,
char *pszDescription
)
{
UINT cbBytesRead=0;
bool fRet = ReadMemory(
uOffset,
pu,
sizeof(*pu),
&cbBytesRead
);
if (!fRet || cbBytesRead != sizeof(*pu))
{
ERRPRT("Read failed: 0x%X(%s, UINT_PTR)\n",uOffset,pszDescription);
fRet = FALSE;
}
return fRet;
}
bool
dbgextReadUINT(
UINT_PTR uOffset,
UINT *pu,
char *pszDescription
)
{
UINT cbBytesRead=0;
bool fRet = ReadMemory(
uOffset,
pu,
sizeof(*pu),
&cbBytesRead
);
if (!fRet || cbBytesRead != sizeof(*pu))
{
ERRPRT("Read failed: 0x%X(%s, UINT)\n",uOffset,pszDescription);
fRet = FALSE;
}
return fRet;
}
bool
dbgextReadSZ(
UINT_PTR uOffset,
char *szBuf,
UINT cbMax,
char *pszDescription
)
// Read a NULL-terminated string (upto cbMax)
//
{
UINT cbBytesRead=0;
bool fRet = ReadMemory(
uOffset,
szBuf,
cbMax,
&cbBytesRead
);
if (!fRet)
{
ERRPRT("Read failed: 0x%p(%s, SZ)\n",uOffset,pszDescription);
fRet = FALSE;
}
else
{
if (cbBytesRead)
{
szBuf[cbBytesRead-1] = 0;
}
else
{
*szBuf = 0;
}
}
return fRet;
}
bool
dbgextWriteUINT_PTR(
UINT_PTR uOffset,
UINT_PTR u,
char *pszDescription
)
{
UINT cbBytesWritten=0;
bool fRet = WriteMemory(
uOffset,
&u,
sizeof(uOffset),
&cbBytesWritten
);
if (!fRet || cbBytesWritten != sizeof(u))
{
ERRPRT("Write failed: 0x%X(%s, UINT_PTR)\n",uOffset,pszDescription);
fRet = FALSE;
}
return fRet;
}
UINT_PTR
dbgextGetExpression(
const char *pcszExpression
)
{
UINT_PTR uRet = GetExpression(pcszExpression);
//
// At such a point we use this for something besides pointers,
// we will remove the check below.
//
if (!uRet)
{
ERRPRT("Eval failed: \"%s\"\n", pcszExpression);
}
return uRet;
}
void
dbgextDumpDLlist(
UINT_PTR uOffset,
UINT uContainingOffset,
char *pszDescription
)
/*++
Print the pointers to the containing records of
all the items in the doubly-linked list.
--*/
{
bool fRet;
LIST_ENTRY Link;
LIST_ENTRY *pHead;
LIST_ENTRY *pFlink;
UINT uCount = 0;
UINT uMax = 16;
do
{
char *szPrefix;
char *szSuffix;
// Read the list header.
//
fRet = dbgextReadMemory(
uOffset,
&Link,
sizeof(Link),
pszDescription
);
if (!fRet) break;
pHead = (LIST_ENTRY *) uOffset;
pFlink = Link.Flink;
if (pFlink == pHead)
{
MyDbgPrintf(" <empty>\n");
break;
}
for(
;
(pFlink != pHead);
pFlink = Link.Flink, uCount++)
{
char *pContainingRecord = ((char *)pFlink) - uContainingOffset;
szPrefix = " ";
szSuffix = "";
if (uCount%4)
{
szPrefix = " ";
if ((uCount%4)==3)
{
szSuffix = "\n";
}
}
if (uCount >= uMax) break;
// Read the next link.
//
fRet = dbgextReadMemory(
(UINT_PTR) pFlink,
&Link,
sizeof(Link),
pszDescription
);
if (!fRet) break;
MyDbgPrintf("%s0x%p%s", szPrefix, pContainingRecord, szSuffix);
}
if (uCount%4)
{
MyDbgPrintf("\n");
}
if (pFlink != pHead && uCount >= uMax)
{
MyDbgPrintf(" ...\n");
}
} while (FALSE);
}
void
WalkDLlist(
UINT_PTR uOffsetHeadList,
UINT_PTR uOffsetStartLink, OPTIONAL
void *pvContext,
PFNNODEFUNC pFunc,
UINT MaxToWalk,
char *pszDescription
)
/*++
Print the pointers to the containing records of
all the items in the doubly-linked list.
--*/
{
bool fRet;
LIST_ENTRY Link;
LIST_ENTRY *pHead;
LIST_ENTRY *pFlink;
UINT uCount = 0;
UINT uMax = MaxToWalk;
do
{
// Read the list header.
//
fRet = dbgextReadMemory(
uOffsetHeadList,
&Link,
sizeof(Link),
pszDescription
);
if (!fRet) break;
pHead = (LIST_ENTRY *) uOffsetHeadList;
if (uOffsetStartLink == 0)
{
pFlink = Link.Flink;
}
else
{
pFlink = (LIST_ENTRY*) uOffsetStartLink;
}
if (pFlink == pHead)
{
// MyDbgPrintf(" <end-of-list>\n");
break;
}
for(
;
(pFlink != pHead);
pFlink = Link.Flink, uCount++)
{
if (uCount >= uMax) break;
// Read the next link.
//
fRet = dbgextReadMemory(
(UINT_PTR) pFlink,
&Link,
sizeof(Link),
pszDescription
);
if (!fRet) break;
// Call the nodefunc..
//
pFunc((UINT_PTR)pFlink, 0 /*uIndex*/, pvContext);
}
} while (FALSE);
}
void
DumpObjects(TYPE_INFO *pType, UINT_PTR uAddr, UINT cObjects, UINT uFlags)
{
//
// Print object's type and size
//
dprintf(
"%s@0x%X (%lu Bytes)\n",
pType->szName,
uAddr,
pType->cbSize
);
DumpMemory(
uAddr,
pType->cbSize,
0,
pType->szName
);
//
// Dump bytes...
//
return;
}
BYTE rgbScratchBuffer[100000];
bool
DumpMemory(
UINT_PTR uAddr,
UINT cb,
UINT uFlags,
const char *pszDescription
)
{
bool fTruncated = FALSE;
bool fRet = FALSE;
UINT cbLeft = cb;
char *pbSrc = rgbScratchBuffer;
if (cbLeft>1024)
{
cbLeft = 1024;
fTruncated = TRUE;
}
fRet = dbgextReadMemory(
uAddr,
rgbScratchBuffer,
cbLeft,
(char*)pszDescription
);
if (!fRet) goto end;
#define ROWSIZE 16 // bytes
//
// Dump away...
//
while (cbLeft)
{
char rgTmp_dwords[ROWSIZE];
char rgTmp_bytes[ROWSIZE];
char *pb=NULL;
UINT cbRow = ROWSIZE;
if (cbRow > cbLeft)
{
cbRow = cbLeft;
}
memset(rgTmp_dwords, 0xff, sizeof(rgTmp_dwords));
memset(rgTmp_bytes, ' ', sizeof(rgTmp_bytes));
memcpy(rgTmp_dwords, pbSrc, cbRow);
memcpy(rgTmp_bytes, pbSrc, cbRow);
// sanitize bytes
for (pb=rgTmp_bytes; pb<(rgTmp_bytes+sizeof(rgTmp_bytes)); pb++)
{
char c = *pb;
if (c>=0x20 && c<0x7f) // isprint is too permissive.
{
if (*pb=='\t')
{
*pb=' ';
}
}
else
{
*pb='.';
}
}
dprintf(
" %08lx: %08lx %08lx %08lx %08lx |%4.4s|%4.4s|%4.4s|%4.4s|\n",
uAddr,
((DWORD*) rgTmp_dwords)[0],
((DWORD*) rgTmp_dwords)[1],
((DWORD*) rgTmp_dwords)[2],
((DWORD*) rgTmp_dwords)[3],
#if 1
rgTmp_bytes+0,
rgTmp_bytes+4,
rgTmp_bytes+8,
rgTmp_bytes+12
#else
"aaaabbbbccccdddd",
"bbbb",
"cccc",
"dddd"
#endif
);
cbLeft -= cbRow;
pbSrc += cbRow;
uAddr += cbRow;
}
#if 0
0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
#endif //
end:
return fRet;
}
bool
MatchPrefix(const char *szPattern, const char *szString)
{
BOOL fRet = FALSE;
ULONG uP = lstrlenA(szPattern);
ULONG uS = lstrlenA(szString);
if (uP<=uS)
{
fRet = (_memicmp(szPattern, szString, uP)==0);
}
return fRet;
}
bool
MatchSuffix(const char *szPattern, const char *szString)
{
BOOL fRet = FALSE;
ULONG uP = lstrlenA(szPattern);
ULONG uS = lstrlenA(szString);
if (uP<=uS)
{
szString += (uS-uP);
fRet = (_memicmp(szPattern, szString, uP)==0);
}
return fRet;
}
bool
MatchSubstring(const char *szPattern, const char *szString)
{
BOOL fRet = FALSE;
ULONG uP = lstrlenA(szPattern);
ULONG uS = lstrlenA(szString);
if (uP<=uS)
{
const char *szLast = szString + (uS-uP);
do
{
fRet = (_memicmp(szPattern, szString, uP)==0);
} while (!fRet && szString++ < szLast);
}
return fRet;
}
bool
MatchExactly(const char *szPattern, const char *szString)
{
BOOL fRet = FALSE;
ULONG uP = lstrlenA(szPattern);
ULONG uS = lstrlenA(szString);
if (uP==uS)
{
fRet = (_memicmp(szPattern, szString, uP)==0);
}
return fRet;
}
bool
MatchAlways(const char *szPattern, const char *szString)
{
return TRUE;
}
void
DumpBitFields(
ULONG Flags,
BITFIELD_INFO rgBitFieldInfo[]
)
{
BITFIELD_INFO *pbf = rgBitFieldInfo;
for(;pbf->szName; pbf++)
{
if ((Flags & pbf->Mask) == pbf->Value)
{
MyDbgPrintf(" %s", pbf->szName);
}
}
}
void
DumpStructure(
TYPE_INFO *pType,
UINT_PTR uAddr,
char *szFieldSpec,
UINT uFlags
)
{
//
// Determine field comparision function ...
//
PFNMATCHINGFUNCTION pfnMatchingFunction = MatchAlways;
if (pType->pfnSpecializedDump)
{
// Call the specialized function to handle this...
//
pType->pfnSpecializedDump(
pType,
uAddr,
szFieldSpec,
uFlags
);
return;
}
//
// Pick a selection function ...
//
if (szFieldSpec)
{
if (uFlags & fMATCH_SUBSTRING)
{
pfnMatchingFunction = MatchSubstring;
}
else if (uFlags & fMATCH_SUFFIX)
{
pfnMatchingFunction = MatchSuffix;
}
else if (uFlags & fMATCH_PREFIX)
{
pfnMatchingFunction = MatchPrefix;
}
else
{
pfnMatchingFunction = MatchExactly;
}
}
//
// Print object's type and size
//
dprintf(
"%s@0x%X (%lu Bytes)\n",
pType->szName,
uAddr,
pType->cbSize
);
//
// Run through all the fields in this type, and if the entry is selected,
// we will display it.
//
{
STRUCT_FIELD_INFO *pField = pType->rgFields;
for (;pField->szFieldName; pField++)
{
bool fMatch = !szFieldSpec
|| pfnMatchingFunction(szFieldSpec, pField->szFieldName);
if (fMatch)
{
UINT_PTR uFieldAddr = uAddr + pField->uFieldOffset;
// special-case small fields...
if (pField->uFieldSize<=sizeof(ULONG_PTR))
{
ULONG_PTR Buf=0;
BOOL fRet = dbgextReadMemory(
uFieldAddr,
&Buf,
pField->uFieldSize,
(char*)pField->szFieldName
);
if (fRet)
{
// print it as a hex number
MyDbgPrintf(
"\n%s\t[%lx,%lx]: 0x%lx",
pField->szFieldName,
pField->uFieldOffset,
pField->uFieldSize,
Buf
);
//
// If it's an embedded object and it's a bitfield,
// print the bitfields...
//
if ( FIELD_IS_EMBEDDED_TYPE(pField)
&& TYPEISBITFIELD(pField->pBaseType) )
{
DumpBitFields(
(ULONG)Buf,
pField->pBaseType->rgBitFieldInfo
);
}
MyDbgPrintf("\n");
}
continue;
}
#if 0
MyDbgPrintf(
"%s\ndc 0x%08lx L %03lx %s\n",
pField->szSourceText,
uFieldAddr,
pField->uFieldSize,
pField->szFieldName
);
#else // 1
MyDbgPrintf(
"\n%s\t[%lx,%lx]\n",
pField->szFieldName,
pField->uFieldOffset,
pField->uFieldSize
);
#endif // 1
// if (szFieldSpec)
{
#if 0
MyDumpObjects(
pCmd,
pgi->pBaseType,
pgi->uAddr,
pgi->cbSize,
pgi->szName
);
#endif // 0
DumpMemory(
uFieldAddr,
pField->uFieldSize,
0,
pField->szFieldName
);
}
}
}
}
return;
}
DECLARE_API( help )
{
do_help(args);
}
DECLARE_API( rm )
{
do_rm(args);
}
DECLARE_API( arp )
{
do_arp(args);
}
ULONG
NodeFunc_DumpAddress (
UINT_PTR uNodeAddr,
UINT uIndex,
void *pvContext
)
{
MyDbgPrintf("[%lu] 0x%08lx\n", uIndex, uNodeAddr);
return 0;
}
UINT
WalkList(
UINT_PTR uStartAddress,
UINT uNextOffset,
UINT uStartIndex,
UINT uEndIndex,
void *pvContext,
PFNNODEFUNC pFunc,
char *pszDescription
)
//
// Visit each node in the list in turn,
// reading just the next pointers. It calls pFunc for each list node
// between uStartIndex and uEndIndex. It terminates under the first of
// the following conditions:
// * Null pointer
// * ReadMemoryError
// * Read past uEndIndex
// * pFunc returns FALSE
//
{
UINT uIndex = 0;
UINT_PTR uAddress = uStartAddress;
BOOL fRet = TRUE;
UINT uRet = 0;
//
// First skip until we get to uStart Index
//
for (;fRet && uAddress && uIndex < uStartIndex; uIndex++)
{
fRet = dbgextReadUINT_PTR(
uAddress+uNextOffset,
&uAddress,
pszDescription
);
}
//
// Now call pFunc with each node
//
for (;fRet && uAddress && uIndex <= uEndIndex; uIndex++)
{
uRet = pFunc(uAddress, uIndex, pvContext);
fRet = dbgextReadUINT_PTR(
uAddress+uNextOffset,
&uAddress,
pszDescription
);
}
pFunc = NodeFunc_DumpAddress;
return uRet;
}