/*++ Copyright (c) 1998-2000 Microsoft Corporation Module Name: dbglkrh.cxx Abstract: LKRhash support Author: George V. Reilly (GeorgeRe) 22-Feb-1999 Revision History: --*/ #include "precomp.hxx" #include "lkrcust.h" #define LKR_CUST_DECLARE_TABLE #include "lkrcust.h" #include #ifdef LOCK_INSTRUMENTATION LONG CLKRLinearHashTable::CBucket::sm_cBuckets = 0; LONG CLKRLinearHashTable::sm_cTables = 0; #endif // LOCK_INSTRUMENTATION #define CMDNAME "lkrhash" // There are several different DLLs in the IISRTL family. This is to // allow us to set the name of the DLL on the fly. // TODO: add a command to set this name dynamically. #ifndef LKRHASH_NAME # define LKRHASH_NAME "LKRhash" #endif // LKRHASH_NAME CHAR g_szLKRhashDllName[MAX_PATH] = LKRHASH_NAME; // sprintf-formatted string, e.g., "&%s!CLKRHashTable__sm_llGlobalList" // Has to return LPSTR, not LPCSTR, because GetExpression is not const-correct LPSTR LKRhashDllVar( LPCSTR pszFormat) { // we can get away with a static CHAR[] because debugger extensions // are single-threaded static CHAR szSymbol[MAX_SYMBOL_LEN]; sprintf(szSymbol, pszFormat, g_szLKRhashDllName); return szSymbol; } #ifndef __LKRHASH_NO_NAMESPACE__ #define STR_LKRHASH_NS "LKRhash__" #else #define STR_LKRHASH_NS "" #endif // !__LKRHASH_NO_NAMESPACE__ /************************************************************ * Dump LKRhash tables ************************************************************/ const char* LKRC2String( LK_RETCODE lkrc) { const char* psz = NULL; switch (lkrc) { case LK_UNUSABLE: psz = "LK_UNUSABLE"; break; case LK_ALLOC_FAIL: psz = "LK_ALLOC_FAIL"; break; case LK_BAD_ITERATOR: psz = "LK_BAD_ITERATOR"; break; case LK_BAD_RECORD: psz = "LK_BAD_RECORD"; break; case LK_BAD_PARAMETERS: psz = "LK_BAD_PARAMETERS"; break; case LK_NOT_INITIALIZED: psz = "LK_NOT_INITIALIZED"; break; case LK_BAD_TABLE: psz = "LK_BAD_TABLE"; break; case LK_SUCCESS: psz = "LK_SUCCESS"; break; case LK_KEY_EXISTS: psz = "LK_KEY_EXISTS"; break; case LK_NO_SUCH_KEY: psz = "LK_NO_SUCH_KEY"; break; case LK_NO_MORE_ELEMENTS: psz = "LK_NO_MORE_ELEMENTS"; break; default: psz = "Unknown LK_RETCODE"; break; } return psz; } enum { NODES_PER_CLUMP = CNodeClump::NODES_PER_CLUMP, BUCKET_BYTE_SIZE = CNodeClump::BUCKET_BYTE_SIZE, HASH_INVALID_SIGNATURE = LKRHASH_NS::CLKRLinearHashTable::HASH_INVALID_SIGNATURE, LKLH_SIGNATURE = LKRHASH_NS::CLKRLinearHashTable::SIGNATURE, LKLH_SIGNATURE_FREE = LKRHASH_NS::CLKRLinearHashTable::SIGNATURE_FREE, LKHT_SIGNATURE = LKRHASH_NS::CLKRHashTable::SIGNATURE, LKHT_SIGNATURE_FREE = LKRHASH_NS::CLKRHashTable::SIGNATURE_FREE, }; BOOL EnumerateBucketChain( LKR_CUST_EXTN* plce, IN LOCK_LOCKTYPE ltBucketLockType, IN INT iBkt, IN CBucket* pbkt, IN INT nVerbose) { PSTR cmdName = CMDNAME; BYTE abBkt[BUCKET_BYTE_SIZE]; DEFINE_CPP_VAR(CNodeClump, nc); CNodeClump* pnc = GET_CPP_VAR_PTR(CNodeClump, nc); CNodeClump* pncCurr; DWORD cNodes = 0, cnc = 0; BOOL fLockPrinted = FALSE; ReadMemory(pbkt, abBkt, sizeof(abBkt), NULL); // Just in case lkrdbg.dll and lkrhash.dll were built with different // definitions for BucketLock, we make no assumptions about the size // of BucketLock for (pncCurr = (CNodeClump*) ((PBYTE) pbkt + LockSize(ltBucketLockType)); pncCurr != NULL; pncCurr = pnc->m_pncNext) { DWORD i, c; ReadMemory(pncCurr, &nc, sizeof(nc), NULL); for (i = c = 0; i < NODES_PER_CLUMP; i++) { if (pnc->m_dwKeySigs[i] == HASH_INVALID_SIGNATURE) c++; } if (c == NODES_PER_CLUMP) { if (nVerbose >= 3) dprintf(" 0-%d: -- empty\n", NODES_PER_CLUMP); } else { if (cnc++ == 0) PrintLock(ltBucketLockType, &pbkt->m_Lock, nVerbose); dprintf("Bucket %4d, %d:\n", iBkt, cnc); for (i = 0; i < NODES_PER_CLUMP; i++) { if (pnc->m_dwKeySigs[i] == HASH_INVALID_SIGNATURE) { if (nVerbose >= 3) dprintf(" %d: --\n", i); else break; } else if (plce != NULL) { if (!(*plce->m_pfn_Record_Dump)(pnc->m_pvNode[i], pnc->m_dwKeySigs[i], nVerbose)) return FALSE; } } } if (CheckControlC()) { dprintf("\n^C\n"); return FALSE; } const DWORD MAX_NODES = 20; if (++cNodes > MAX_NODES) { dprintf(DBGEXT ".%s: Bucket chain contains more than %d nodes! " "Corrupted?\n", cmdName, MAX_NODES); return TRUE; } } return TRUE; } BOOL EnumerateLKRLinearHashTable( LKR_CUST_EXTN* plce, IN CLinearHashTable* plht, IN INT nVerbose) { PSTR cmdName = CMDNAME; DEFINE_CPP_VAR(CLinearHashTable, lht); CLinearHashTable* plht2 = GET_CPP_VAR_PTR(CLinearHashTable, lht); INT i; BOOL fRet = FALSE; LOCK_LOCKTYPE ltTableLockType = LOCK_SPINLOCK; LOCK_LOCKTYPE ltBucketLockType = LOCK_SPINLOCK; CDirEntry* paDirSegs = NULL; // // Read the header, perform some sanity checks. // if (!ReadMemory(plht, &lht, sizeof(lht), NULL) ) { dprintf(DBGEXT ".%s: cannot read memory @ %p\n", cmdName, (PVOID)plht); goto cleanup; } dprintf( "\n" DBGEXT ".%s: @ %p:\n" " CLKRLinearHashTable Signature = %08lx '%c%c%c%c' (%s), \"%s\",\n" " State = %d (%s)\n", cmdName, plht, plht2->m_dwSignature, DECODE_SIGNATURE(plht2->m_dwSignature), plht2->m_dwSignature == LKLH_SIGNATURE ? "OK" : (plht2->m_dwSignature == LKLH_SIGNATURE_FREE ? "FREED" : "INVALID"), plht2->m_szName, plht2->m_lkrcState, LKRC2String(plht2->m_lkrcState)); if (nVerbose == 0) goto done; ltTableLockType = (LOCK_LOCKTYPE) plht2->m_nTableLockType; ltBucketLockType = (LOCK_LOCKTYPE) plht2->m_nBucketLockType; dprintf( " TableLock = %s, BucketLock = %s, Parent CLKRHashTable = %p\n", LockName(ltTableLockType), LockName(ltBucketLockType), plht2->m_phtParent); dprintf( " Size = %d, SegBits = %d, SegSize = %d, SegMask = %x\n", plht2->m_lkts, plht2->m_dwSegBits, plht2->m_dwSegSize, plht2->m_dwSegMask); dprintf( " MaxLoad = %3.1f, paDirSegs = %p, cDirSegs = %d\n", plht2->m_MaxLoad, plht2->m_paDirSegs, plht2->m_cDirSegs); dprintf( " cRecords = %d, cActiveBuckets = %d, BucketSpins = %hd\n", plht2->m_cRecords, plht2->m_cActiveBuckets, plht2->m_wBucketLockSpins); dprintf( " nLevel = %d, dwBktAddrMask0 = %x, iExpansionIdx = %d\n", plht2->m_nLevel, plht2->m_dwBktAddrMask0, plht2->m_iExpansionIdx); PrintLock(ltTableLockType, &plht->m_Lock, nVerbose); if (plce != NULL && !(*plce->m_pfn_LKLH_Dump)(plht, nVerbose)) goto done; if (nVerbose == 1) goto done; paDirSegs = (CDirEntry*) calloc(plht2->m_cDirSegs, sizeof(CDirEntry)); if (paDirSegs == NULL) { dprintf("Couldn't allocate %d bytes for directory segment\n", plht2->m_cDirSegs * sizeof(CDirEntry)); return fRet; } if (!ReadMemory(plht2->m_paDirSegs, paDirSegs, sizeof(CDirEntry) * plht2->m_cDirSegs, NULL)) goto cleanup; for (i = 0; i < (INT) (plht2->m_cDirSegs * plht2->m_dwSegSize); i++) { const DWORD iSeg = i >> plht2->m_dwSegBits; CLargeSegment* pseg = static_cast(paDirSegs[iSeg].m_pseg); if ((i & plht2->m_dwSegMask) == 0) dprintf("Segment %d: %p\n", iSeg, pseg); if (pseg == NULL) continue; if (nVerbose >= 2) { CBucket* pbkt = pseg->m_bktSlots + (i & plht2->m_dwSegMask); if (!EnumerateBucketChain(plce, ltBucketLockType, i, pbkt, nVerbose)) goto cleanup; } if (CheckControlC()) { dprintf("\n^C\n"); goto cleanup; } } done: fRet = TRUE; cleanup: memset(&lht, 0, sizeof(lht)); free(paDirSegs); return fRet; } BOOL EnumerateLKRhashTable( LKR_CUST_EXTN* plce, IN CHashTable* pht, IN INT nVerbose) { DEFINE_CPP_VAR(CHashTable, ht); CHashTable* pht2 = GET_CPP_VAR_PTR(CHashTable, ht); PSTR cmdName = CMDNAME; CLinearHashTable** palhtDir = NULL; UINT i; BOOL fRet = FALSE; // // Read the header, perform some sanity checks. // if (!ReadMemory(pht, &ht, sizeof(ht), NULL) ) { dprintf(DBGEXT ".%s: cannot read memory @ %p\n", cmdName, (PVOID)pht); goto cleanup; } dprintf( DBGEXT ".%s: @ %p:\n" " CLKRHashTable Signature = %08lx '%c%c%c%c' (%s), \"%s\",\n" " %d subtables, State = %d (%s)\n", cmdName, pht, pht2->m_dwSignature, DECODE_SIGNATURE(pht2->m_dwSignature), pht2->m_dwSignature == LKHT_SIGNATURE ? "OK" : pht2->m_dwSignature == LKHT_SIGNATURE_FREE ? "FREED" : "INVALID", pht2->m_szName, pht2->m_cSubTables, pht2->m_lkrcState, LKRC2String(pht2->m_lkrcState) ); if (plce != NULL && !(*plce->m_pfn_LKHT_Dump)(pht, nVerbose)) goto done; if (nVerbose == 0) goto done; palhtDir = (CLinearHashTable**) calloc(pht2->m_cSubTables, sizeof(CLinearHashTable*)); if (!palhtDir) goto cleanup; if (!ReadMemory(pht2->m_palhtDir, palhtDir, pht2->m_cSubTables * sizeof(CLinearHashTable*), NULL)) goto cleanup; for (i = 0; i < pht2->m_cSubTables; ++i) { dprintf("\n%d : ", i); if (!EnumerateLKRLinearHashTable(plce, palhtDir[i], nVerbose)) break; if (CheckControlC()) { dprintf("\n^C\n"); goto cleanup; } } done: fRet = TRUE; cleanup: free(palhtDir); return fRet; } VOID PrintLKRLinearHashTableThunk( PVOID psdDebuggee, PVOID psdDebugger, CHAR chVerbosity, DWORD iThunk) { DWORD dwSig = ((CLinearHashTable*) psdDebugger)->m_dwSignature; if (dwSig != LKLH_SIGNATURE) { dprintf( "CLKRLinearHashTable(%08p) signature %08lx '%c%c%c%c' doesn't" " match expected: %08lx '%c%c%c%c'\n", psdDebuggee, dwSig, DECODE_SIGNATURE(dwSig), LKLH_SIGNATURE, DECODE_SIGNATURE(LKLH_SIGNATURE) ); return; } LKR_CUST_EXTN* plce = FindLkrCustExtn(CMDNAME, psdDebuggee, dwSig); if (plce != NULL) EnumerateLKRLinearHashTable(plce, (CLinearHashTable*) psdDebuggee, chVerbosity); } VOID PrintLKRHashTableThunk( PVOID psdDebuggee, PVOID psdDebugger, CHAR chVerbosity, DWORD iThunk) { DWORD dwSig = ((CHashTable*) psdDebugger)->m_dwSignature; if (dwSig != LKHT_SIGNATURE) { dprintf( "CLKRHashTable(%08p) signature %08lx '%c%c%c%c' doesn't" " match expected: %08lx '%c%c%c%c'\n", psdDebuggee, dwSig, DECODE_SIGNATURE(dwSig), LKHT_SIGNATURE, DECODE_SIGNATURE(LKHT_SIGNATURE) ); return; } LKR_CUST_EXTN* plce = FindLkrCustExtn(CMDNAME, psdDebuggee, dwSig); if (plce != NULL) EnumerateLKRhashTable(plce, (CHashTable*) psdDebuggee, chVerbosity); } VOID DumpLKRsList( IN INT nVerbose) { #ifndef LKR_NO_GLOBAL_LIST CLockedDoubleList* plstHashTables = (CLockedDoubleList*) GetExpression( LKRhashDllVar("&%s!" STR_LKRHASH_NS "CLKRHashTable__sm_llGlobalList")); if (NULL == plstHashTables) { dprintf("Unable to get %s\n", LKRhashDllVar("%s!" STR_LKRHASH_NS "CLKRHashTable__sm_llGlobalList")); return; } dprintf("\nGlobal List of CLKRHashTables\n"); EnumLinkedList( (LIST_ENTRY*) &plstHashTables->m_list.m_leHead, PrintLKRHashTableThunk, (CHAR) nVerbose, sizeof(CHashTable), FIELD_OFFSET( CHashTable, m_leGlobalList)); plstHashTables = (CLockedDoubleList*) GetExpression( LKRhashDllVar("&%s!" STR_LKRHASH_NS "CLKRLinearHashTable__sm_llGlobalList")); if (NULL == plstHashTables) { dprintf("Unable to get %s\n", LKRhashDllVar("!" STR_LKRHASH_NS "CLKRLinearHashTable__sm_llGlobalList")); return; } dprintf("\nGlobal List of CLKRLinearHashTables\n"); EnumLinkedList( (LIST_ENTRY*) &plstHashTables->m_list.m_leHead, PrintLKRLinearHashTableThunk, (CHAR) nVerbose, sizeof(CLinearHashTable), FIELD_OFFSET( CLinearHashTable, m_leGlobalList)); #endif // !LKR_NO_GLOBAL_LIST return; } // DumpLKRsList() DECLARE_API( lkrhash ) /*++ Routine Description: This function is called as an NTSD extension to format and dump an LKRhash table. Arguments: hCurrentProcess - Supplies a handle to the current process (at the time the extension was called). hCurrentThread - Supplies a handle to the current thread (at the time the extension was called). CurrentPc - Supplies the current pc at the time the extension is called. lpExtensionApis - Supplies the address of the functions callable by this extension. lpArgumentString - Supplies the asciiz string that describes the ansi string to be dumped. Return Value: None. --*/ { INIT_API(); ULONG_PTR lkrAddress = 0; INT nVerbose = 0; PSTR cmdName = CMDNAME; // // Skip leading blanks. // while( *lpArgumentString == ' ' || *lpArgumentString == '\t' ) { lpArgumentString++; } if( *lpArgumentString == '\0' ) { PrintUsage( cmdName ); return; } if ( *lpArgumentString == '-' ) { lpArgumentString++; if ( *lpArgumentString == 'h' ) { PrintUsage( cmdName ); return; } if ( *lpArgumentString == 'l' ) { lpArgumentString++; if ('0' <= *lpArgumentString && *lpArgumentString <= '9' ) { nVerbose = *lpArgumentString++ - '0'; } } if ( *lpArgumentString == 'v' ) { lpArgumentString++; nVerbose = 99; } if ( *lpArgumentString == 'g' ) { lpArgumentString++; if ('0' <= *lpArgumentString && *lpArgumentString <= '9' ) { nVerbose = *lpArgumentString++ - '0'; } DumpLKRsList(nVerbose); return; } } while( *lpArgumentString == ' ' || *lpArgumentString == '\t' ) { lpArgumentString++; } lkrAddress = (ULONG_PTR) GetExpression( lpArgumentString ); if (lkrAddress == 0) { dprintf( DBGEXT ".%s: cannot evaluate \"%s\"\n", cmdName, lpArgumentString ); return; } // // Skip to end of expression, then skip any blanks. // while( *lpArgumentString != ' ' && *lpArgumentString != '\t' && *lpArgumentString != '\0' ) { lpArgumentString++; } DWORD dwSig; LKR_CUST_EXTN* plce = FindLkrCustExtn(CMDNAME, (VOID*) lkrAddress, dwSig); if (plce == NULL) goto cleanup; if (dwSig == LKHT_SIGNATURE || dwSig == LKHT_SIGNATURE_FREE) { EnumerateLKRhashTable(plce, (CHashTable*) lkrAddress, nVerbose); } else if (dwSig == LKLH_SIGNATURE || dwSig == LKLH_SIGNATURE_FREE) { EnumerateLKRLinearHashTable(plce, (CLinearHashTable*) lkrAddress, nVerbose); } else { dprintf(DBGEXT ".%s: %p does not contain a valid LKRhash table\n", cmdName, (PVOID)lkrAddress); } cleanup: return; } // DECLARE_API( lkrhash )