1543 lines
37 KiB
C
1543 lines
37 KiB
C
|
/*++
|
|||
|
|
|||
|
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 > (1<<VACB_LEVEL_SHIFT) ) {
|
|||
|
Limit = (1<<VACB_LEVEL_SHIFT);
|
|||
|
}
|
|||
|
|
|||
|
Increment = 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;
|
|||
|
}
|