/*++ 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(" \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(" \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; }