|
|
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
Cc.c
Abstract:
WinDbg Extension Api for examining cache manager data structures
Author:
Keith Kaplan [KeithKa] 17-Apr-97
Environment:
User Mode.
Revision History:
--*/
#include "precomp.h"
#undef CREATE_NEW
#undef OPEN_EXISTING
#undef FlagOn
#undef WordAlign
#undef LongAlign
#undef QuadAlign
#pragma hdrstop
//
// DUMP_WITH_OFFSET -- for dumping values contained in structures.
//
#define DUMP_WITH_OFFSET(type, ptr, element, label) \
dprintf( "\n(%03x) %08x %s ", \ FIELD_OFFSET(type, element), \ ptr.element, \ label )
//
// DUMP_LL_W_OFFSET -- for dumping longlongs contained in structures.
//
#define DUMP_LL_W_OFFSET(type, ptr, element, label) \
dprintf( "\n(%03x) %I64x %s ", \ FIELD_OFFSET(type, element), \ ptr.element, \ label )
//
// DUMP_EMBW_OFFSET -- for dumping addresses of values embedded in structures.
//
#define DUMP_EMBW_OFFSET(type, ptr, element, label) \
dprintf( "\n(%03x) %08x %s ", \ FIELD_OFFSET(type, element), \ ptr + FIELD_OFFSET(type, element), \ label )
#define RM( Addr, Obj, pObj, Type, Result, label ) \
(pObj) = (Type) (Addr); \ if ( !ReadMemory( (DWORD) (pObj), &(Obj), sizeof( Obj ), &(Result)) ) { \ if (label) { \ dprintf( "Unable to read memory at %p (%s)\n", (pObj), (label) );\ } else { \ dprintf( "Unable to read memory at %p\n", (pObj) ); \ } \ return; \ }
//
// The help strings printed out
//
static LPSTR Extensions[] = { "Cache Manager Debugger Extensions:\n", "bcb [addr] Dump Buffer Control Block", "scm [addr] Dump Shared Cache Map", "finddata [FileObject Offset] Find cached data at given offset in file object", "defwrites Dump deferred write queue", 0 };
typedef PVOID (*STRUCT_DUMP_ROUTINE)( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread );
VOID DumpBcb ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread );
VOID DumpPcm ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread );
VOID DumpScm ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread );
typedef struct { CSHORT TypeCode; char *Text; } MY_NODE_TYPE;
static MY_NODE_TYPE NodeTypeCodes[] = { { CACHE_NTC_SHARED_CACHE_MAP, "Shared Cache Map" }, { CACHE_NTC_PRIVATE_CACHE_MAP, "Private Cache Map" }, { CACHE_NTC_BCB, "Bcb" }, { CACHE_NTC_DEFERRED_WRITE, "Deferred Write" }, { CACHE_NTC_MBCB, "Mbcb" }, { CACHE_NTC_OBCB, "Obcb" }, { CACHE_NTC_MBCB_GRANDE, "Mcb Grande" }, { 0, "Unknown" } };
static const char * TypeCodeGuess ( IN CSHORT TypeCode )
/*++
Routine Description:
Guess at a structure's type code
Arguments:
TypeCode - Type code from the data structure
Return Value:
None
--*/
{ int i = 0;
while (NodeTypeCodes[i].TypeCode != 0 && NodeTypeCodes[i].TypeCode != TypeCode) {
i++;
}
return NodeTypeCodes[i].Text; }
VOID DumpBcb ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific bcb.
Arguments:
Address - Gives the address of the bcb to dump
Return Value:
None
--*/
{ ULONG Result, NodeTypeCode;
UNREFERENCED_PARAMETER( Options ); UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
dprintf( "\n Bcb: %08p", Address );
if (GetFieldValue(Address, "BCB", "NodeTypeCode", NodeTypeCode) ) { dprintf("Unable to read BCB at %p.\n", Address); return; } //
// Type of a bcb must be CACHE_NTC_BCB.
//
if (NodeTypeCode != CACHE_NTC_BCB) {
dprintf( "\nBCB signature does not match, type code is %s", TypeCodeGuess((CSHORT) NodeTypeCode )); return; } else { dprintf("Use 'dt -n BCB %p' to dump BCB", Address); return; } #if 0
RM( Address, Bcb, pBcb, PBCB, Result, "Bcb" );
//
// Before we get into too much trouble, make sure this looks like a bcb.
//
//
// Type of a bcb must be CACHE_NTC_BCB.
//
if (Bcb.NodeTypeCode != CACHE_NTC_BCB) {
dprintf( "\nBCB signature does not match, type code is %s", TypeCodeGuess( Bcb.NodeTypeCode )); return; }
//
// Having established that this looks like a bcb, let's dump the
// interesting parts.
//
DUMP_WITH_OFFSET( BCB, Bcb, PinCount, "PinCount " ); DUMP_WITH_OFFSET( BCB, Bcb, ByteLength, "ByteLength " ); DUMP_LL_W_OFFSET( BCB, Bcb, FileOffset, "FileOffset " ); DUMP_LL_W_OFFSET( BCB, Bcb, BeyondLastByte, "BeyondLastByte " ); DUMP_LL_W_OFFSET( BCB, Bcb, OldestLsn, "OldestLsn " ); DUMP_LL_W_OFFSET( BCB, Bcb, NewestLsn, "NewestLsn " ); DUMP_WITH_OFFSET( BCB, Bcb, Vacb, "Vacb " ); DUMP_EMBW_OFFSET( BCB, Address, Resource, "Resource " ); DUMP_WITH_OFFSET( BCB, Bcb, SharedCacheMap, "SharedCacheMap " ); DUMP_WITH_OFFSET( BCB, Bcb, BaseAddress, "BaseAddress " );
if (Bcb.Dirty) {
dprintf( "\n Dirty" );
} else {
dprintf( "\n Not dirty" ); }
dprintf( "\n" );
return; #endif
}
VOID DumpFindData ( IN ULONG64 FileObjectAddress, IN LONG ffset, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump the cache contents for a given file object at the given offset.
Arguments:
FileObjectAddress - Gives the address of the file object to dump
Offset - Gives the offset within the file to dump
Return Value:
None
--*/
{ ULONG Result; ULONG64 pFileObject; ULONG64 pScm; ULONG64 pSop; ULONG64 VacbAddr; // the address of the vacb
ULONG64 *pVacbAddr; ULONG64 VacbAddrAddr; // the address of the address of the vacb
ULONG64 pVacb; ULONG VacbNumber; ULONG OffsetWithinVacb; ULONG Level; ULONG Shift; ULONG OffsetForLevel; LONGLONG Offset = ffset; LONGLONG OriginalOffset = Offset; LONGLONG LevelMask; ULONG PtrSize = DBG_PTR_SIZE; ULONG Type, InVacbsOffset; ULONG64 SectionObjectPointer, SharedCacheMap, Vacbs, SectionSize_Quad;
UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
//
// Before we get into too much trouble, make sure this looks like a FileObject or SCM.
//
if (GetFieldValue(FileObjectAddress, "FILE_OBJECT", "Type", Type)) { dprintf("Unable to read FILE_OBJECT at %p\n", FileObjectAddress); return; }
//
// Type must be IO_TYPE_FILE or a CACHE_NTC_SHARED_CACHE_MAP
//
if (Type != CACHE_NTC_SHARED_CACHE_MAP) {
dprintf( "\n FindData for FileObject %08p", FileObjectAddress );
if (Type != IO_TYPE_FILE) {
dprintf( "\nFILE_OBJECT type signature does not match, type code is %s", TypeCodeGuess((USHORT) Type )); return; }
GetFieldValue(FileObjectAddress, "FILE_OBJECT", "SectionObjectPointer", SectionObjectPointer); dprintf( " Section Object Pointers: %08p", SectionObjectPointer );
if (GetFieldValue(SectionObjectPointer, "SECTION_OBJECT_POINTERS", "SharedCacheMap", SharedCacheMap)) { dprintf("Unable to read SECTION_OBJECT_POINTERS at %p\n", SectionObjectPointer); return; }
dprintf( "\n Shared Cache Map: %08p", SharedCacheMap );
} else {
dprintf( "\n FindData for SharedCacheMap %08p", FileObjectAddress );
SharedCacheMap = FileObjectAddress; }
if (GetFieldValue(SharedCacheMap, "SHARED_CACHE_MAP", "Vacbs", Vacbs)) { dprintf("Unable to read SHARED_CACHE_MAP at %p\n", SharedCacheMap); return; } GetFieldValue(SharedCacheMap, "SHARED_CACHE_MAP", "SectionSize.QuadPart", SectionSize_Quad);
OffsetWithinVacb = (((ULONG) Offset) & (VACB_MAPPING_GRANULARITY - 1)); GetFieldOffset("SHARED_CACHE_MAP", "InitialVacbs", &InVacbsOffset);
dprintf( " File Offset: %I64x ", Offset );
if (Vacbs == (SharedCacheMap + InVacbsOffset)) { CHAR Buff[50]; //
// Small file case -- we're using one of the Vacbs in the Shared Cache Map's
// embedded array.
//
VacbNumber = (ULONG) (Offset >> VACB_OFFSET_SHIFT);
if (VacbNumber >= PREALLOCATED_VACBS) {
dprintf( "\nInvalid VacbNumber for resident Vacb" ); return; }
sprintf(Buff, "InitialVacbs[%d]", VacbNumber); GetFieldValue(SharedCacheMap, "SHARED_CACHE_MAP", Buff, VacbAddr);
dprintf( "in VACB number %x", VacbNumber );
} else if (SectionSize_Quad <= (VACB_SIZE_OF_FIRST_LEVEL)) {
//
// Medium file case -- we're using a single level (linear) structure to
// store the Vacbs.
//
VacbNumber = (ULONG) (Offset >> VACB_OFFSET_SHIFT); VacbAddrAddr = Vacbs + (VacbNumber * PtrSize); if (!ReadPointer(VacbAddrAddr, &VacbAddr)) { dprintf("Unable to read at %p\n", VacbAddrAddr); return; } // RM( VacbAddrAddr, VacbAddr, pVacbAddr, PVOID, Result, "VACB array" );
dprintf( "in VACB number %x", VacbNumber );
} else {
//
// Large file case -- multilevel Vacb storage.
//
Level = 0; Shift = VACB_OFFSET_SHIFT + VACB_LEVEL_SHIFT;
//
// Loop to calculate how many levels we have and how much we have to
// shift to index into the first level.
//
do {
Level += 1; Shift += VACB_LEVEL_SHIFT;
} while (SectionSize_Quad > ((ULONG64)1 << Shift));
//
// Now descend the tree to the bottom level to get the caller's Vacb.
//
Shift -= VACB_LEVEL_SHIFT; // dprintf( "Shift: 0x%x\n", Shift );
OffsetForLevel = (ULONG) (Offset >> Shift); VacbAddrAddr = Vacbs + (OffsetForLevel * PtrSize); if (!ReadPointer(VacbAddrAddr, &VacbAddr)) { dprintf("Unable to read at %p\n", VacbAddrAddr); return; }
while ((VacbAddr != 0) && (Level != 0)) {
Level -= 1;
Offset &= ((LONGLONG)1 << Shift) - 1;
Shift -= VACB_LEVEL_SHIFT;
// dprintf( "Shift: 0x%x\n", Shift );
OffsetForLevel = (ULONG) (Offset >> Shift); VacbAddrAddr = VacbAddr + (OffsetForLevel * PtrSize); if (!ReadPointer(VacbAddrAddr, &VacbAddr)) { dprintf("Unable to read at %p\n", VacbAddrAddr); return; } } }
if (VacbAddr != 0) { ULONG64 Base;
dprintf( "\n Vacb: %08p", VacbAddr ); if (GetFieldValue(VacbAddr, "VACB", "BaseAddress", Base)) { dprintf("Unable to read VACB at %p.", VacbAddr); return; } dprintf( "\n Your data is at: %08p", (Base + OffsetWithinVacb) );
} else {
dprintf( "\n Data at offset %I64x not mapped", OriginalOffset ); }
dprintf( "\n" );
return; }
VOID DumpPcm ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific private cache map.
Arguments:
Address - Gives the address of the private cache map to dump
Return Value:
None
--*/
{ ULONG Result, NodeTypeCode; ULONG64 pPcm;
UNREFERENCED_PARAMETER( Options ); UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
dprintf( "\n Private cache map: %08p", Address );
if (GetFieldValue(Address, "PRIVATE_CACHE_MAP", "NodeTypeCode", NodeTypeCode)) { dprintf("Unable to read PRIVATE_CACHE_MAP at %p\n", Address); return; }
// RM( Address, Pcm, pPcm, PPRIVATE_CACHE_MAP, Result, "PrivateCacheMap" );
//
// Before we get into too much trouble, make sure this looks like a private cache map.
//
//
// Type of a private cache map must be CACHE_NTC_PRIVATE_CACHE_MAP.
//
if (NodeTypeCode != CACHE_NTC_PRIVATE_CACHE_MAP) {
dprintf( "\nPrivate cache map signature does not match, type code is %s", TypeCodeGuess( (USHORT) NodeTypeCode )); return; } else { dprintf("Use 'dt PRIVATE_CACHE_MAP %p' to dump PCM\n", Address); return; } #if 0
DUMP_WITH_OFFSET( PRIVATE_CACHE_MAP, Pcm, FileObject, "FileObject " );
DUMP_LL_W_OFFSET( PRIVATE_CACHE_MAP, Pcm, FileOffset1, "FileOffset1 " ); DUMP_LL_W_OFFSET( PRIVATE_CACHE_MAP, Pcm, BeyondLastByte1, "BeyondLastByte1 " ); DUMP_LL_W_OFFSET( PRIVATE_CACHE_MAP, Pcm, FileOffset2, "FileOffset2 " ); DUMP_LL_W_OFFSET( PRIVATE_CACHE_MAP, Pcm, BeyondLastByte2, "BeyondLastByte2 " );
DUMP_EMBW_OFFSET( PRIVATE_CACHE_MAP, Address, ReadAheadSpinLock, "ReadAheadSpinLock " );
DUMP_WITH_OFFSET( PRIVATE_CACHE_MAP, Pcm, ReadAheadMask, "ReadAheadMask " );
dprintf( "\n ReadAhead -- " );
if (Pcm.ReadAheadActive) {
dprintf( "Active, " );
} else {
dprintf( "Not active, " ); }
if (Pcm.ReadAheadEnabled) {
dprintf( "Enabled" );
} else {
dprintf( "Not enabled" ); }
dprintf( "\n" ); #endif
return; }
VOID DumpScm ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific shared cache map.
Arguments:
Address - Gives the address of the shared cache map to dump
Return Value:
None
--*/
{ ULONG Result, NodeTypeCode; ULONG64 pScm;
UNREFERENCED_PARAMETER( Options ); UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
dprintf( "\n Shared cache map: %08p", Address );
if (GetFieldValue(Address, "SHARED_CACHE_MAP", "NodeTypeCode", NodeTypeCode)) { dprintf("Unable to read SHARED_CACHE_MAP at %p\n", Address); return; }
// RM( Address, Scm, pScm, PSHARED_CACHE_MAP, Result, "SharedCacheMap" );
//
// Before we get into too much trouble, make sure this looks like a shared cache map.
//
//
// Type of a shared cache map must be CACHE_NTC_SHARED_CACHE_MAP.
//
if (NodeTypeCode != CACHE_NTC_SHARED_CACHE_MAP) {
dprintf( "\nShared cache map signature does not match, type code is %s", TypeCodeGuess( (USHORT) NodeTypeCode )); return; } else { dprintf("Use 'dt SHARED_CACHE_MAP %p' to dump PCM\n", Address); return; } #if 0
DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, OpenCount, "OpenCount " );
DUMP_LL_W_OFFSET( SHARED_CACHE_MAP, Scm, FileSize, "FileSize " ); DUMP_LL_W_OFFSET( SHARED_CACHE_MAP, Scm, SectionSize, "SectionSize " ); DUMP_LL_W_OFFSET( SHARED_CACHE_MAP, Scm, ValidDataLength, "ValidDataLength " ); DUMP_LL_W_OFFSET( SHARED_CACHE_MAP, Scm, ValidDataGoal, "ValidDataGoal " );
DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, FileObject, "FileObject " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, ActiveVacb, "ActiveVacb " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, ActivePage, "ActivePage " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, NeedToZero, "NeedToZero " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, NeedToZeroPage, "NeedToZeroPage " );
DUMP_EMBW_OFFSET( SHARED_CACHE_MAP, Address, ActiveVacbSpinLock, "ActiveVacbSpinLock " );
DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, VacbActiveCount, "VacbActiveCount " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, Mbcb, "Mbcb " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, DirtyPages, "DirtyPages " );
DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, Section, "Section " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, Status, "Status " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, CreateEvent, "CreateEvent " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, WaitOnActiveCount, "WaitOnActiveCount " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, PagesToWrite, "PagesToWrite " );
DUMP_LL_W_OFFSET( SHARED_CACHE_MAP, Scm, BeyondLastFlush, "BeyondLastFlush " );
DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, Callbacks, "Callbacks " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, LazyWriteContext, "LazyWriteContext " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, LogHandle, "LogHandle " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, FlushToLsnRoutine, "FlushToLsnRoutine " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, DirtyPageThreshold, "DirtyPageThreshold " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, LazyWritePassCount, "LazyWritePassCount " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, UninitializeEvent, "UninitializeEvent " ); DUMP_WITH_OFFSET( SHARED_CACHE_MAP, Scm, NeedToZeroVacb, "NeedToZeroVacb " );
DUMP_EMBW_OFFSET( SHARED_CACHE_MAP, Address, BcbSpinLock, "BcbSpinLock " ); DUMP_EMBW_OFFSET( SHARED_CACHE_MAP, Address, Event, "Event " ); DUMP_EMBW_OFFSET( SHARED_CACHE_MAP, Address, PrivateCacheMap, "PrivateCacheMap " );
dprintf( "\n" ); #endif
return; }
VOID DumpDeferredWrites ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific shared cache map.
Arguments:
Address - Gives the address of the shared cache map to dump
Return Value:
None
--*/
{ ULONG64 ListHeadAddr; ULONG Result; ULONG Temp; ULONG64 GlobalAddress; ULONG Value; ULONG64 CcTotalDirtyPages=0; ULONG64 CcDirtyPageThreshold=0; ULONG64 MmAvailablePages=0; ULONG64 MmThrottleTop=0; ULONG64 MmThrottleBottom=0; ULONG64 CcSingleDirtySourceDominant=0; ULONG64 CcDirtyPageHysteresisThreshold=0; ULONG64 Total; ULONG Offset; ULONG64 Flink;
BOOLEAN CheckLazyWriter = FALSE; BOOLEAN CheckMappedPageWriter = FALSE; BOOLEAN CheckModifiedPageWriter = FALSE;
UNREFERENCED_PARAMETER( Options ); UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread ); UNREFERENCED_PARAMETER( Address );
dprintf( "*** Cache Write Throttle Analysis ***\n\n" );
CcTotalDirtyPages = GetUlongFromAddress( GlobalAddress = GetExpression( "nt!CcTotalDirtyPages" ));
CcDirtyPageThreshold = GetUlongFromAddress( GlobalAddress = GetExpression( "nt!CcDirtyPageThreshold" ));
if (GlobalAddress = GetExpression( "nt!CcDirtyPageHysteresisThreshold" )) { CcDirtyPageHysteresisThreshold = GetUlongFromAddress( GlobalAddress ); } else { //
// Write hysteresis is new to Whistler. Assume that if the symbol or read fails
// that we're dealing with a downlevel OS.
//
CcDirtyPageHysteresisThreshold = 0; }
MmAvailablePages = GetUlongFromAddress( GlobalAddress = GetExpression( "nt!MmAvailablePages" ));
MmThrottleTop = GetUlongFromAddress( GlobalAddress = GetExpression( "nt!MmThrottleTop" ));
MmThrottleBottom = GetUlongFromAddress( GlobalAddress = GetExpression( "nt!MmThrottleBottom" ));
if (GetFieldValue( GlobalAddress = GetExpression( "nt!MmModifiedPageListHead" ), "nt!_MMPFNLIST", "Total", Total)) { dprintf("Unable to read nt!_MMPFNLIST at %p\n", GlobalAddress); return; }
if (!ReadPointer( GlobalAddress = GetExpression( "nt!CcSingleDirtySourceDominant" ), &CcSingleDirtySourceDominant)) { //
// CcSingleDirtySourceDominant is present in newer builds only
//
if (GlobalAddress) { dprintf("Unable to read nt!CcSingleDirtySourceDominant at %p\n", GlobalAddress); return; } else { CcSingleDirtySourceDominant = 0; } }
dprintf("\tCcTotalDirtyPages: %8u (%8u Kb)\n" "\tCcDirtyPageThreshold: %8u (%8u Kb)\n", (ULONG) CcTotalDirtyPages, (ULONG) CcTotalDirtyPages*_KB, (ULONG) CcDirtyPageThreshold, (ULONG) CcDirtyPageThreshold*_KB);
if (CcDirtyPageHysteresisThreshold) {
dprintf("\tCcDirtyPageHysteresisThreshold: %8u (%8u Kb)\n", (ULONG) CcDirtyPageHysteresisThreshold, (ULONG) CcDirtyPageHysteresisThreshold*_KB); }
dprintf("\tMmAvailablePages: %8u (%8u Kb)\n" "\tMmThrottleTop: %8u (%8u Kb)\n" "\tMmThrottleBottom: %8u (%8u Kb)\n" "\tMmModifiedPageListHead.Total: %8u (%8u Kb)\n\n", (ULONG) MmAvailablePages, (ULONG) MmAvailablePages*_KB, (ULONG) MmThrottleTop, (ULONG) MmThrottleTop*_KB, (ULONG) MmThrottleBottom, (ULONG) MmThrottleBottom*_KB, (ULONG) Total, (ULONG) Total*_KB );
//
// Cc element of the throttle.
//
if (CcDirtyPageHysteresisThreshold) {
if (CcSingleDirtySourceDominant) {
dprintf("Active write hysteresis with CcSingleDirtySourceDominant (%p)\n", CcSingleDirtySourceDominant);
//
// Split this up in the rare case where the transition is occuring.
//
if (CcTotalDirtyPages > CcDirtyPageHysteresisThreshold) { dprintf("CcTotalDirtyPages > CcDirtyPageHysteresisThreshold, writes may be throttled\n"); CheckLazyWriter = TRUE; } } }
if (CcTotalDirtyPages < CcDirtyPageThreshold) {
// From cc.h
#define WRITE_CHARGE_THRESHOLD (64 * PageSize)
if (CcTotalDirtyPages + (WRITE_CHARGE_THRESHOLD/PageSize) >= CcDirtyPageThreshold) {
//
// Fortunately, this is in pages, not bytes. The target's page size could be different.
//
dprintf("CcTotalDirtyPages within %u (max charge) pages of the threshold, writes\n" " may be throttled\n", (WRITE_CHARGE_THRESHOLD/PageSize)); CheckLazyWriter = TRUE;
}
} else {
dprintf("CcTotalDirtyPages >= CcDirtyPageThreshold, writes throttled\n"); CheckLazyWriter = TRUE; }
//
// Mm element of the throttle.
//
if (MmAvailablePages <= MmThrottleTop) {
if (MmAvailablePages <= MmThrottleBottom) {
dprintf("MmAvailablePages <= MmThrottleTop,\n "); dprintf("and MmAvailablePages <= MmThrottleBottom, writes throttled\n "); CheckMappedPageWriter = TRUE; }
if (Total >= 1000) {
if (!CheckMappedPageWriter) { dprintf("MmAvailablePages <= MmThrottleTop,\n "); } dprintf("and modified page list >= 1000, writes throttled\n "); CheckModifiedPageWriter = TRUE; } }
//
// Suggest useful things.
//
if (CheckMappedPageWriter || CheckModifiedPageWriter || CheckLazyWriter) { dprintf("\nCheck these thread(s): "); if (CheckMappedPageWriter) { dprintf("MiMappedPageWriter "); } if (CheckModifiedPageWriter) { dprintf("MiModifiedPageWriter "); } if (CheckLazyWriter) { dprintf("CcWriteBehind(LazyWriter)"); } dprintf("\n");
if (CheckModifiedPageWriter || CheckMappedPageWriter) { dprintf("Check system process for the Mm page writers, !vm 3\n"); } if (CheckLazyWriter) { dprintf("Check critical workqueue for the lazy writer, !exqueue 16\n"); } } else { dprintf("Write throttles not engaged\n"); }
ListHeadAddr = GetExpression( "nt!CcDeferredWrites" ); if (!ListHeadAddr) { dprintf( "Unable to get address of CcDeferredWrites\n" ); return; }
GetFieldOffset("nt!_DEFERRED_WRITE", "DeferredWriteLinks", &Offset); if (GetFieldValue( ListHeadAddr - Offset, "nt!_DEFERRED_WRITE", "DeferredWriteLinks.Flink", Flink)) {
dprintf( "Cannot read nt!_DEFERRED_WRITE at %p\n", ListHeadAddr - Offset); return; }
if (Flink != ListHeadAddr) { ULONG64 Next, Event;
dprintf("Cc Deferred Write list: (CcDeferredWrites)\n"); do {
if (GetFieldValue( Flink - Offset, "nt!_DEFERRED_WRITE", "DeferredWriteLinks.Flink", Next)) {
dprintf( "Cannot read nt!_DEFERRED_WRITE at %p\n", Flink - Offset); return; }
InitTypeRead((Flink - Offset), nt!_DEFERRED_WRITE);
dprintf( " File: %p ", ReadField(FileObject) ); if (Event = ReadField(Event)) { dprintf("Event: %p\n", Event ); } else { UCHAR Name[0x100]; ULONG64 Displacement, PostRoutine;
GetSymbol( (PostRoutine = ReadField(PostRoutine)), Name, &Displacement ); dprintf("CallBack: %s (%p) ", Name, PostRoutine ); dprintf("Context1: %p ", ReadField(Context1) ); dprintf("Context2: %p\n", ReadField(Context2) ); }
if (CheckControlC()) { break; }
} while ( (Flink = Next) != ListHeadAddr );
dprintf( "\n" ); }
return; }
ULONG DumpBcbList ( IN PFIELD_INFO ListElement, IN PVOID Context ) /*++
Routine Description:
Enumeration callback function for the bcblist
Arguments:
ListElement - Pointer to the containing record Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration FALSE to continue the enumeration
--*/ { ULONG Signature; ULONG PinCount = 0; ULONG Dirty = 0; LONGLONG Offset = 0; LONG Options = (LONG)(ULONG_PTR) Context;
if (GetFieldValue( ListElement->address, "BCB", "NodeTypeCode", Signature )) { dprintf( "Unable to read bcb at 0x%I64x\n", ListElement->address ); } else { if (Signature == CACHE_NTC_BCB) {
GetFieldValue( ListElement->address, "BCB", "PinCount", PinCount ); GetFieldValue( ListElement->address, "BCB", "Dirty", Dirty ); GetFieldValue( ListElement->address, "BCB", "FileOffset.QuadPart", Offset );
if ((Options != 0) || (PinCount != 0)) { dprintf( " Bcb: %p @ 0x%I64x PinCount: 0x%x Dirty: %x\n", ListElement->address, Offset, PinCount, Dirty ); } } }
return FALSE; }
VOID DumpLevel( ULONG64 Address, ULONG64 MaxSize, ULONG64 Offset, ULONG Options, ULONG Level, ULONG *Count, ULONG *CountActive ) /*++
Routine Description:
Dump a vacb level recursively.
Arguments:
Address - Gives the address of the vacb level to dump
MaxSize - the total section size
Offset - Current offset within the section being dumped. Should be 0 for 1st caller
Options - if non zero dump everything otherwise only dump referenced vacbs
Level - how many levels of vacbs there are
Count, CountActive - accumulators for counts of VACBs and active VACBs
Return Value:
None
--*/
{ int Index; ULONG PtrSize; ULONG64 SubAddress; ULONG64 Overlay = 0; ULONG64 Limit; ULONG64 Increment; USHORT ActiveCount;
Limit = (MaxSize - Offset) >> VACB_OFFSET_SHIFT; if (Limit > (((ULONG64) 1) << VACB_LEVEL_SHIFT) ) { Limit = (((ULONG64) 1) << VACB_LEVEL_SHIFT); }
Increment = ((ULONG64) 1) << ((VACB_LEVEL_SHIFT * (Level - 1)) + VACB_OFFSET_SHIFT );
PtrSize = DBG_PTR_SIZE;
for (Index=0; Index < Limit; Index++ ) {
if (!ReadPointer( Address + PtrSize * Index, &SubAddress )) { dprintf( "Unable to read subaddress at 0x%I64x\n", Address + PtrSize * Index ); return; }
if (SubAddress != 0) {
if (Level == 1) {
*Count = *Count + 1;
if (GetFieldValue(SubAddress, "VACB", "Overlay.FileOffset.QuadPart", Overlay)) { dprintf( "Unable to read overlay at %p\n", SubAddress ); return; }
//
// If verbose print all vacbs o.w. print any vacbs with reference count
//
ActiveCount = (USHORT) (Overlay & 0xFFFF);
if (ActiveCount != 0) {
*CountActive = *CountActive + 1; }
if (ActiveCount != 0 || Options != 0) { dprintf( "%16I64x ActiveCount %u @ Vacb %p\n", (Overlay & ~(0xFFFF)), ActiveCount, SubAddress ); }
} else {
DumpLevel( SubAddress, MaxSize, Offset + Increment * Index, Options, Level - 1, Count, CountActive ); } } } }
VOID DumpOpenMaps ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific shared cache map.
Arguments:
Address - Gives the address of the shared cache map to dump
Return Value:
None
--*/
{ ULONG64 FirstBcb; ULONGLONG SectionSize; LONG Level; LONG Shift; ULONG64 Vacbs; ULONG ActiveCount = 0, Count = 0;
UNREFERENCED_PARAMETER( Options ); UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread ); UNREFERENCED_PARAMETER( Address );
dprintf( "SharedCacheMap %p\n", Address );
//
// First look for mapped vacbs
//
if (GetFieldValue( Address, "SHARED_CACHE_MAP", "SectionSize.QuadPart", SectionSize )) { dprintf( "Unable to read sectionsize at 0x%I64x\n", Address ); return; }
if (GetFieldValue( Address, "SHARED_CACHE_MAP", "Vacbs", Vacbs )) { dprintf( "Unable to read vacbs at 0x%I64x\n", Address ); return; }
dprintf( "Section Size %I64x\n", SectionSize );
//
// Large file case -- multilevel Vacb storage.
//
Level = 0; Shift = VACB_OFFSET_SHIFT;
//
// Loop to calculate how many levels we have and how much we have to
// shift to index into the first level.
//
while (SectionSize > ((ULONG64)1 << Shift)) {
Level += 1; Shift += VACB_LEVEL_SHIFT; }
dprintf( "Levels %d\n", Level );
if (GetFieldValue( Address, "SHARED_CACHE_MAP", "VacbActiveCount", ActiveCount )) { dprintf( "Unable to read ActiveVacbCount at 0x%I64x\n", Address ); return; }
dprintf("VacbActiveCount %u\n\n", ActiveCount );
//
// Now spit out all of the Vacbs.
//
Count = ActiveCount = 0;
DumpLevel( Vacbs, SectionSize, 0, Options, Level, &Count, &ActiveCount );
dprintf( "\n" );
dprintf( "Total VACBs %u Active %u\n", Count, ActiveCount );
if (GetFieldValue( Address, "SHARED_CACHE_MAP", "BcbList.Flink", FirstBcb )) { dprintf( "Unable to read bcblist at 0x%I64x\n", Address ); return; }
ListType( "SHARED_CACHE_MAP", FirstBcb, 1, "BcbList.Flink", (PVOID)(ULONG_PTR) Options, DumpBcbList );
return; }
//
// Entry points, parameter parsers, etc. below
//
static VOID ParseAndDump ( IN PCHAR args, IN STRUCT_DUMP_ROUTINE DumpFunction, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Parse command line arguments and dump an ntfs structure.
Arguments:
Args - String of arguments to parse.
DumpFunction - Function to call with parsed arguments.
Return Value: %u
None
--*/
{ ULONG64 StructToDump; LONG Options; LPSTR arg2 = args;
//
// If the caller specified an address then that's the item we dump
//
StructToDump = 0; Options = 0;
if (GetExpressionEx(args,&StructToDump, &args )) { Options = (ULONG)GetExpression( args ); }
#ifdef CDAVIS_DEBUG
if (!StructToDump){ dprintf("unable to get expression %s\n",arg2); return; } #endif
(*DumpFunction) ( StructToDump, Options, Processor, hCurrentThread );
dprintf( "\n" );
return; }
static VOID PrintHelp ( VOID ) /*++
Routine Description:
Dump out one line of help for each DECLARE_API
Arguments:
None
Return Value:
None
--*/ { int i;
for( i=0; Extensions[i]; i++ ) dprintf( " %s\n", Extensions[i] ); }
DECLARE_API( bcb )
/*++
Routine Description:
Dump bcb struct
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpBcb, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
DECLARE_API( cchelp )
/*++
Routine Description:
Dump help message
Arguments:
None
Return Value:
None
--*/
{ PrintHelp(); return S_OK; }
DECLARE_API( finddata )
/*++
Routine Description:
Dump bcb struct
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpFindData, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
DECLARE_API( pcm )
/*++
Routine Description:
Dump private cache map struct
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpPcm, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
DECLARE_API( scm )
/*++
Routine Description:
Dump shared cache map struct
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpScm, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
DECLARE_API( defwrites )
/*++
Routine Description:
Dump deferred write queue
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpDeferredWrites, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
DECLARE_API( openmaps )
/*++
Routine Description:
Find referenced bcbs and vacbs in a cache map
Arguments:
arg - [Shared cache map address]
Return Value:
None
--*/
{ ULONG dwProcessor; HANDLE hCurrentThread;
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpOpenMaps, (USHORT)dwProcessor, hCurrentThread );
return S_OK; }
|