|
|
/*++
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 <i-lkrhash.h>
#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 PBucket pbkt, IN INT nVerbose) { PSTR cmdName = CMDNAME; BYTE abBkt[BUCKET_BYTE_SIZE]; DEFINE_CPP_VAR(CNodeClump, nc); PNodeClump pnc = GET_CPP_VAR_PTR(CNodeClump, nc); PNodeClump 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 = (PNodeClump) ((PBYTE) pbkt + LockSize(ltBucketLockType)); pncCurr != NULL; pncCurr = pnc->m_pncNext) { DWORD i, c;
ReadMemory(pncCurr, pnc, 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 { #ifdef LKR_USE_BUCKET_LOCKS
if (cnc++ == 0) PrintLock(ltBucketLockType, &pbkt->m_Lock, nVerbose); #endif
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; PSegment* paDirSegs = NULL;
//
// Read the header, perform some sanity checks.
//
if (!ReadMemory(plht, plht2, 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_nSegBits, plht2->m_nSegSize, plht2->m_nSegMask); 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 = (PSegment*) calloc(plht2->m_cDirSegs, sizeof(PSegment));
if (paDirSegs == NULL) { dprintf("Couldn't allocate %d bytes for directory segment\n", plht2->m_cDirSegs * sizeof(PSegment)); return fRet; } if (!ReadMemory(plht2->m_paDirSegs, paDirSegs, sizeof(PSegment) * plht2->m_cDirSegs, NULL)) goto cleanup;
for (i = 0; i < (INT) (plht2->m_cDirSegs * plht2->m_nSegSize); i++) { const DWORD iSeg = i >> plht2->m_nSegBits; CLargeSegment* pseg = static_cast<CLargeSegment*>(paDirSegs[iSeg]);
if ((i & plht2->m_nSegMask) == 0) dprintf("Segment %d: %p\n", iSeg, pseg);
if (pseg == NULL) continue;
if (nVerbose >= 2) { PBucket pbkt = pseg->m_bktSlots + (i & plht2->m_nSegMask); if (!EnumerateBucketChain(plce, ltBucketLockType, i, pbkt, nVerbose)) goto cleanup; }
if (CheckControlC()) { dprintf("\n^C\n"); goto cleanup; } }
done: fRet = TRUE;
cleanup: memset(plht2, 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, pht2, 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 )
|