|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dmpaddr.c
Abstract:
Routines to examine pages and addresses.
Author:
Lou Perazzoli (loup) 20-Mar-1989 Landy Wang (landyw) 02-Jun-1997
Environment:
Kernel Mode.
Revision History:
--*/
#include "mi.h"
#if DBG
LOGICAL MiFlushUnusedSectionInternal ( IN PCONTROL_AREA ControlArea );
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,MmPerfSnapShotValidPhysicalMemory)
#endif
extern PFN_NUMBER MiStartOfInitialPoolFrame; extern PFN_NUMBER MiEndOfInitialPoolFrame; extern PMMPTE MmSystemPtesEnd[MaximumPtePoolTypes];
#if DBG
PFN_NUMBER MiIdentifyFrame = (PFN_NUMBER)-1; ULONG MiIdentifyCounters[64];
#define MI_INCREMENT_IDENTIFY_COUNTER(x) { \
ASSERT (x < 64); \ MiIdentifyCounters[x] += 1; \ } #else
#define MI_INCREMENT_IDENTIFY_COUNTER(x)
#endif
#if DBG
VOID MiDumpValidAddresses ( ) { ULONG_PTR va; ULONG i; ULONG j; PMMPTE PointerPde; PMMPTE PointerPte;
va = 0; PointerPde = MiGetPdeAddress ((PVOID)va);
for (i = 0; i < PDE_PER_PAGE; i += 1) { if (PointerPde->u.Hard.Valid) { DbgPrint(" **valid PDE, element %ld %lx %lx\n",i,i, PointerPde->u.Long); PointerPte = MiGetPteAddress ((PVOID)va);
for (j = 0 ; j < PTE_PER_PAGE; j += 1) { if (PointerPte->u.Hard.Valid) { DbgPrint("Valid address at %p PTE %p\n", (ULONG)va, PointerPte->u.Long); } va += PAGE_SIZE; PointerPte += 1; } } else { va += (ULONG_PTR)PDE_PER_PAGE * (ULONG_PTR)PAGE_SIZE; }
PointerPde += 1; }
return;
}
VOID MiFormatPte ( IN PMMPTE PointerPte ) {
PMMPTE proto_pte; PSUBSECTION subsect;
if (MmIsAddressValid (PointerPte) == FALSE) { DbgPrint(" cannot dump PTE %p - it's not valid\n\n", PointerPte); return; }
DbgPrint("***DumpPTE at %p contains %p\n", PointerPte, PointerPte->u.Long);
proto_pte = MiPteToProto(PointerPte); subsect = MiGetSubsectionAddress(PointerPte);
DbgPrint(" protoaddr %p subsectaddr %p\n\n", proto_pte, (ULONG_PTR)subsect);
return; }
VOID MiDumpWsl ( VOID ) { ULONG i; PMMWSLE wsle; PEPROCESS CurrentProcess;
CurrentProcess = PsGetCurrentProcess();
DbgPrint("***WSLE cursize %lx frstfree %lx Min %lx Max %lx\n", CurrentProcess->Vm.WorkingSetSize, MmWorkingSetList->FirstFree, CurrentProcess->Vm.MinimumWorkingSetSize, CurrentProcess->Vm.MaximumWorkingSetSize);
DbgPrint(" quota %lx firstdyn %lx last ent %lx next slot %lx\n", MmWorkingSetList->Quota, MmWorkingSetList->FirstDynamic, MmWorkingSetList->LastEntry, MmWorkingSetList->NextSlot);
wsle = MmWsle;
for (i = 0; i < MmWorkingSetList->LastEntry; i += 1) { DbgPrint(" index %lx %p\n",i,wsle->u1.Long); wsle += 1; } return;
}
#define ALLOC_SIZE ((ULONG)8*1024)
#define MM_SAVED_CONTROL 64
//
// Note these are deliberately sign-extended so they will always be greater
// than the highest user address.
//
#define MM_NONPAGED_POOL_MARK ((PUCHAR)(LONG_PTR)0xfffff123)
#define MM_PAGED_POOL_MARK ((PUCHAR)(LONG_PTR)0xfffff124)
#define MM_KERNEL_STACK_MARK ((PUCHAR)(LONG_PTR)0xfffff125)
#define MM_PAGEFILE_BACKED_SHMEM_MARK ((PUCHAR)(LONG_PTR)0xfffff126)
#define MM_DUMP_ONLY_VALID_PAGES 1
typedef struct _KERN_MAP { PVOID StartVa; PVOID EndVa; PKLDR_DATA_TABLE_ENTRY Entry; } KERN_MAP, *PKERN_MAP;
ULONG MiBuildKernelMap ( OUT PKERN_MAP *KernelMapOut );
LOGICAL MiIsAddressRangeValid ( IN PVOID VirtualAddress, IN SIZE_T Length );
NTSTATUS MmMemoryUsage ( IN PVOID Buffer, IN ULONG Size, IN ULONG Type, OUT PULONG OutLength )
/*++
Routine Description:
This routine (debugging only) dumps the current memory usage by walking the PFN database.
Arguments:
Buffer - Supplies a *USER SPACE* buffer in which to copy the data.
Size - Supplies the size of the buffer.
Type - Supplies a value of 0 to dump everything, a value of 1 to dump only valid pages.
OutLength - Returns how much data was written into the buffer.
Return Value:
NTSTATUS.
--*/
{ ULONG i; MMPFN_IDENTITY PfnId; PMMPFN LastPfn; PMMPFN Pfn1; KIRQL OldIrql; PSYSTEM_MEMORY_INFORMATION MemInfo; PSYSTEM_MEMORY_INFO Info; PSYSTEM_MEMORY_INFO InfoStart; PSYSTEM_MEMORY_INFO InfoEnd; PUCHAR String; PUCHAR Master; PCONTROL_AREA ControlArea; NTSTATUS status; ULONG Length; PEPROCESS Process; PUCHAR End; PCONTROL_AREA SavedControl[MM_SAVED_CONTROL]; PSYSTEM_MEMORY_INFO SavedInfo[MM_SAVED_CONTROL]; ULONG j; ULONG ControlCount; UCHAR PageFileMappedString[] = "PageFile Mapped"; UCHAR MetaFileString[] = "Fs Meta File"; UCHAR NoNameString[] = "No File Name"; UCHAR NonPagedPoolString[] = "NonPagedPool"; UCHAR PagedPoolString[] = "PagedPool"; UCHAR KernelStackString[] = "Kernel Stack"; PUCHAR NameString; PKERN_MAP KernMap; ULONG KernSize; PVOID VirtualAddress; PSUBSECTION Subsection; PKLDR_DATA_TABLE_ENTRY DataTableEntry;
String = NULL; ControlCount = 0; Master = NULL; status = STATUS_SUCCESS;
KernSize = MiBuildKernelMap (&KernMap); if (KernSize == 0) { return STATUS_INSUFFICIENT_RESOURCES; }
MemInfo = ExAllocatePoolWithTag (NonPagedPool, (SIZE_T) Size, 'lMmM');
if (MemInfo == NULL) { ExFreePool (KernMap); return STATUS_INSUFFICIENT_RESOURCES; }
InfoStart = &MemInfo->Memory[0]; InfoEnd = InfoStart; End = (PUCHAR)MemInfo + Size;
//
// Walk through the ranges identifying pages.
//
LOCK_PFN (OldIrql);
for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
Pfn1 = MI_PFN_ELEMENT (MmPhysicalMemoryBlock->Run[i].BasePage); LastPfn = Pfn1 + MmPhysicalMemoryBlock->Run[i].PageCount;
for ( ; Pfn1 < LastPfn; Pfn1 += 1) {
RtlZeroMemory (&PfnId, sizeof(PfnId));
MiIdentifyPfn (Pfn1, &PfnId);
if ((PfnId.u1.e1.ListDescription == FreePageList) || (PfnId.u1.e1.ListDescription == ZeroedPageList) || (PfnId.u1.e1.ListDescription == BadPageList) || (PfnId.u1.e1.ListDescription == TransitionPage)) { continue; }
if (PfnId.u1.e1.ListDescription != ActiveAndValid) { if (Type == MM_DUMP_ONLY_VALID_PAGES) { continue; } }
if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGEFILEMAPPED) {
//
// This page belongs to a pagefile-backed shared memory section.
//
Master = MM_PAGEFILE_BACKED_SHMEM_MARK; } else if ((PfnId.u1.e1.UseDescription == MMPFNUSE_FILE) || (PfnId.u1.e1.UseDescription == MMPFNUSE_METAFILE)) {
//
// This shared page maps a file or file metadata.
//
Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte); ControlArea = Subsection->ControlArea; Master = (PUCHAR) ControlArea; } else if (PfnId.u1.e1.UseDescription == MMPFNUSE_NONPAGEDPOOL) {
//
// This is nonpaged pool, put it in the nonpaged pool cell.
//
Master = MM_NONPAGED_POOL_MARK; } else if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGEDPOOL) {
//
// This is paged pool, put it in the paged pool cell.
//
Master = MM_PAGED_POOL_MARK; } else if (PfnId.u1.e1.UseDescription == MMPFNUSE_SESSIONPRIVATE) {
//
// Call this paged pool for now.
//
Master = MM_PAGED_POOL_MARK; } else if (PfnId.u1.e1.UseDescription == MMPFNUSE_DRIVERLOCKPAGE) {
//
// Call this nonpaged pool for now.
//
Master = MM_NONPAGED_POOL_MARK; } else if (PfnId.u1.e1.UseDescription == MMPFNUSE_AWEPAGE) {
//
// Call this nonpaged pool for now.
//
Master = MM_NONPAGED_POOL_MARK; } else {
//
// See if the page is part of the kernel or a driver image.
// If not but it's in system PTEs, call it a kernel thread
// stack.
//
// If neither of the above, then see if the page belongs to
// a user address or a session pagetable page.
//
VirtualAddress = PfnId.u2.VirtualAddress;
for (j = 0; j < KernSize; j += 1) { if ((VirtualAddress >= KernMap[j].StartVa) && (VirtualAddress < KernMap[j].EndVa)) { Master = (PUCHAR)&KernMap[j]; break; } }
if (j == KernSize) { if (PfnId.u1.e1.UseDescription == MMPFNUSE_SYSTEMPTE) { Master = MM_KERNEL_STACK_MARK; } else if (MI_IS_SESSION_PTE (VirtualAddress)) { Master = MM_NONPAGED_POOL_MARK; } else {
ASSERT ((PfnId.u1.e1.UseDescription == MMPFNUSE_PROCESSPRIVATE) || (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGETABLE));
Master = (PUCHAR) (ULONG_PTR) PfnId.u1.e3.PageDirectoryBase; } } }
//
// The page has been identified.
// See if there is already a bucket allocated for it.
//
for (Info = InfoStart; Info < InfoEnd; Info += 1) { if (Info->StringOffset == Master) { break; } }
if (Info == InfoEnd) {
InfoEnd += 1; if ((PUCHAR)InfoEnd > End) { status = STATUS_DATA_OVERRUN; goto Done; }
RtlZeroMemory (Info, sizeof(*Info)); Info->StringOffset = Master; }
if (PfnId.u1.e1.ListDescription == ActiveAndValid) { Info->ValidCount += 1; } else if ((PfnId.u1.e1.ListDescription == StandbyPageList) || (PfnId.u1.e1.ListDescription == TransitionPage)) {
Info->TransitionCount += 1; } else if ((PfnId.u1.e1.ListDescription == ModifiedPageList) || (PfnId.u1.e1.ListDescription == ModifiedNoWritePageList)) { Info->ModifiedCount += 1; }
if (PfnId.u1.e1.UseDescription == MMPFNUSE_PAGETABLE) { Info->PageTableCount += 1; } } }
MemInfo->StringStart = (ULONG_PTR)Buffer + (ULONG_PTR)InfoEnd - (ULONG_PTR)MemInfo; String = (PUCHAR)InfoEnd;
//
// Process the buckets ...
//
for (Info = InfoStart; Info < InfoEnd; Info += 1) {
ControlArea = NULL;
if (Info->StringOffset == MM_PAGEFILE_BACKED_SHMEM_MARK) { Length = 16; NameString = PageFileMappedString; } else if (Info->StringOffset == MM_NONPAGED_POOL_MARK) { Length = 14; NameString = NonPagedPoolString; } else if (Info->StringOffset == MM_PAGED_POOL_MARK) { Length = 14; NameString = PagedPoolString; } else if (Info->StringOffset == MM_KERNEL_STACK_MARK) { Length = 14; NameString = KernelStackString; } else if (((PUCHAR)Info->StringOffset >= (PUCHAR)&KernMap[0]) && ((PUCHAR)Info->StringOffset <= (PUCHAR)&KernMap[KernSize])) {
DataTableEntry = ((PKERN_MAP)Info->StringOffset)->Entry; NameString = (PUCHAR)DataTableEntry->BaseDllName.Buffer; Length = DataTableEntry->BaseDllName.Length; } else if (Info->StringOffset > (PUCHAR)MM_HIGHEST_USER_ADDRESS) {
//
// This points to a control area - get the file name.
//
ControlArea = (PCONTROL_AREA)(Info->StringOffset); NameString = (PUCHAR)&ControlArea->FilePointer->FileName.Buffer[0];
Length = ControlArea->FilePointer->FileName.Length; if (Length == 0) { if (ControlArea->u.Flags.NoModifiedWriting) { NameString = MetaFileString; Length = 14; } else if (ControlArea->u.Flags.File == 0) { NameString = PageFileMappedString; Length = 16; } else { NameString = NoNameString; Length = 14; } } } else {
//
// This is a process (or session) top-level page directory.
//
Pfn1 = MI_PFN_ELEMENT (PtrToUlong(Info->StringOffset)); ASSERT (Pfn1->u4.PteFrame == (ULONG_PTR)(Pfn1 - MmPfnDatabase));
Process = (PEPROCESS)Pfn1->u1.Event;
NameString = &Process->ImageFileName[0]; Length = 16; }
if ((String+Length+2) >= End) { status = STATUS_DATA_OVERRUN; Info->StringOffset = NULL; goto Done; }
if ((ControlArea == NULL) || (MiIsAddressRangeValid (NameString, Length))) {
RtlCopyMemory (String, NameString, Length); Info->StringOffset = (PUCHAR)Buffer + ((PUCHAR)String - (PUCHAR)MemInfo); String[Length] = 0; String[Length + 1] = 0; String += Length + 2; } else { if (!(ControlArea->u.Flags.BeingCreated || ControlArea->u.Flags.BeingDeleted) && (ControlCount < MM_SAVED_CONTROL)) {
SavedControl[ControlCount] = ControlArea; SavedInfo[ControlCount] = Info; ControlArea->NumberOfSectionReferences += 1; ControlCount += 1; } Info->StringOffset = NULL; } }
Done: UNLOCK_PFN (OldIrql); ExFreePool (KernMap);
while (ControlCount != 0) {
//
// Process all the pagable name strings.
//
ControlCount -= 1; ControlArea = SavedControl[ControlCount]; Info = SavedInfo[ControlCount]; NameString = (PUCHAR)&ControlArea->FilePointer->FileName.Buffer[0]; Length = ControlArea->FilePointer->FileName.Length; if (Length == 0) { if (ControlArea->u.Flags.NoModifiedWriting) { Length = 12; NameString = MetaFileString; } else if (ControlArea->u.Flags.File == 0) { NameString = PageFileMappedString; Length = 16;
} else { NameString = NoNameString; Length = 12; } } if ((String+Length+2) >= End) { status = STATUS_DATA_OVERRUN; } if (status != STATUS_DATA_OVERRUN) { RtlCopyMemory (String, NameString, Length); Info->StringOffset = (PUCHAR)Buffer + ((PUCHAR)String - (PUCHAR)MemInfo); String[Length] = 0; String[Length + 1] = 0; String += Length + 2; }
LOCK_PFN (OldIrql); ControlArea->NumberOfSectionReferences -= 1; MiCheckForControlAreaDeletion (ControlArea); UNLOCK_PFN (OldIrql); } *OutLength = (ULONG)((PUCHAR)String - (PUCHAR)MemInfo);
//
// Carefully copy the results to the user buffer.
//
try { RtlCopyMemory (Buffer, MemInfo, (ULONG_PTR)String - (ULONG_PTR)MemInfo); } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); }
ExFreePool (MemInfo);
return status; }
ULONG MiBuildKernelMap ( OUT PKERN_MAP *KernelMapOut ) { PKTHREAD CurrentThread; PLIST_ENTRY NextEntry; PKLDR_DATA_TABLE_ENTRY DataTableEntry; PKERN_MAP KernelMap; ULONG i;
i = 0; CurrentThread = KeGetCurrentThread (); KeEnterCriticalRegionThread (CurrentThread); ExAcquireResourceShared (&PsLoadedModuleResource, TRUE);
//
// The caller wants us to allocate the return result buffer. Size it
// by allocating the maximum possibly needed as this should not be
// very big (relatively). It is the caller's responsibility to free
// this. Obviously this option can only be requested after pool has
// been initialized.
//
NextEntry = PsLoadedModuleList.Flink; while (NextEntry != &PsLoadedModuleList) { i += 1; NextEntry = NextEntry->Flink; }
KernelMap = ExAllocatePoolWithTag (NonPagedPool, i * sizeof(KERN_MAP), 'lMmM');
if (KernelMap == NULL) { return 0; }
*KernelMapOut = KernelMap;
i = 0; NextEntry = PsLoadedModuleList.Flink; while (NextEntry != &PsLoadedModuleList) { DataTableEntry = CONTAINING_RECORD (NextEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks); KernelMap[i].Entry = DataTableEntry; KernelMap[i].StartVa = DataTableEntry->DllBase; KernelMap[i].EndVa = (PVOID)((ULONG_PTR)KernelMap[i].StartVa + DataTableEntry->SizeOfImage); i += 1; NextEntry = NextEntry->Flink; }
ExReleaseResourceLite(&PsLoadedModuleResource); KeLeaveCriticalRegionThread (CurrentThread);
return i; }
VOID MiDumpReferencedPages ( VOID )
/*++
Routine Description:
This routine (debugging only) dumps all PFN entries which appear to be locked in memory for i/o.
Arguments:
None.
Return Value:
None.
--*/
{ KIRQL OldIrql; PMMPFN Pfn1; PMMPFN PfnLast;
LOCK_PFN (OldIrql);
Pfn1 = MI_PFN_ELEMENT (MmLowestPhysicalPage); PfnLast = MI_PFN_ELEMENT (MmHighestPhysicalPage);
while (Pfn1 <= PfnLast) {
if ((Pfn1->u2.ShareCount == 0) && (Pfn1->u3.e2.ReferenceCount != 0)) { MiFormatPfn (Pfn1); }
if (Pfn1->u3.e2.ReferenceCount > 1) { MiFormatPfn (Pfn1); }
Pfn1 += 1; }
UNLOCK_PFN (OldIrql); return; }
#else //DBG
NTSTATUS MmMemoryUsage ( IN PVOID Buffer, IN ULONG Size, IN ULONG Type, OUT PULONG OutLength ) { UNREFERENCED_PARAMETER (Buffer); UNREFERENCED_PARAMETER (Size); UNREFERENCED_PARAMETER (Type); UNREFERENCED_PARAMETER (OutLength);
return STATUS_NOT_IMPLEMENTED; }
#endif //DBG
//
// One benefit of using run length maximums of less than 4GB is that even
// frame numbers above 4GB are handled properly despite the 32-bit limitations
// of the bitmap routines.
//
#define MI_MAXIMUM_PFNID_RUN 4096
NTSTATUS MmPerfSnapShotValidPhysicalMemory ( VOID )
/*++
Routine Description:
This routine logs the PFN numbers of all ActiveAndValid pages.
Arguments:
None.
Return Value:
NTSTATUS.
Environment:
Kernel mode. PASSIVE level. No locks held.
--*/
{ ULONG i; PFN_NUMBER StartPage; PFN_NUMBER EndPage; ULONG_PTR MemSnapLocal[(sizeof(MMPFN_MEMSNAP_INFORMATION)/sizeof(ULONG_PTR)) + (MI_MAXIMUM_PFNID_RUN / (8*sizeof(ULONG_PTR))) ]; PMMPFN_MEMSNAP_INFORMATION MemSnap; PMMPFN Pfn1; PMMPFN FirstPfn; PMMPFN LastPfn; PMMPFN MaxPfn; PMMPFN InitialPfn; RTL_BITMAP BitMap; PULONG ActualBits;
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
ASSERT ((MI_MAXIMUM_PFNID_RUN % (8 * sizeof(ULONG_PTR))) == 0);
MemSnap = (PMMPFN_MEMSNAP_INFORMATION)&MemSnapLocal;
ActualBits = (PULONG)(MemSnap + 1);
RtlInitializeBitMap (&BitMap, ActualBits, MI_MAXIMUM_PFNID_RUN);
MemSnap->Count = 0; RtlClearAllBits (&BitMap);
ExAcquireFastMutex (&MmDynamicMemoryMutex);
for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
StartPage = MmPhysicalMemoryBlock->Run[i].BasePage; EndPage = StartPage + MmPhysicalMemoryBlock->Run[i].PageCount; FirstPfn = MI_PFN_ELEMENT (StartPage); LastPfn = MI_PFN_ELEMENT (EndPage);
//
// Find the first valid PFN and start the run there.
//
for (Pfn1 = FirstPfn; Pfn1 < LastPfn; Pfn1 += 1) { if (Pfn1->u3.e1.PageLocation == ActiveAndValid) { break; } }
if (Pfn1 == LastPfn) {
//
// No valid PFNs in this block, move on to the next block.
//
continue; }
MaxPfn = LastPfn; InitialPfn = NULL;
do { if (Pfn1->u3.e1.PageLocation == ActiveAndValid) { if (InitialPfn == NULL) { MemSnap->InitialPageFrameIndex = Pfn1 - MmPfnDatabase; InitialPfn = Pfn1; MaxPfn = InitialPfn + MI_MAXIMUM_PFNID_RUN; } RtlSetBit (&BitMap, (ULONG) (Pfn1 - InitialPfn)); }
Pfn1 += 1;
if ((Pfn1 >= MaxPfn) && (InitialPfn != NULL)) {
//
// Log the bitmap as we're at then end of it.
//
ASSERT ((Pfn1 - InitialPfn) == MI_MAXIMUM_PFNID_RUN); MemSnap->Count = MI_MAXIMUM_PFNID_RUN; PerfInfoLogBytes (PERFINFO_LOG_TYPE_MEMORYSNAPLITE, MemSnap, sizeof(MemSnapLocal));
InitialPfn = NULL; MaxPfn = LastPfn; RtlClearAllBits (&BitMap); } } while (Pfn1 < LastPfn);
//
// Dump any straggling bitmap entries now as this range is finished.
//
if (InitialPfn != NULL) {
ASSERT (Pfn1 == LastPfn); ASSERT (Pfn1 < MaxPfn); ASSERT (Pfn1 > InitialPfn);
MemSnap->Count = Pfn1 - InitialPfn; PerfInfoLogBytes (PERFINFO_LOG_TYPE_MEMORYSNAPLITE, MemSnap, sizeof(MMPFN_MEMSNAP_INFORMATION) + (ULONG) ((MemSnap->Count + 8) / 8));
RtlClearAllBits (&BitMap); } }
ExReleaseFastMutex (&MmDynamicMemoryMutex);
return STATUS_SUCCESS; }
#define PFN_ID_BUFFERS 128
NTSTATUS MmIdentifyPhysicalMemory ( VOID )
/*++
Routine Description:
This routine calls the pfn id code for each page. Because the logging can't handle very large amounts of data in a burst (limited buffering), the data is broken into page size chunks.
Arguments:
None.
Return Value:
NTSTATUS.
Environment:
Kernel mode. PASSIVE level. No locks held.
--*/
{ ULONG i; KIRQL OldIrql; PMMPFN Pfn1; PMMPFN EndPfn; PFN_NUMBER PageFrameIndex; MMPFN_IDENTITY PfnIdBuffer[PFN_ID_BUFFERS]; PMMPFN_IDENTITY BufferPointer; PMMPFN_IDENTITY BufferLast;
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
BufferPointer = &PfnIdBuffer[0]; BufferLast = BufferPointer + PFN_ID_BUFFERS; RtlZeroMemory (PfnIdBuffer, sizeof(PfnIdBuffer));
ExAcquireFastMutex (&MmDynamicMemoryMutex);
//
// Walk through the ranges and identify pages until
// the buffer is full or we've run out of pages.
//
for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i += 1) {
PageFrameIndex = MmPhysicalMemoryBlock->Run[i].BasePage; Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
EndPfn = Pfn1 + MmPhysicalMemoryBlock->Run[i].PageCount;
LOCK_PFN (OldIrql);
while (Pfn1 < EndPfn) {
MiIdentifyPfn (Pfn1, BufferPointer);
BufferPointer += 1;
if (BufferPointer == BufferLast) {
//
// Release and reacquire the PFN lock so it's not held so long.
//
UNLOCK_PFN (OldIrql);
//
// Log the buffered entries.
//
BufferPointer = &PfnIdBuffer[0]; do {
PerfInfoLogBytes (PERFINFO_LOG_TYPE_PAGEINMEMORY, BufferPointer, sizeof(PfnIdBuffer[0]));
BufferPointer += 1;
} while (BufferPointer < BufferLast);
//
// Reset the buffer to the beginning and zero it.
//
BufferPointer = &PfnIdBuffer[0]; RtlZeroMemory (PfnIdBuffer, sizeof(PfnIdBuffer));
LOCK_PFN (OldIrql); } Pfn1 += 1; }
UNLOCK_PFN (OldIrql); }
//
// Note that releasing this mutex here means the last entry can be
// inserted out of order if we are preempted and another thread starts
// the same operation (or if we're on an MP machine). The PERF module
// must handle this properly as any synchronization provided by this
// routine is purely a side effect not deliberate.
//
ExReleaseFastMutex (&MmDynamicMemoryMutex);
if (BufferPointer != &PfnIdBuffer[0]) {
BufferLast = BufferPointer; BufferPointer = &PfnIdBuffer[0];
do {
PerfInfoLogBytes (PERFINFO_LOG_TYPE_PAGEINMEMORY, BufferPointer, sizeof(PfnIdBuffer[0]));
BufferPointer += 1;
} while (BufferPointer < BufferLast); }
return STATUS_SUCCESS; }
VOID FASTCALL MiIdentifyPfn ( IN PMMPFN Pfn1, OUT PMMPFN_IDENTITY PfnIdentity )
/*++
Routine Description:
This routine captures relevant information for the argument page frame.
Arguments:
Pfn1 - Supplies the PFN element of the page frame number being queried.
PfnIdentity - Receives the structure to fill in with the information.
Return Value:
None.
Environment:
Kernel mode. PFN lock held.
--*/
{ ULONG i; PMMPTE PteAddress; PSUBSECTION Subsection; PCONTROL_AREA ControlArea; PVOID VirtualAddress; PFILE_OBJECT FilePointer; PFN_NUMBER PageFrameIndex;
MI_INCREMENT_IDENTIFY_COUNTER (8);
ASSERT (PfnIdentity->u2.VirtualAddress == 0); ASSERT (PfnIdentity->u1.e1.ListDescription == 0); ASSERT (PfnIdentity->u1.e1.UseDescription == 0); ASSERT (PfnIdentity->u1.e1.Pinned == 0); ASSERT (PfnIdentity->u1.e2.Offset == 0);
MM_PFN_LOCK_ASSERT();
PageFrameIndex = Pfn1 - MmPfnDatabase; PfnIdentity->PageFrameIndex = PageFrameIndex; PfnIdentity->u1.e1.ListDescription = Pfn1->u3.e1.PageLocation;
#if DBG
if (PageFrameIndex == MiIdentifyFrame) { DbgPrint ("MmIdentifyPfn: requested PFN %p\n", PageFrameIndex); DbgBreakPoint (); } #endif
MI_INCREMENT_IDENTIFY_COUNTER (Pfn1->u3.e1.PageLocation);
switch (Pfn1->u3.e1.PageLocation) {
case ZeroedPageList: case FreePageList: case BadPageList: return;
case ActiveAndValid:
//
// It's too much work to determine if the page is locked
// in a working set due to cross-process WSL references, etc.
// So don't bother for now.
//
ASSERT (PfnIdentity->u1.e1.ListDescription == MMPFNLIST_ACTIVE);
if (Pfn1->u1.WsIndex == 0) { MI_INCREMENT_IDENTIFY_COUNTER (9); PfnIdentity->u1.e1.Pinned = 1; } else if (Pfn1->u3.e2.ReferenceCount > 1) {
//
// This page is pinned, presumably for an ongoing I/O.
//
PfnIdentity->u1.e1.Pinned = 1; MI_INCREMENT_IDENTIFY_COUNTER (10); } break;
case StandbyPageList: case ModifiedPageList: case ModifiedNoWritePageList: if (Pfn1->u3.e2.ReferenceCount >= 1) {
//
// This page is pinned, presumably for an ongoing I/O.
//
PfnIdentity->u1.e1.Pinned = 1; MI_INCREMENT_IDENTIFY_COUNTER (11); }
if ((Pfn1->u3.e1.PageLocation == ModifiedPageList) && (MI_IS_PFN_DELETED (Pfn1)) && (Pfn1->u2.ShareCount == 0)) {
//
// This page may be a modified write completing in the
// context of the modified writer thread. If the
// address space was deleted while the I/O was in
// progress, the frame will be released now. More
// importantly, the frame's containing frame is
// meaningless as it may have already been freed
// and reused.
//
// We can't tell what this page was being used for
// since its address space is gone, so just call it
// process private for now.
//
MI_INCREMENT_IDENTIFY_COUNTER (40); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE;
return; }
break;
case TransitionPage:
//
// This page is pinned due to a straggling I/O - the virtual
// address has been deleted but an I/O referencing it has not
// completed.
//
PfnIdentity->u1.e1.Pinned = 1; MI_INCREMENT_IDENTIFY_COUNTER (11); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE; return;
default: #if DBG
DbgPrint ("MmIdentifyPfn: unknown PFN %p %x\n", Pfn1, Pfn1->u3.e1.PageLocation); DbgBreakPoint (); #endif
break;
}
//
// Capture differing information based on the type of page being examined.
//
//
// General purpose stress shows 40% of the pages are prototypes so
// for speed, check for these first.
//
if (Pfn1->u3.e1.PrototypePte == 1) {
MI_INCREMENT_IDENTIFY_COUNTER (12);
if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
//
// Demand zero or (equivalently) pagefile backed.
//
// There are some hard problems here preventing more indepth
// identification of these pages:
//
// 1. The PFN contains a backpointer to the prototype PTE - but
// there is no definitive way to get to the SEGMENT or
// CONTROL_AREA from this.
//
// 2. The prototype PTE pointer itself may be paged out and
// the PFN lock is held right now.
//
MI_INCREMENT_IDENTIFY_COUNTER (13);
#if 0
PfnIdentity->u2.FileObject = (PVOID) ControlArea->Segment->u1.CreatingProcess;
PfnIdentity->u1.e2.Offset = (((ULONG_PTR)ControlArea->Segment->u2.FirstMappedVa) >> MMSECTOR_SHIFT); #endif
PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGEFILEMAPPED; return; }
MI_INCREMENT_IDENTIFY_COUNTER (14);
//
// Backed by a mapped file.
//
Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte); ControlArea = Subsection->ControlArea; ASSERT (ControlArea->u.Flags.File == 1); FilePointer = ControlArea->FilePointer; ASSERT (FilePointer != NULL);
PfnIdentity->u2.FileObject = FilePointer;
if (Subsection->SubsectionBase != NULL) { PfnIdentity->u1.e2.Offset = (MiStartingOffset (Subsection, Pfn1->PteAddress) >> MMSECTOR_SHIFT); } else {
//
// The only time we should be here (a valid PFN with no subsection)
// is if we are the segment dereference thread putting pages into
// the freelist. At this point the PFN lock is held and the
// control area/subsection/PFN structures are not yet consistent
// so just treat this as an offset of 0 as it should be rare.
//
ASSERT (PsGetCurrentThread()->StartAddress == (PVOID)(ULONG_PTR)MiDereferenceSegmentThread); }
//
// Check for nomodwrite sections - typically this is filesystem
// metadata although it could also be registry data (which is named).
//
if (ControlArea->u.Flags.NoModifiedWriting) { MI_INCREMENT_IDENTIFY_COUNTER (15); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_METAFILE; return; }
if (FilePointer->FileName.Length != 0) {
//
// This mapped file has a name.
//
MI_INCREMENT_IDENTIFY_COUNTER (16); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_FILE; return; }
//
// No name - this file must be in the midst of a purge, but it
// still *was* a mapped file of some sort.
//
MI_INCREMENT_IDENTIFY_COUNTER (17); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_FILE; return; }
if ((PageFrameIndex >= MiStartOfInitialPoolFrame) && (PageFrameIndex <= MiEndOfInitialPoolFrame)) {
//
// This is initial nonpaged pool.
//
MI_INCREMENT_IDENTIFY_COUNTER (18); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL; VirtualAddress = (PVOID)((ULONG_PTR)MmNonPagedPoolStart + ((PageFrameIndex - MiStartOfInitialPoolFrame) << PAGE_SHIFT)); PfnIdentity->u2.VirtualAddress = PAGE_ALIGN(VirtualAddress); return; }
PteAddress = Pfn1->PteAddress; VirtualAddress = MiGetVirtualAddressMappedByPte (PteAddress); PfnIdentity->u2.VirtualAddress = PAGE_ALIGN(VirtualAddress);
if (MI_IS_SESSION_ADDRESS(VirtualAddress)) {
//
// Note session addresses that map images (or views) that haven't
// undergone a copy-on-write split were already treated as prototype
// PTEs above. This clause handles session pool and copy-on-written
// pages.
//
MI_INCREMENT_IDENTIFY_COUNTER (19); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SESSIONPRIVATE; return; }
if ((VirtualAddress >= MmPagedPoolStart) && (VirtualAddress <= MmPagedPoolEnd)) {
//
// This is paged pool.
//
MI_INCREMENT_IDENTIFY_COUNTER (20); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGEDPOOL; return;
}
if ((VirtualAddress >= MmNonPagedPoolExpansionStart) && (VirtualAddress < MmNonPagedPoolEnd)) {
//
// This is expansion nonpaged pool.
//
MI_INCREMENT_IDENTIFY_COUNTER (21); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL; return; }
if ((VirtualAddress >= MmNonPagedSystemStart) && (PteAddress <= MmSystemPtesEnd[SystemPteSpace])) {
//
// This is driver space, kernel stack, special pool or other
// system PTE mappings.
//
MI_INCREMENT_IDENTIFY_COUNTER (22); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SYSTEMPTE; return; }
#if defined (_X86_)
//
// 2 other ranges of system PTEs can exist on x86.
//
if (((MiNumberOfExtraSystemPdes != 0) && (VirtualAddress >= (PVOID)MiExtraResourceStart) && (VirtualAddress < (PVOID)MiExtraResourceEnd)) ||
((MiUseMaximumSystemSpace != 0) && (VirtualAddress >= (PVOID)MiUseMaximumSystemSpace) && (VirtualAddress < (PVOID)MiUseMaximumSystemSpaceEnd))) { //
// This is driver space, kernel stack, special pool or other
// system PTE mappings.
//
MI_INCREMENT_IDENTIFY_COUNTER (23); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_SYSTEMPTE; return; }
#endif
if (Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME) {
MI_INCREMENT_IDENTIFY_COUNTER (24);
//
// Carefully check here as this could be a legitimate frame as well.
//
if ((Pfn1->u3.e1.StartOfAllocation == 1) && (Pfn1->u3.e1.EndOfAllocation == 1) && (Pfn1->u3.e1.PageLocation == ActiveAndValid)) { if (MI_IS_PFN_DELETED (Pfn1)) { MI_INCREMENT_IDENTIFY_COUNTER (25); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_DRIVERLOCKPAGE; } else { MI_INCREMENT_IDENTIFY_COUNTER (26); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_AWEPAGE; } return; } }
#if DBG
//
// In checked kernels, AWE frames get their containing frame decremented
// when the AWE frame is freed.
//
if (Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME - 1) {
MI_INCREMENT_IDENTIFY_COUNTER (24);
//
// Carefully check here as this could be a legitimate frame as well.
//
if ((Pfn1->u3.e1.StartOfAllocation == 0) && (Pfn1->u3.e1.EndOfAllocation == 0) && (Pfn1->u3.e1.PageLocation == StandbyPageList)) {
MI_INCREMENT_IDENTIFY_COUNTER (26); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_AWEPAGE; return; } }
#endif
//
// Check the PFN working set index carefully here. This must be done
// before walking back through the containing frames because if this page
// is not in a working set, the containing frame may not be meaningful and
// dereferencing it can crash the system and/or yield incorrect walks.
// This is because if a page will never be trimmable there is no need to
// have a containing frame initialized. This also covers the case of
// data pages mapped via large page directory entries as these have no
// containing page table frame.
//
if (Pfn1->u3.e1.PageLocation == ActiveAndValid) {
if (Pfn1->u1.WsIndex == 0) {
//
// Default to calling these allocations nonpaged pool because even
// when they technically are not, from a usage standpoint they are.
// Note the default is overridden for specific cases where the usage
// is not in fact nonpaged.
//
PfnIdentity->u1.e1.UseDescription = MMPFNUSE_NONPAGEDPOOL; ASSERT (PfnIdentity->u1.e1.Pinned == 1); MI_INCREMENT_IDENTIFY_COUNTER (27); return; } }
//
// Must be a process private page
//
// OR
//
// a page table, page directory, parent or extended parent.
//
i = 0; while (Pfn1->u4.PteFrame != PageFrameIndex) {
//
// The only way the PTE address will go out of bounds is if this is
// a top level page directory page for a process that has been
// swapped out but is still waiting for the transition/modified
// page table pages to be reclaimed. ie: until that happens, the
// page directory is marked Active, but the PteAddress & containing
// page are pointing at the EPROCESS pool page.
//
#if defined(_IA64_)
if (((Pfn1->PteAddress >= (PMMPTE) PTE_BASE) && (Pfn1->PteAddress <= (PMMPTE) PTE_TOP)) ||
((Pfn1->PteAddress >= (PMMPTE) PTE_KBASE) && (Pfn1->PteAddress <= (PMMPTE) PTE_KTOP)) ||
((Pfn1->PteAddress >= (PMMPTE) PTE_SBASE) && (Pfn1->PteAddress <= (PMMPTE) PTE_STOP)))
#else
if ((Pfn1->PteAddress >= (PMMPTE) PTE_BASE) && (Pfn1->PteAddress <= (PMMPTE) PTE_TOP))
#endif
{ PageFrameIndex = Pfn1->u4.PteFrame; Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); i += 1; } else { MI_INCREMENT_IDENTIFY_COUNTER (41); break; } }
MI_INCREMENT_IDENTIFY_COUNTER (31+i);
PfnIdentity->u1.e3.PageDirectoryBase = PageFrameIndex;
#if defined(_X86PAE_)
//
// PAE is unique because the 3rd level is not defined as only a mini
// 4 entry 3rd level is in use. Check for that explicitly, noting that
// it takes one extra walk to get to the top. Top level PAE pages (the
// ones that contain only the 4 PDPTE pointers) are treated above as
// active pinned pages, not as pagetable pages because each one is shared
// across 127 processes and resides in the system global space.
//
if (i == _MI_PAGING_LEVELS + 1) {
//
// Had to walk all the way to the top. Must be a data page.
//
MI_INCREMENT_IDENTIFY_COUNTER (29); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE; return; }
#else
if (i == _MI_PAGING_LEVELS) {
//
// Had to walk all the way to the top. Must be a data page.
//
MI_INCREMENT_IDENTIFY_COUNTER (29); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PROCESSPRIVATE; return; }
#endif
//
// Must have been a page in the hierarchy (not a data page) as we arrived
// at the top early.
//
MI_INCREMENT_IDENTIFY_COUNTER (30); PfnIdentity->u1.e1.UseDescription = MMPFNUSE_PAGETABLE;
return; }
|