/*++ 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 "precomp.h" #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) 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 DWORD dwAddress, IN ULONG size, IN PCSTR type ) { BOOL b; ULONG BytesRead; ULONG count = size; while( size > 0 ) { if (count >= 3000) count = 3000; b = ReadMemory((ULONG) 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 + count); } return TRUE; } /* * Follow a LIST_ENTRY list beginning with a head at dwListHeadAddr in the debugee's * address space. For each element in the list, print out the pointer value at 'offset' */ BOOL PrintListEntryList( IN DWORD dwListHeadAddr, IN LONG offset ) { LIST_ENTRY ListEntry; ULONG i=0; BOOL retval = TRUE; ULONG count = 20; if( !GetData( &ListEntry, dwListHeadAddr, sizeof( ListEntry ), "LIST_ENTRY" ) ) return FALSE; while( count-- ) { if( (DWORD)ListEntry.Flink == dwListHeadAddr || (DWORD)ListEntry.Flink == 0 ) break; if( !GetData( &ListEntry, (DWORD)ListEntry.Flink, sizeof( ListEntry ), "ListEntry" ) ) { retval = FALSE; break; } dprintf( "%16X%s", (LONG)ListEntry.Flink + offset, (i && !(i&3)) ? "\n" : "" ); i++; } if( count == 0 && (DWORD)ListEntry.Flink != dwListHeadAddr && ListEntry.Flink ) { dprintf( "\nTruncated list dump\n" ); } else if( ! ( i && !(i&3) ) ) { dprintf( "\n" ); } return retval; } /* * 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 DWORD 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((DWORD)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 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 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; // // 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( 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( aac ) { do_aac(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; }