1559 lines
42 KiB
C
1559 lines
42 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
}
|