windows-nt/Source/XPSP1/NT/base/tools/kdexts2/pool.c
2020-09-26 16:20:57 +08:00

2421 lines
73 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
pool.c
Abstract:
WinDbg Extension Api
Author:
Lou Perazzoli (Loup) 5-Nov-1993
Environment:
User Mode.
Revision History:
Kshitiz K. Sharma (kksharma)
Using debugger type info.
--*/
#include "precomp.h"
#pragma hdrstop
typedef struct _POOL_BLOCK_HEAD {
// POOL_HEADER Header;
LIST_ENTRY List;
} POOL_BLOCK_HEAD, *PPOOL_BLOCK_HEADER;
typedef struct _POOL_HACKER {
// POOL_HEADER Header;
ULONG Contents[8];
} POOL_HACKER;
#define TAG 0
#define NONPAGED_ALLOC 1
#define NONPAGED_FREE 2
#define PAGED_ALLOC 3
#define PAGED_FREE 4
#define NONPAGED_USED 5
#define PAGED_USED 6
BOOL NewPool;
ULONG SortBy;
typedef struct _FILTER {
ULONG Tag;
BOOLEAN Exclude;
} FILTER, *PFILTER;
#define MAX_FILTER 64
FILTER Filter[MAX_FILTER];
ULONG64 SpecialPoolStart;
ULONG64 SpecialPoolEnd;
ULONG64 PoolBigTableAddress;
#define DecodeLink(Pool) ( (ULONG64) (Pool & (ULONG64) ~1))
//
// Size of a pool page.
//
// This must be greater than or equal to the page size.
//
#define POOL_PAGE_SIZE PageSize
//
// The smallest pool block size must be a multiple of the page size.
//
// Define the block size as 32.
//
#define POOL_LIST_HEADS (POOL_PAGE_SIZE / (1 << POOL_BLOCK_SHIFT))
#define SPECIAL_POOL_BLOCK_SIZE(PoolHeader_Ulong1) (PoolHeader_Ulong1 & (MI_SPECIAL_POOL_VERIFIER - 1))
#ifndef _EXTFNS_H
// GetPoolTagDescription
typedef HRESULT
(WINAPI *PGET_POOL_TAG_DESCRIPTION)(
ULONG PoolTag,
PSTR *pDescription
);
#endif
ULONG64
GetSpecialPoolHeader (
IN PVOID DataPage,
IN ULONG64 RealDataPage,
OUT PULONG64 ReturnedDataStart
);
int __cdecl
ulcomp(const void *e1,const void *e2)
{
ULONG u1;
LONG64 diff;
ULONG64 ValE1, ValE2;
switch (SortBy) {
case TAG:
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "Key", ValE1);
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "Key", ValE2);
u1 = ((PUCHAR)&ValE1)[0] - ((PUCHAR)&ValE2)[0];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)&ValE1)[1] - ((PUCHAR)&ValE2)[1];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)&ValE1)[2] - ((PUCHAR)&ValE2)[2];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)&ValE1)[3] - ((PUCHAR)&ValE2)[3];
return u1;
break;
case NONPAGED_ALLOC:
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedAllocs", ValE1);
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedAllocs", ValE2);
diff = ValE2 - ValE1;
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
break;
case NONPAGED_FREE:
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedFrees", ValE1);
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedFrees", ValE2);
diff = ValE2 - ValE1;
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
break;
case NONPAGED_USED:
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "NonPagedBytes", ValE1);
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "NonPagedBytes", ValE2);
diff = ValE2 - ValE1;
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
break;
case PAGED_USED:
GetFieldValue(*((PULONG64) e1), "nt!_POOL_TRACKER_TABLE", "PagedBytes", ValE1);
GetFieldValue(*((PULONG64) e2), "nt!_POOL_TRACKER_TABLE", "PagedBytes", ValE2);
diff = ValE2 - ValE1;
return( diff ? ( diff > 0 ? 1 : -1 ) : 0 );
break;
default:
return(0);
break;
}
}
/*++
Routine Description:
Sets up generally useful pool globals.
Must be called in every DECLARE_API interface that uses pool.
Arguments:
None.
Return Value:
None
--*/
LOGICAL PoolInitialized = FALSE;
LOGICAL
PoolInitializeGlobals(
VOID
)
{
if (PoolInitialized == TRUE) {
return TRUE;
}
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
if (PageSize < 0x1000 || (PageSize & (ULONG)0xFFF)) {
dprintf ("unable to get MmPageSize (0x%x) - probably bad symbols\n", PageSize);
return FALSE;
}
PoolInitialized = TRUE;
return TRUE;
}
DECLARE_API( frag )
/*++
Routine Description:
Dump pool fragmentation
Arguments:
args - Flags
Return Value:
None
--*/
{
ULONG Flags;
ULONG result;
ULONG i;
ULONG count;
ULONG64 Pool;
ULONG64 PoolLoc1;
ULONG TotalFrag;
ULONG TotalCount;
ULONG Frag;
ULONG64 PoolStart;
ULONG PoolOverhead;
ULONG64 PoolLoc;
ULONG PoolTag, BlockSize, PreviousSize, PoolIndex;
ULONG TotalPages, TotalBigPages;
ULONG64 Flink, Blink;
PCHAR pc;
ULONG64 tmp;
#define PoolBlk(F,V) GetFieldValue(Pool, "nt!_POOL_BLOCK_HEAD", #F, V)
if (PoolInitializeGlobals() == FALSE) {
return E_INVALIDARG;
}
dprintf("\n NonPaged Pool Fragmentation\n\n");
Flags = 0;
PoolStart = 0;
if (GetExpressionEx(args, &tmp, &args)) {
Flags = (ULONG) tmp;
PoolStart = GetExpression (args);
}
PoolOverhead = GetTypeSize("nt!_POOL_HEADER");
if (PoolStart != 0) {
PoolStart += PoolOverhead;
Pool = DecodeLink(PoolStart);
do {
Pool = Pool - PoolOverhead;
if ( PoolBlk(Header.PoolTag, PoolTag) ) {
dprintf("%08p: Unable to get contents of pool block\n", Pool );
return E_INVALIDARG;
}
PoolBlk(Header.BlockSize,BlockSize);
PoolBlk(Header.PreviousSize,PreviousSize);
PoolBlk(List.Flink,Flink);
PoolBlk(List.Blink,Blink);
dprintf(" %p size: %4lx previous size: %4lx %c%c%c%c links: %8p %8p\n",
Pool,
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP((PoolTag&~PROTECTED_POOL) >> 24),
#undef PP
Flink,
Blink);
if (Flags != 3) {
Pool = Flink;
} else {
Pool = Blink;
}
Pool = DecodeLink(Pool);
if (CheckControlC()) {
return E_INVALIDARG;
}
} while ( (Pool & (ULONG64) ~1) != (PoolStart & (ULONG64) ~1) );
return E_INVALIDARG;
}
PoolLoc1 = GetNtDebuggerData( NonPagedPoolDescriptor );
if (PoolLoc1 == 0) {
dprintf ("unable to get nonpaged pool head\n");
return E_INVALIDARG;
}
PoolLoc = PoolLoc1;
TotalFrag = 0;
TotalCount = 0;
for (i = 0; i < POOL_LIST_HEADS; i += 1) {
CHAR Buffer[40];
ULONG ListOffset;
sprintf(Buffer, "ListHeads[%d].Flink", i);
Frag = 0;
count = 0;
if (GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", Buffer, Pool)) {
dprintf ("%08p: Unable to get pool descriptor\n", PoolLoc1);
return E_INVALIDARG;
}
GetFieldOffset("nt!_POOL_DESCRIPTOR", Buffer, &ListOffset);
// Pool = (PUCHAR)PoolDesc.ListHeads[i].Flink;
Pool = DecodeLink(Pool);
while (Pool != (ListOffset + PoolLoc)) {
Pool = Pool - PoolOverhead;
if ( PoolBlk(Header.PoolTag, PoolTag) ) {
dprintf("%08p: Unable to get contents of pool block\n", Pool );
return E_INVALIDARG;
}
PoolBlk(Header.BlockSize,BlockSize);
PoolBlk(Header.PreviousSize,PreviousSize);
PoolBlk(List.Flink,Flink);
Frag += BlockSize << POOL_BLOCK_SHIFT;
count += 1;
if (Flags & 2) {
dprintf(" ListHead[%x]: %p size: %4lx previous size: %4lx %c%c%c%c\n",
i,
(ULONG)Pool,
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP((PoolTag&~PROTECTED_POOL) >> 24));
#undef PP
}
Pool = Flink;
Pool = DecodeLink(Pool);
if (CheckControlC()) {
return E_INVALIDARG;
}
}
if (Flags & 1) {
dprintf("index: %2ld number of fragments: %5ld bytes: %6ld\n",
i,count,Frag);
}
TotalFrag += Frag;
TotalCount += count;
}
dprintf("\n Number of fragments: %7ld consuming %7ld bytes\n",
TotalCount,TotalFrag);
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalPages",TotalPages);
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalBigPages", TotalBigPages);
dprintf( " NonPagedPool Usage: %7ld bytes\n",(TotalPages + TotalBigPages)*PageSize);
return S_OK;
#undef PoolBlk
}
PRTL_BITMAP
GetBitmap(
ULONG64 pBitmap
)
{
PRTL_BITMAP p;
ULONG Size, Result;
ULONG64 Buffer=0;
if ( GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "Buffer", Buffer)) {
dprintf("%08p: Unable to get contents of bitmap\n", pBitmap );
return 0;
}
GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "SizeOfBitMap", Size);
p = HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) + (Size / 8) );
if (p) {
p->SizeOfBitMap = Size;
p->Buffer = (PULONG)(p + 1);
if ( !ReadMemory( Buffer,
p->Buffer,
Size / 8,
&Result) ) {
dprintf("%08p: Unable to get contents of bitmap buffer\n", Buffer );
HeapFree( GetProcessHeap(), 0, p );
p = NULL;
}
}
return p;
}
VOID
DumpPool(
VOID
)
{
ULONG64 p, pStart;
ULONG64 Size;
ULONG BusyFlag;
ULONG CurrentPage, NumberOfPages;
PRTL_BITMAP StartMap;
PRTL_BITMAP EndMap;
ULONG64 PagedPoolStart;
ULONG64 PagedPoolEnd;
ULONG Result;
UCHAR PgPool[] = "nt!_MM_PAGED_POOL_INFO";
ULONG64 PagedPoolInfoPointer;
ULONG64 PagedPoolAllocationMap=0, EndOfPagedPoolBitmap=0;
if (PoolInitializeGlobals() == FALSE) {
return;
}
PagedPoolInfoPointer = GetNtDebuggerData( MmPagedPoolInformation );
if ( GetFieldValue( PagedPoolInfoPointer,
PgPool,
"PagedPoolAllocationMap",
PagedPoolAllocationMap)) {
dprintf("%08p: Unable to get contents of paged pool information\n",
PagedPoolInfoPointer );
return;
}
GetFieldValue( PagedPoolInfoPointer, PgPool, "EndOfPagedPoolBitmap", EndOfPagedPoolBitmap);
StartMap = GetBitmap( PagedPoolAllocationMap );
EndMap = GetBitmap( EndOfPagedPoolBitmap );
PagedPoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
PagedPoolEnd = GetNtDebuggerDataPtrValue( MmPagedPoolEnd );
if (StartMap && EndMap) {
p = PagedPoolStart;
CurrentPage = 0;
dprintf( "Paged Pool: %p .. %p\n", PagedPoolStart, PagedPoolEnd );
while (p < PagedPoolEnd) {
if ( CheckControlC() ) {
return;
}
pStart = p;
BusyFlag = RtlCheckBit( StartMap, CurrentPage );
while ( ~(BusyFlag ^ RtlCheckBit( StartMap, CurrentPage )) ) {
p += PageSize;
if (RtlCheckBit( EndMap, CurrentPage )) {
CurrentPage++;
break;
}
CurrentPage++;
if (p > PagedPoolEnd) {
break;
}
}
Size = p - pStart;
dprintf( "%p: %I64x - %s\n", pStart, Size, BusyFlag ? "busy" : "free" );
}
}
HeapFree( GetProcessHeap(), 0, StartMap );
HeapFree( GetProcessHeap(), 0, EndMap );
}
void
PrintPoolTagComponent(
ULONG PoolTag
)
{
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
PSTR TagComponent;
#ifdef _EXTFNS_H
DEBUG_POOLTAG_DESCRIPTION Desc = {0};
Desc.SizeOfStruct = sizeof(DEBUG_POOLTAG_DESCRIPTION);
GetPoolTagDescription = NULL;
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
!GetPoolTagDescription) {
return;
}
(*GetPoolTagDescription)(PoolTag, &Desc);
if (Desc.Description[0]) {
dprintf("\t\tPooltag %4.4s : %s", &PoolTag, Desc.Description);
if (Desc.Binary[0]) {
dprintf(", Binary : %s",Desc.Binary);
}
if (Desc.Owner[0]) {
dprintf(", Owner : %s", Desc.Owner);
}
dprintf("\n");
} else {
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
}
#else
GetPoolTagDescription = NULL;
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
!GetPoolTagDescription) {
return;
}
(*GetPoolTagDescription)(PoolTag, &TagComponent);
if (TagComponent && (100 < (ULONG64) TagComponent)) {
dprintf("\t\tOwning component : %s\n", TagComponent);
} else {
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
}
#endif
}
PSTR g_PoolRegion[DbgPoolRegionMax] = {
"Unknown", // DbgPoolRegionUnknown,
"Special pool", // DbgPoolRegionSpecial,
"Paged pool", // DbgPoolRegionPaged,
"Nonpaged pool", // DbgPoolRegionNonPaged,
"Pool code", // DbgPoolRegionCode,
"Nonpaged pool expansion", // DbgPoolRegionNonPagedExpansion,
};
DEBUG_POOL_REGION
GetPoolRegion(
ULONG64 Pool
)
{
static ULONG64 PoolCodeEnd;
static ULONG64 SpecialPoolEnd;
static ULONG64 PagedPoolEnd;
static ULONG64 NonPagedPoolEnd;
static ULONG64 NonPagedPoolStart;
static ULONG64 SpecialPoolStart;
static ULONG64 PagedPoolStart;
static ULONG64 NonPagedPoolExpansionStart;
static ULONG64 PoolCodeStart;
static BOOL GotAll = FALSE;
if (!GotAll) {
PoolCodeEnd = GetPointerValue("nt!MmPoolCodeEnd");
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
PagedPoolEnd = GetPointerValue("nt!MmPagedPoolEnd");
NonPagedPoolEnd = GetPointerValue("nt!MmNonPagedPoolEnd");
NonPagedPoolStart = GetPointerValue("nt!MmNonPagedPoolStart");
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
PagedPoolStart = GetPointerValue("nt!MmPagedPoolStart");
NonPagedPoolExpansionStart = GetPointerValue("nt!MmNonPagedPoolExpansionStart");
PoolCodeStart = GetPointerValue("nt!MmPoolCodeStart");
GotAll = TRUE;
}
if (!(PoolCodeStart || SpecialPoolStart || SpecialPoolEnd || PoolCodeEnd ||
NonPagedPoolExpansionStart || NonPagedPoolStart || NonPagedPoolEnd ||
PagedPoolStart || PagedPoolEnd)) {
GotAll = FALSE;
return DbgPoolRegionUnknown;
}
if ( Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
return DbgPoolRegionSpecial;
} else if ( Pool >= PagedPoolStart && Pool < PagedPoolEnd) {
return DbgPoolRegionPaged;
} else if ( Pool >= NonPagedPoolStart && Pool < NonPagedPoolEnd) {
return DbgPoolRegionNonPaged;
} else if ( Pool >= PoolCodeStart && Pool < PoolCodeEnd) {
return DbgPoolRegionCode;
} else if ( Pool >= NonPagedPoolExpansionStart) {
return DbgPoolRegionNonPagedExpansion;
} else {
return DbgPoolRegionUnknown;
}
return DbgPoolRegionUnknown;
}
void
PrintPoolRegion(
ULONG64 Pool
)
{
PSTR pszRegion;
DEBUG_POOL_REGION Region;
Region = GetPoolRegion(Pool);
pszRegion = g_PoolRegion[Region];
if (pszRegion) {
dprintf(pszRegion);
dprintf("\n");
} else {
dprintf("Region unkown\n", Pool);
}
}
HRESULT
ListPoolPage(
ULONG64 PoolPageToDump,
ULONG Flags,
PDEBUG_POOL_DATA PoolData
)
{
ULONG64 PoolTableAddress;
ULONG result;
ULONG PoolTag;
ULONG Result;
ULONG64 StartPage;
ULONG64 Pool;
ULONG PoolBlockSize;
ULONG PoolHeaderSize;
ULONG64 PoolHeader;
ULONG Previous;
UCHAR c;
PUCHAR p;
ULONG64 PoolDataEnd;
UCHAR PoolBlockPattern;
UCHAR DataPage[0x5000];
PUCHAR DataStart;
ULONG64 RealDataStart;
LOGICAL Pagable;
LOGICAL FirstBlock;
ULONG BlockType;
ULONG PoolWhere;
ULONG i;
ULONG j;
ULONG ct;
ULONG start;
ULONG PoolBigPageTableSize;
ULONG SizeOfPoolHdr=GetTypeSize("nt!_POOL_HEADER");
if (!SpecialPoolStart) {
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
}
Pool = PAGE_ALIGN64 (PoolPageToDump);
StartPage = Pool;
Previous = 0;
if (PoolData) {
ZeroMemory(PoolData, sizeof(DEBUG_POOL_DATA));
}
if (!(Flags & 0x80000000)) {
dprintf("Pool page %p region is ", PoolPageToDump);
PrintPoolRegion(PoolPageToDump);
}
if ( Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
ULONG Hdr_Ulong=0;
// LWFIX: this is not ported yet.
// dprintf("reading %I64x datapage %x\n", Pool, min(PageSize, sizeof(DataPage)));
// Pre read the pool
if ( !ReadMemory( Pool,
&DataPage[0],
min(PageSize, sizeof(DataPage)),
&Result) ) {
dprintf("%08p: Unable to get contents of special pool block\n", Pool );
return E_INVALIDARG;
}
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong)) {
dprintf("%08p: Unable to get nt!_POOL_HEADER\n", Pool );
return E_INVALIDARG;
}
//
// Determine whether the data is at the start or end of the page.
// Start off by assuming the data is at the end, in this case the
// header will be at the start.
//
PoolHeader = GetSpecialPoolHeader((PVOID) &DataPage[0], Pool, &RealDataStart);
if (PoolHeader == 0) {
dprintf("Block %p is a corrupted special pool allocation\n",
PoolPageToDump);
return E_INVALIDARG;
}
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(Hdr_Ulong);
if (Hdr_Ulong & MI_SPECIAL_POOL_PAGABLE) {
Pagable = TRUE;
} else {
Pagable = FALSE;
}
if (PoolData) {
PoolData->Pool = RealDataStart;
PoolData->PoolBlock = PoolPageToDump;
PoolData->SpecialPool = 1;
PoolData->Pageable = Hdr_Ulong & 0x8000 ? 1 : 0;
PoolData->Size = PoolBlockSize;
if (Flags & 0x80000000) {
// do not print anything
return S_OK;
}
}
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
RealDataStart,
PoolBlockSize,
Hdr_Ulong & 0x8000 ? "pagable" : "non-paged",
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP(PoolTag >> 24)
);
#undef PP
PrintPoolTagComponent(PoolTag);
//
// Add code to validate whole block.
//
return S_OK;
}
FirstBlock = TRUE;
while (PAGE_ALIGN64(Pool) == StartPage) {
ULONG BlockSize=0, PreviousSize=0, PoolType=0, AllocatorBackTraceIndex=0;
ULONG PoolTagHash=0, PoolIndex=0;
ULONG64 ProcessBilled=0;
if ( CheckControlC() ) {
return E_INVALIDARG;
}
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize) ) {
dprintf("%08p: Unable to get contents of pool block\n", Pool );
return E_INVALIDARG;
}
if (PoolPageToDump >= Pool &&
PoolPageToDump < (Pool + (BlockSize << POOL_BLOCK_SHIFT))
) {
c = '*';
} else {
c = ' ';
}
GetFieldValue( Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolType", PoolType);
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
GetFieldValue( Pool, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
GetFieldValue( Pool, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
BlockType = 0;
if ((BlockSize << POOL_BLOCK_SHIFT) >= POOL_PAGE_SIZE) {
BlockType = 1;
} else if (BlockSize == 0) {
BlockType = 2;
} else if (PreviousSize != Previous) {
BlockType = 3;
}
if (BlockType != 0) {
ULONG BigPageSize = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
if (!BigPageSize) {
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
break;
}
//
// See if this is a big block allocation. Iff we have not parsed
// any other small blocks in here already.
//
if (FirstBlock == TRUE) {
if (!PoolBigTableAddress) {
PoolBigTableAddress = GetPointerValue ("PoolBigPageTable");
}
PoolTableAddress = PoolBigTableAddress;
if (PoolTableAddress) {
dprintf ("%p is not a valid small pool allocation, checking large pool...\n", Pool);
PoolBigPageTableSize = GetUlongValue ("PoolBigPageTableSize");
//
// Scan the table looking for a match.
//
i = 0;
ct = PageSize / BigPageSize;
while (i < PoolBigPageTableSize) {
ULONG64 Va=0;
ULONG Key=0, NumberOfPages=0;
if (PoolBigPageTableSize - i < ct) {
ct = PoolBigPageTableSize - i;
}
if ( GetFieldValue( PoolTableAddress,
"nt!_POOL_TRACKER_BIG_PAGES",
"Va",
Va) ) {
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
return E_INVALIDARG;
}
for (j = 0; j < ct; j += 1) {
if ( GetFieldValue( PoolTableAddress + BigPageSize*j,
"nt!_POOL_TRACKER_BIG_PAGES",
"Va",
Va) ) {
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
return E_INVALIDARG;
}
if (Va == PAGE_ALIGN64(Pool)) {
//
// Match !
//
GetFieldValue( PoolTableAddress + BigPageSize*j,
"nt!_POOL_TRACKER_BIG_PAGES",
"Key",
Key);
GetFieldValue( PoolTableAddress + BigPageSize*j,
"nt!_POOL_TRACKER_BIG_PAGES",
"NumberOfPages",
NumberOfPages);
PoolTag = Key;
if (PoolData) {
PoolData->Pool = PoolPageToDump;
PoolData->Size = NumberOfPages*PageSize;
PoolData->PoolTag = PoolTag;
PoolData->LargePool = 1;
PoolData->Free = (Pool & POOL_BIG_TABLE_ENTRY_FREE) ? 1 : 0;
if (Flags & 0x80000000) {
// do not print anything
return S_OK;
}
}
dprintf("*%p :%s large page allocation, Tag is %c%c%c%c, size is 0x%x bytes\n",
(Pool & ~POOL_BIG_TABLE_ENTRY_FREE),
(Pool & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP(PoolTag >> 24),
NumberOfPages * PageSize
);
#undef PP
PrintPoolTagComponent(PoolTag);
return S_OK;
}
}
i += ct;
PoolTableAddress += (ct * BigPageSize);
}
//
// No match in small or large pool, must be
// freed or corrupt pool
//
dprintf("%p is freed (or corrupt) pool\n", Pool);
return E_INVALIDARG;
}
dprintf("unable to get pool big page table - either wrong symbols or pool tagging is disabled\n");
}
if (BlockType == 1) {
dprintf("Bad allocation size @%p, too large\n", Pool);
return E_INVALIDARG;
} else if (BlockType == 2) {
dprintf("Bad allocation size @%p, zero is invalid\n", Pool);
return E_INVALIDARG;
} else if (BlockType == 3) {
dprintf("Bad previous allocation size @%p, last size was %lx\n",
Pool, Previous);
return E_INVALIDARG;
}
}
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
if (!(Flags & 2) || c == '*') {
if (PoolData) {
PoolData->Pool = Pool;
PoolData->PoolBlock = PoolPageToDump;
PoolData->PoolTag = PoolTag & ~PROTECTED_POOL;
PoolData->ProcessBilled = ProcessBilled;
PoolData->PreviousSize = PreviousSize << POOL_BLOCK_SHIFT;
PoolData->Size = BlockSize << POOL_BLOCK_SHIFT;
PoolData->Free = ((PoolType != 0) && (!NewPool ?
(PoolIndex & 0x80) : (PoolType & 0x04))) ? 0 : 1;
PoolData->Protected = (PoolTag&PROTECTED_POOL) ? 1 : 0;
if (Flags & 0x80000000) {
// do not print anything
return S_OK;
}
}
dprintf("%c%p size: %4lx previous size: %4lx ",
c,
Pool,
BlockSize << POOL_BLOCK_SHIFT,
PreviousSize << POOL_BLOCK_SHIFT);
if (PoolType == 0) {
//
// "Free " with a space after it before the parentheses means
// it's been freed to a (pool manager internal) lookaside list.
// We used to print "Lookaside" but that just confused driver
// writers because they didn't know if this meant in use or not
// and many would say "but I don't use lookaside lists - the
// extension or kernel is broken".
//
// "Free" with no space after it before the parentheses means
// it is not on a pool manager internal lookaside list and is
// instead on the regular pool manager internal flink/blink
// chains.
//
// Note to anyone using the pool package, these 2 terms are
// equivalent. The fine distinction is only for those actually
// writing pool internal code.
//
dprintf(" (Free)");
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
dprintf(" %c%c%c%c%c\n",
c,
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP((PoolTag&~PROTECTED_POOL) >> 24)
);
#undef PP
if (c=='*') {
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
}
} else {
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
dprintf(" (Allocated)");
} else {
//
// "Free " with a space after it before the parentheses means
// it's been freed to a (pool manager internal) lookaside list.
// We used to print "Lookaside" but that just confused driver
// writers because they didn't know if this meant in use or not
// and many would say "but I don't use lookaside lists - the
// extension or kernel is broken".
//
// "Free" with no space after it before the parentheses means
// it is not on a pool manager internal lookaside list and is
// instead on the regular pool manager internal flink/blink
// chains.
//
// Note to anyone using the pool package, these 2 terms are
// equivalent. The fine distinction is only for those actually
// writing pool internal code.
//
dprintf(" (Free )");
}
if ((PoolType & POOL_QUOTA_MASK) == 0) {
/*
ULONG Key=0;
if (AllocatorBackTraceIndex != 0 &&
AllocatorBackTraceIndex & POOL_BACKTRACEINDEX_PRESENT
) {
if ( GetFieldValue( PoolTrackTable + ( PoolTagHash&~(PROTECTED_POOL >> 16) )*GetTypeSize("nt!_POOL_TRACKER_TABLE"),
"nt!_POOL_TRACKER_TABLE",
"Key",
Key) ) {
PoolTag = 0;
} else {
PoolTag = Key;
}
if (PoolTagHash & (PROTECTED_POOL >> 16)) {
PoolTag |= PROTECTED_POOL;
}
} else {
PoolTag = PoolTag;
}*/
dprintf(" %c%c%c%c%c%s\n",
c,
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(PoolTag),
PP(PoolTag >> 8),
PP(PoolTag >> 16),
PP((PoolTag&~PROTECTED_POOL) >> 24),
(PoolTag&PROTECTED_POOL) ? " (Protected)" : ""
#undef PP
);
if (c=='*') {
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
}
} else {
if (ProcessBilled != 0) {
dprintf(" Process: %0p\n", ProcessBilled );
}
}
}
}
if (Flags & 1) {
ULONG i, Contents[8];
// BUG if Contents have different size than 32 bits
ReadMemory(Pool + SizeOfPoolHdr,
&Contents,
sizeof(Contents),
&i);
dprintf(" %08lx %08lx %08lx %08lx %08lx\n",
Pool+SizeOfPoolHdr,
Contents[0],
Contents[1],
Contents[2],
Contents[3]);
dprintf(" %08lx %08lx %08lx %08lx %08lx\n",
Pool+SizeOfPoolHdr+16,
Contents[4],
Contents[5],
Contents[6],
Contents[7]);
dprintf("\n");
}
Previous = BlockSize;
Pool += (Previous << POOL_BLOCK_SHIFT);
FirstBlock = FALSE;
}
return S_OK;
}
DECLARE_API( pool )
/*++
Routine Description:
Dump kernel mode heap
Arguments:
args - Page Flags
Return Value:
None
--*/
{
ULONG64 PoolPageToDump;
ULONG Flags;
HRESULT Hr;
INIT_API();
if (PoolInitializeGlobals() == FALSE) {
Hr = E_INVALIDARG;
} else {
PoolPageToDump = 0;
Flags = 0;
if (GetExpressionEx(args, &PoolPageToDump, &args)) {
Flags = (ULONG) GetExpression (args);
}
if (PoolPageToDump == 0) {
DumpPool();
Hr = S_OK;;
} else {
Hr = ListPoolPage(PoolPageToDump, Flags, NULL);
}
}
EXIT_API();
return Hr;
}
DECLARE_API( poolused )
/*++
Routine Description:
Dump usage by pool tag
Arguments:
args -
Return Value:
None
--*/
{
ULONG PoolTrackTableSize;
ULONG PoolTrackTableSizeInBytes;
PULONG64 p;
PUCHAR PoolTrackTableData;
ULONG Flags;
ULONG i;
ULONG result;
ULONG ct;
ULONG TagName;
CHAR TagNameX[4] = {'*','*','*','*'};
ULONG SizeOfPoolTarker;
ULONG64 PoolTableAddress;
ULONG64 PoolTrackTable;
ULONG NonPagedAllocsTotal,NonPagedFreesTotal,PagedAllocsTotal,PagedFreesTotal;
ULONG64 NonPagedBytesTotal, PagedBytesTotal;
if (PoolInitializeGlobals() == FALSE) {
return E_INVALIDARG;
}
Flags = 0;
if (!sscanf(args,"%lx %c%c%c%c", &Flags, &TagNameX[0],
&TagNameX[1], &TagNameX[2], &TagNameX[3])) {
Flags = 0;
}
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
PoolTrackTableSize = GetUlongValue ("PoolTrackTableSize");
if (!(SizeOfPoolTarker = GetTypeSize("nt!_POOL_TRACKER_TABLE"))) {
dprintf("Unable to get _POOL_TRACKER_TABLE : probably wrong symbols.\n");
return E_INVALIDARG;
}
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
if (PoolTrackTable == 0) {
dprintf ("unable to get PoolTrackTable - ");
if (GetExpression("nt!PoolTrackTable")) {
dprintf ("pool tagging is disabled, enable it to use this command\n");
dprintf ("Use gflags.exe and check the box that says \"Enable pool tagging\".\n");
} else {
dprintf ("symbols could be worng\n");
}
return E_INVALIDARG;
}
PoolTrackTableSizeInBytes = PoolTrackTableSize * SizeOfPoolTarker;
PoolTrackTableData = malloc (PoolTrackTableSizeInBytes);
if (PoolTrackTableData == NULL) {
dprintf("unable to allocate memory for tag table.\n");
return E_INVALIDARG;
}
//
// KD is going to cache the data
//
PoolTableAddress = PoolTrackTable;
if ( !ReadMemory( PoolTableAddress,
&PoolTrackTableData[0],
PoolTrackTableSizeInBytes,
&result) ) {
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
free (PoolTrackTableData);
return E_INVALIDARG;
}
if (Flags & 2) {
SortBy = NONPAGED_USED;
dprintf(" Sorting by NonPaged Pool Consumed\n");
} else if (Flags & 4) {
SortBy = PAGED_USED;
dprintf(" Sorting by Paged Pool Consumed\n");
} else {
SortBy = TAG;
dprintf(" Sorting by Tag\n");
}
dprintf("\n Pool Used:\n");
if (!(Flags & 1)) {
dprintf(" NonPaged Paged\n");
dprintf(" Tag Allocs Used Allocs Used\n");
} else {
dprintf(" NonPaged Paged\n");
dprintf(" Tag Allocs Frees Diff Used Allocs Frees Diff Used\n");
}
ct = PageSize / SizeOfPoolTarker;
i = 0;
PoolTableAddress = PoolTrackTable;
free (PoolTrackTableData);
//
// Create array of POOL_TRACKER_TABLE addresses and sort the addresses
//
PoolTrackTableData = malloc (PoolTrackTableSize * sizeof(ULONG64));
if (PoolTrackTableData == NULL) {
dprintf("unable to allocate memory for tag table.\n");
return E_INVALIDARG;
}
while (i < PoolTrackTableSize) {
if ( CheckControlC() ) {
free (PoolTrackTableData);
return E_INVALIDARG;
}
((PULONG64) PoolTrackTableData)[i] = PoolTableAddress + i * SizeOfPoolTarker;
i++;
}
qsort((void *)PoolTrackTableData,
(size_t)PoolTrackTableSize,
(size_t)sizeof(ULONG64),
ulcomp);
i = 0;
p = (PULONG64) &PoolTrackTableData[i];
NonPagedAllocsTotal = 0;
NonPagedFreesTotal = 0;
NonPagedBytesTotal = 0;
PagedAllocsTotal = 0;
PagedFreesTotal = 0;
PagedBytesTotal = 0;
for ( ; i < PoolTrackTableSize; i += 1, p += 1) {
ULONG Key,NonPagedAllocs,NonPagedFrees,PagedAllocs,PagedFrees;
ULONG64 NonPagedBytes, PagedBytes;
#define TrackFld(F) GetFieldValue(*p, "nt!_POOL_TRACKER_TABLE", #F, F)
TrackFld(Key); TrackFld(NonPagedAllocs); TrackFld(NonPagedBytes);
TrackFld(PagedBytes); TrackFld(NonPagedFrees); TrackFld(PagedAllocs);
TrackFld(PagedFrees);
#undef TrackFld
if ((Key != 0) &&
(CheckSingleFilter ((PCHAR)&Key, (PCHAR)&TagName))) {
if (!(Flags & 1)) {
if ((NonPagedBytes != 0) || (PagedBytes != 0)) {
NonPagedAllocsTotal += NonPagedAllocs;
NonPagedFreesTotal += NonPagedFrees;
NonPagedBytesTotal += NonPagedBytes;
PagedAllocsTotal += PagedAllocs;
PagedFreesTotal += PagedFrees;
PagedBytesTotal += PagedBytes;
dprintf(" %c%c%c%c %8ld %8I64ld %8ld %8I64ld\n",
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(Key),
PP(Key >> 8),
PP(Key >> 16),
PP(Key >> 24),
NonPagedAllocs - NonPagedFrees,
NonPagedBytes,
PagedAllocs - PagedFrees,
PagedBytes);
}
} else {
NonPagedAllocsTotal += NonPagedAllocs;
NonPagedFreesTotal += NonPagedFrees;
NonPagedBytesTotal += NonPagedBytes;
PagedAllocsTotal += PagedAllocs;
PagedFreesTotal += PagedFrees;
PagedBytesTotal += PagedBytes;
dprintf(" %c%c%c%c %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
PP(Key),
PP(Key >> 8),
PP(Key >> 16),
PP(Key >> 24),
NonPagedAllocs,
NonPagedFrees,
NonPagedAllocs - NonPagedFrees,
NonPagedBytes,
PagedAllocs,
PagedFrees,
PagedAllocs - PagedFrees,
PagedBytes);
#undef PP
}
}
}
if (!(Flags & 1)) {
dprintf(" TOTAL %8ld %8I64ld %8ld %8I64ld\n",
NonPagedAllocsTotal - NonPagedFreesTotal,
NonPagedBytesTotal,
PagedAllocsTotal - PagedFreesTotal,
PagedBytesTotal);
} else {
dprintf(" TOTAL %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
NonPagedAllocsTotal,
NonPagedFreesTotal,
NonPagedAllocsTotal - NonPagedFreesTotal,
NonPagedBytesTotal,
PagedAllocsTotal,
PagedFreesTotal,
PagedAllocsTotal - PagedFreesTotal,
PagedBytesTotal);
}
free (PoolTrackTableData);
return S_OK;
}
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
BOOLEAN WINAPI
CheckSingleFilterAndPrint (
PCHAR Tag,
PCHAR Filter,
ULONG Flags,
ULONG64 PoolHeader,
ULONG BlockSize,
ULONG64 Data,
PVOID Context
)
/*++
Routine Description:
Callback to check a piece of pool and print out information about it
if it matches the specified tag.
Arguments:
Tag - Supplies the tag to search for.
Filter - Supplies the filter string to match against.
Flags - Supplies 0 if a nonpaged pool search is desired.
Supplies 1 if a paged pool search is desired.
Supplies 2 if a special pool search is desired.
Supplies 4 if a pool is a large pool
PoolHeader - Supplies the pool header.
BlockSize - Supplies the size of the pool block in bytes.
Data - Supplies the address of the pool block.
Context - Unused.
Return Value:
TRUE for a match, FALSE if not.
--*/
{
ULONG UTag = *((PULONG)Tag);
ULONG HdrUlong1=0, HdrPoolSize ;
UNREFERENCED_PARAMETER (Context);
if (CheckSingleFilter (Tag, Filter) == FALSE) {
return FALSE;
}
HdrPoolSize = GetTypeSize("nt!_POOL_HEADER");
if ((BlockSize >= (PageSize-2*HdrPoolSize)) || (Flags & 0x4)) {
dprintf("*%p :%slarge page allocation, Tag %3s %c%c%c%c, size %3s 0x%x bytes\n",
(Data & ~POOL_BIG_TABLE_ENTRY_FREE),
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
PP(UTag),
PP(UTag >> 8),
PP(UTag >> 16),
PP(UTag >> 24),
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
BlockSize
);
} else if (Flags & 0x2) {
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
Data,
BlockSize,
HdrUlong1 & MI_SPECIAL_POOL_PAGABLE ? "pagable" : "non-paged",
PP(UTag),
PP(UTag >> 8),
PP(UTag >> 16),
PP(UTag >> 24)
);
} else {
ULONG BlockSize, PreviousSize, PoolType, PoolIndex, AllocatorBackTraceIndex;
ULONG PoolTagHash, PoolTag;
ULONG64 ProcessBilled;
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", BlockSize);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
dprintf("%p size: %4lx previous size: %4lx ",
Data - HdrPoolSize,
BlockSize << POOL_BLOCK_SHIFT,
PreviousSize << POOL_BLOCK_SHIFT);
if (PoolType == 0) {
//
// "Free " with a space after it before the parentheses means
// it's been freed to a (pool manager internal) lookaside list.
// We used to print "Lookaside" but that just confused driver
// writers because they didn't know if this meant in use or not
// and many would say "but I don't use lookaside lists - the
// extension or kernel is broken".
//
// "Free" with no space after it before the parentheses means
// it is not on a pool manager internal lookaside list and is
// instead on the regular pool manager internal flink/blink
// chains.
//
// Note to anyone using the pool package, these 2 terms are
// equivalent. The fine distinction is only for those actually
// writing pool internal code.
//
dprintf(" (Free)");
dprintf(" %c%c%c%c\n",
PP(UTag),
PP(UTag >> 8),
PP(UTag >> 16),
PP(UTag >> 24)
);
} else {
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
dprintf(" (Allocated)");
} else {
//
// "Free " with a space after it before the parentheses means
// it's been freed to a (pool manager internal) lookaside list.
// We used to print "Lookaside" but that just confused driver
// writers because they didn't know if this meant in use or not
// and many would say "but I don't use lookaside lists - the
// extension or kernel is broken".
//
// "Free" with no space after it before the parentheses means
// it is not on a pool manager internal lookaside list and is
// instead on the regular pool manager internal flink/blink
// chains.
//
// Note to anyone using the pool package, these 2 terms are
// equivalent. The fine distinction is only for those actually
// writing pool internal code.
//
dprintf(" (Free )");
}
if ((PoolType & POOL_QUOTA_MASK) == 0) {
UTag = PoolTag;
dprintf(" %c%c%c%c%s\n",
PP(UTag),
PP(UTag >> 8),
PP(UTag >> 16),
PP((UTag &~PROTECTED_POOL) >> 24),
(UTag & PROTECTED_POOL) ? " (Protected)" : ""
);
} else {
if (ProcessBilled != 0) {
dprintf(" Process: %08p\n", ProcessBilled );
}
}
}
}
return TRUE;
} // CheckSingleFilterAndPrint
#undef PP
ULONG64
GetNextResidentAddress (
ULONG64 VirtualAddress,
ULONG64 MaximumVirtualAddress
)
{
ULONG64 PointerPde;
ULONG64 PointerPte;
ULONG SizeOfPte;
ULONG Valid;
//
// Note this code will need to handle one more level of indirection for
// WIN64.
//
if (!(SizeOfPte=GetTypeSize("nt!_MMPTE"))) {
dprintf("Cannot get MMPTE type.\n");
return 0;
}
top:
PointerPde = DbgGetPdeAddress (VirtualAddress);
while (GetFieldValue(PointerPde,
"nt!_MMPTE",
"u.Hard.Valid",
Valid) ||
(Valid == 0)) {
//
// Note that on 32-bit systems, the PDE should always be readable.
// If the PDE is not valid then increment to the next PDE's VA.
//
PointerPde = (PointerPde + SizeOfPte);
VirtualAddress = DbgGetVirtualAddressMappedByPte (PointerPde);
VirtualAddress = DbgGetVirtualAddressMappedByPte (VirtualAddress);
if (VirtualAddress >= MaximumVirtualAddress) {
return VirtualAddress;
}
if (CheckControlC()) {
return VirtualAddress;
}
continue;
}
PointerPte = DbgGetPteAddress (VirtualAddress);
while (GetFieldValue(PointerPde,
"nt!_MMPTE",
"u.Hard.Valid",
Valid) ||
(Valid == 0)) {
//
// If the PTE cannot be read then increment by PAGE_SIZE.
//
VirtualAddress = (VirtualAddress + PageSize);
if (CheckControlC()) {
return VirtualAddress;
}
PointerPte = (PointerPte + SizeOfPte);
if ((PointerPte & (PageSize - 1)) == 0) {
goto top;
}
if (VirtualAddress >= MaximumVirtualAddress) {
return VirtualAddress;
}
}
return VirtualAddress;
}
VOID
SearchPool(
ULONG TagName,
ULONG Flags,
ULONG64 RestartAddr,
POOLFILTER Filter,
PVOID Context
)
/*++
Routine Description:
Engine to search the pool.
Arguments:
TagName - Supplies the tag to search for.
Flags - Supplies 0 if a nonpaged pool search is desired.
Supplies 1 if a paged pool search is desired.
Supplies 2 if a special pool search is desired.
RestartAddr - Supplies the address to restart the search from.
Filter - Supplies the filter routine to use.
Context - Supplies the user defined context blob.
Return Value:
None.
--*/
{
LOGICAL PhysicallyContiguous;
ULONG PoolBlockSize;
ULONG64 PoolHeader;
ULONG PoolTag;
ULONG Result;
ULONG64 PoolPage;
ULONG64 StartPage;
ULONG64 Pool;
ULONG Previous;
ULONG64 PoolStart;
ULONG64 PoolPteAddress;
ULONG64 PoolEnd;
ULONG64 ExpandedPoolStart;
ULONG64 ExpandedPoolEnd;
ULONG InitialPoolSize;
ULONG SkipSize;
BOOLEAN TwoPools;
UCHAR DataPage[0x4000]; // MAX pzger size
ULONG64 DataPageReal;
ULONG64 DataStartReal;
LOGICAL Found;
ULONG i;
ULONG j;
ULONG ct;
ULONG PoolBigPageTableSize;
ULONG64 PoolTableAddress;
UCHAR FastTag[4];
ULONG TagLength;
ULONG SizeOfBigPages;
ULONG PoolTypeFlags = Flags & 0x3;
ULONG Ulong1;
ULONG HdrSize;
if (PoolInitializeGlobals() == FALSE) {
return;
}
if (PoolTypeFlags == 2) {
if (RestartAddr && (RestartAddr >= SpecialPoolStart) && (RestartAddr <= SpecialPoolEnd)) {
Pool = RestartAddr;
} else {
Pool = SpecialPoolStart;
}
dprintf("\nSearching special pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
Pool,
SpecialPoolEnd,
TagName,
TagName >> 8,
TagName >> 16,
TagName >> 24);
Found = FALSE;
SkipSize = PageSize;
if (SpecialPoolStart && SpecialPoolEnd) {
//
// Search special pool for the tag.
//
while (Pool < SpecialPoolEnd) {
if ( CheckControlC() ) {
dprintf("\n...terminating - searched pool to %p\n",
Pool);
return;
}
DataStartReal = Pool;
DataPageReal = Pool;
if ( !ReadMemory( Pool,
&DataPage[0],
min(PageSize, sizeof(DataPage)),
&Result) ) {
ULONG64 PteLong=0, PageFileHigh;
if (SkipSize != 2 * PageSize) {
// dprintf("SP skip %x", Pool);
PoolPteAddress = DbgGetPteAddress (Pool);
if (!GetFieldValue(PoolPteAddress,
"nt!_MMPTE",
"u.Soft.PageFileHigh",
PageFileHigh) ) {
if ((PageFileHigh == 0) ||
(PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE) ||
(PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE)) {
//
// Found a NO ACCESS PTE - skip these from
// here on to speed up the search.
//
// dprintf("SP skip double %p", PoolPteAddress);
SkipSize = 2 * PageSize;
Pool += PageSize;
// dprintf("SP skip final %p", Pool);
continue;
}
}
}
Pool += SkipSize;
continue;
}
//
// Determine whether this is a valid special pool block.
//
PoolHeader = GetSpecialPoolHeader (DataPage,
DataPageReal,
&DataStartReal);
if (PoolHeader != 0) {
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Ulong1);
PoolBlockSize = (ULONG) SPECIAL_POOL_BLOCK_SIZE(Ulong1);
Found = Filter( (PCHAR)&PoolTag,
(PCHAR)&TagName,
Flags,
PoolHeader,
PoolBlockSize,
DataStartReal,
Context );
} else {
dprintf( "No pool header for page: 0x%p\n", Pool );
}
Pool += SkipSize;
}
}
if (Found == FALSE) {
dprintf("The %c%c%c%c tag could not be found in special pool.\n",
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
PP(TagName),
PP(TagName >> 8),
PP(TagName >> 16),
PP(TagName >> 24)
);
#undef PP
}
return;
}
if (PoolTypeFlags == 0) {
PhysicallyContiguous = TRUE;
} else {
PhysicallyContiguous = FALSE;
}
__try {
TwoPools = FALSE;
if (!PoolBigTableAddress) {
PoolBigTableAddress = GetPointerValue ("PoolBigPageTable");
}
PoolTableAddress = PoolBigTableAddress;
if (PoolTableAddress) {
ULONG VaOffset;
ULONG NumPagesOffset;
ULONG PtrSize;
ULONG KeyOffset;
PoolBigPageTableSize = GetUlongValue ("PoolBigPageTableSize");
//
// Scan the table looking for a match. We read close to a page at a time
// physical page / sizeof ( pool_tracker_big_page ) * sizeof ( pool_tracker_big_page )
// on x86 this works out to ffc
//
i = 0;
SizeOfBigPages = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
if (!SizeOfBigPages) {
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
return;
}
ct = PageSize / SizeOfBigPages;
dprintf( "\nScanning large pool allocation table for Tag: %c%c%c%c (%p : %p)\n\n\r",
TagName,
TagName >> 8,
TagName >> 16,
TagName >> 24,
PoolBigTableAddress,
PoolBigTableAddress + PoolBigPageTableSize * SizeOfBigPages );
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Va", &VaOffset );
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "NumberOfPages", &NumPagesOffset );
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Key", &KeyOffset );
PtrSize = IsPtr64() ? 8 : 4;
while (i < PoolBigPageTableSize) {
if (PoolBigPageTableSize - i < ct) {
ct = PoolBigPageTableSize - i;
}
if ( !ReadMemory( PoolTableAddress,
&DataPage[0],
ct * SizeOfBigPages,
&Result) ) {
dprintf( "%08lx: Unable to get contents of big pool block\r\n", PoolTableAddress );
break;
}
for (j = 0; j < ct; j += 1) {
ULONG64 Va = 0;
memcpy( &Va, (PCHAR)DataPage + (SizeOfBigPages * j) + VaOffset, PtrSize );
Filter( ((PCHAR)DataPage + (SizeOfBigPages * j) + KeyOffset),
(PCHAR)&TagName,
Flags | 0x4, // To assist filter routine to recognize this as large pool
PoolTableAddress + SizeOfBigPages * j,
(*((PULONG)((PCHAR)DataPage + (SizeOfBigPages * j) + NumPagesOffset))) * PageSize,
Va,
Context );
if ( CheckControlC() ) {
dprintf("\n...terminating - searched pool to %p\n",
PoolTableAddress + j * SizeOfBigPages);
return;
}
}
i += ct;
PoolTableAddress += (ct * SizeOfBigPages);
if ( CheckControlC() ) {
dprintf("\n...terminating - searched pool to %p\n",
PoolTableAddress);
return;
}
}
} else {
dprintf("unable to get large pool allocation table - either wrong symbols or pool tagging is disabled\n");
}
if (PoolTypeFlags == 0) {
PoolStart = GetNtDebuggerDataPtrValue( MmNonPagedPoolStart );
if (0 == PoolStart) {
dprintf( "Unable to get MmNonPagedPoolStart\n" );
}
PoolEnd =
PoolStart + GetNtDebuggerDataValue( MmMaximumNonPagedPoolInBytes );
ExpandedPoolEnd = GetNtDebuggerDataPtrValue( MmNonPagedPoolEnd );
if (PoolEnd != ExpandedPoolEnd) {
InitialPoolSize = (ULONG)GetUlongValue( "MmSizeOfNonPagedPoolInBytes" );
PoolEnd = PoolStart + InitialPoolSize;
ExpandedPoolStart = GetPointerValue( "MmNonPagedPoolExpansionStart" );
TwoPools = TRUE;
}
for (TagLength = 0;TagLength < 3; TagLength++) {
if ((*(((PCHAR)&TagName)+TagLength) == '?') ||
(*(((PCHAR)&TagName)+TagLength) == '*')) {
break;
}
FastTag[TagLength] = *(((PCHAR)&TagName)+TagLength);
}
} else {
PoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
PoolEnd =
PoolStart + GetNtDebuggerDataValue( MmSizeOfPagedPoolInBytes );
}
if (RestartAddr) {
PoolStart = RestartAddr;
if (TwoPools == TRUE) {
if (PoolStart > PoolEnd) {
TwoPools = FALSE;
PoolStart = RestartAddr;
PoolEnd = ExpandedPoolEnd;
}
}
}
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
(PoolTypeFlags == 0) ? "NonPaged" : "Paged",
PoolStart,
PoolEnd,
TagName,
TagName >> 8,
TagName >> 16,
TagName >> 24);
PoolPage = PoolStart;
HdrSize = GetTypeSize("nt!_POOL_HEADER");
while (PoolPage < PoolEnd) {
//
// Optimize things by ioctl'ing over to the other side to
// do a fast search and start with that page.
//
if ((PoolTypeFlags == 0) &&
PhysicallyContiguous &&
(TagLength > 0)) {
SEARCHMEMORY Search;
Search.SearchAddress = PoolPage;
Search.SearchLength = PoolEnd-PoolPage;
Search.PatternLength = TagLength;
Search.Pattern = &FastTag;
Search.FoundAddress = 0;
if ((Ioctl(IG_SEARCH_MEMORY, &Search, sizeof(Search))) &&
(Search.FoundAddress != 0)) {
//
// Got a hit, search the whole page
//
PoolPage = PAGE_ALIGN64(Search.FoundAddress);
} else {
//
// The tag was not found at all, so we can just skip
// this chunk entirely.
//
PoolPage = PoolEnd;
goto skiprange;
}
}
Pool = PAGE_ALIGN64 (PoolPage);
StartPage = Pool;
Previous = 0;
while (PAGE_ALIGN64(Pool) == StartPage) {
ULONG HdrPoolTag, BlockSize, PreviousSize, AllocatorBackTraceIndex, PoolTagHash;
ULONG PoolType;
if ( GetFieldValue(Pool,
"nt!_POOL_HEADER",
"PoolTag",
HdrPoolTag) ) {
PoolPage = GetNextResidentAddress (Pool, PoolEnd);
//
// If we're half resident - half non-res then we'll get back
// that are starting address is the next resident page. In that
// case just go on to the next page
//
if (PoolPage == Pool) {
PoolPage = PoolPage + PageSize;
}
goto nextpage;
}
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTag",HdrPoolTag);
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolType", PoolType);
GetFieldValue(Pool,"nt!_POOL_HEADER","BlockSize",BlockSize);
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTagHash",PoolTagHash);
GetFieldValue(Pool,"nt!_POOL_HEADER","PreviousSize",PreviousSize);
GetFieldValue(Pool,"nt!_POOL_HEADER","AllocatorBackTraceIndex",AllocatorBackTraceIndex);
if ((BlockSize << POOL_BLOCK_SHIFT) > POOL_PAGE_SIZE) {
//dprintf("Bad allocation size @%lx, too large\n", Pool);
break;
}
if (BlockSize == 0) {
//dprintf("Bad allocation size @%lx, zero is invalid\n", Pool);
break;
}
if (PreviousSize != Previous) {
//dprintf("Bad previous allocation size @%lx, last size was %lx\n",Pool, Previous);
break;
}
PoolTag = HdrPoolTag;
Filter((PCHAR)&PoolTag,
(PCHAR)&TagName,
Flags,
Pool,
BlockSize << POOL_BLOCK_SHIFT,
Pool + HdrSize,
Context );
Previous = BlockSize;
Pool += (Previous << POOL_BLOCK_SHIFT);
if ( CheckControlC() ) {
dprintf("\n...terminating - searched pool to %p\n",
PoolPage);
return;
}
}
PoolPage = (PoolPage + PageSize);
nextpage:
if ( CheckControlC() ) {
dprintf("\n...terminating - searched pool to %p\n",
PoolPage);
return;
}
skiprange:
if (TwoPools == TRUE) {
if (PoolPage == PoolEnd) {
TwoPools = FALSE;
PoolStart = ExpandedPoolStart;
PoolEnd = ExpandedPoolEnd;
PoolPage = PoolStart;
PhysicallyContiguous = FALSE;
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\n\n",
"NonPaged",
PoolStart,
PoolEnd,
TagName,
TagName >> 8,
TagName >> 16,
TagName >> 24);
}
}
}
} __finally {
}
return;
} // SearchPool
DECLARE_API( poolfind )
/*++
Routine Description:
flags == 0 means finds a tag in nonpaged pool.
flags == 1 means finds a tag in paged pool.
flags == 2 means finds a tag in special pool.
Arguments:
args -
Return Value:
None
--*/
{
ULONG Flags;
CHAR TagNameX[4] = {' ',' ',' ',' '};
ULONG TagName;
ULONG64 PoolTrackTable;
Flags = 0;
if (!sscanf(args,"%c%c%c%c %lx", &TagNameX[0],
&TagNameX[1], &TagNameX[2], &TagNameX[3], &Flags)) {
Flags = 0;
}
if (TagNameX[0] == '0' && TagNameX[1] == 'x') {
if (!sscanf( args, "%lx %lx", &TagName, &Flags )) {
TagName = 0;
}
} else {
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
}
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
if (PoolTrackTable == 0) {
dprintf ("unable to get PoolTrackTable - probably pool tagging disabled or wrong symbols\n");
}
SearchPool( TagName, Flags, 0, CheckSingleFilterAndPrint, NULL );
return S_OK;
}
BOOLEAN
CheckSingleFilter (
PCHAR Tag,
PCHAR Filter
)
{
ULONG i;
UCHAR tc;
UCHAR fc;
for ( i = 0; i < 4; i++ ) {
tc = (UCHAR) *Tag++;
fc = (UCHAR) *Filter++;
if ( fc == '*' ) return TRUE;
if ( fc == '?' ) continue;
if (i == 3 && (tc & ~(PROTECTED_POOL >> 24)) == fc) continue;
if ( tc != fc ) return FALSE;
}
return TRUE;
}
ULONG64
GetSpecialPoolHeader (
IN PVOID DataPage,
IN ULONG64 RealDataPage,
OUT PULONG64 ReturnedDataStart
)
/*++
Routine Description:
Examine a page of data to determine if it is a special pool block.
Arguments:
DataPage - Supplies a pointer to a page of data to examine.
ReturnedDataStart - Supplies a pointer to return the start of the data.
Only valid if this routine returns non-NULL.
Return Value:
Returns a pointer to the pool header for this special pool block or
NULL if the block is not valid special pool.
--*/
{
ULONG PoolBlockSize;
ULONG PoolHeaderSize;
ULONG PoolBlockPattern;
PUCHAR p;
PUCHAR PoolDataEnd;
PUCHAR DataStart;
ULONG64 PoolHeader;
ULONG HdrUlong1;
PoolHeader = RealDataPage;
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
//
// Determine whether the data is at the start or end of the page.
// Start off by assuming the data is at the end, in this case the
// header will be at the start.
//
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
PoolHeaderSize = POOL_OVERHEAD;
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
PoolHeaderSize += GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
}
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
DataStart = (PUCHAR)DataPage + PageSize - PoolBlockSize;
p = (PUCHAR)DataPage + PoolHeaderSize;
for ( ; p < DataStart; p += 1) {
if (*p != PoolBlockPattern) {
break;
}
}
if (p == DataStart || p >= (PUCHAR)DataPage + PoolHeaderSize + 0x10) {
//
// For this page, the data is at the end of the block.
// The 0x10 is just to give corrupt blocks some slack.
// All pool allocations are quadword aligned.
//
DataStart = (PUCHAR)DataPage + ((PageSize - PoolBlockSize) & ~(sizeof(QUAD)-1));
*ReturnedDataStart = RealDataPage + (ULONG64) ((PUCHAR) DataStart - (PUCHAR) DataPage);
return PoolHeader;
}
//
// The data must be at the front or the block is corrupt.
//
}
//
// Try for the data at the front. Checks are necessary as
// the page could be corrupt on both ends.
//
PoolHeader = (RealDataPage + PageSize - POOL_OVERHEAD);
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
PoolDataEnd = (PUCHAR)PoolHeader;
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
PoolDataEnd -= GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
}
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
DataStart = (PUCHAR)DataPage;
p = DataStart + PoolBlockSize;
for ( ; p < PoolDataEnd; p += 1) {
if (*p != PoolBlockPattern) {
break;
}
}
if (p == (PUCHAR)PoolDataEnd || p > (PUCHAR)DataPage + PoolBlockSize + 0x10) {
//
// For this page, the data is at the front of the block.
// The 0x10 is just to give corrupt blocks some slack.
// All pool allocations are quadword aligned.
//
*ReturnedDataStart = RealDataPage + (ULONG64)( (PUCHAR)DataStart - (PUCHAR) DataPage);
return PoolHeader;
}
}
//
// Not valid special pool.
//
return 0;
}
#define BYTE(u,n) ((u & (0xff << 8*n)) >> 8*n)
#define LOCHAR_BYTE(u,n) (tolower(BYTE(u,n)) & 0xff)
#define REVERSE_ULONGBYTES(u) (LOCHAR_BYTE(u,3) | (LOCHAR_BYTE(u,2) << 8) | (LOCHAR_BYTE(u,1) << 16) | (LOCHAR_BYTE(u,0) << 24))
PSTR
GetNextLine(
HANDLE hFile
)
// Returns next line in the file hFile
// Returns NULL if EOF is reached
{
static CHAR FileLines1[MAX_PATH] = {0}, FileLines2[MAX_PATH] = {0};
static CHAR FileLine[MAX_PATH];
PCHAR pEOL;
ULONG BytesRead;
PCHAR pEndOfBuff;
ULONG BuffLen, ReadLen;
pEOL = NULL;
if (!(pEOL = strchr(FileLines1, '\n'))) {
// We have something that was already read but it isn't enough for a whole line
// We need to read the data
BuffLen = strlen(FileLines1);
// sanity check
if (BuffLen >= sizeof(FileLines1)) {
return NULL;
}
pEndOfBuff = &FileLines1[0] + BuffLen;
ReadLen = sizeof(FileLines1) - BuffLen;
ZeroMemory(pEndOfBuff, ReadLen);
if (ReadFile(hFile, pEndOfBuff, ReadLen - 1, &BytesRead, NULL)) {
pEOL = strchr(FileLines1, '\n');
}
}
if (pEOL) {
FileLine[0] = 0;
strncat(FileLine,FileLines1, (ULONG) (pEOL - &FileLines1[0]));
strcpy(FileLines2, pEOL+1);
strcpy(FileLines1, FileLines2);
return FileLine;
}
return NULL;
}
EXTENSION_API ( GetPoolRegion )(
PDEBUG_CLIENT Client,
ULONG64 Pool,
DEBUG_POOL_REGION *PoolData
)
{
INIT_API();
*PoolData = GetPoolRegion(Pool);
EXIT_API();
return S_OK;
}
EXTENSION_API ( GetPoolData )(
PDEBUG_CLIENT Client,
ULONG64 Pool,
PDEBUG_POOL_DATA PoolData
)
{
PCHAR Desc;
HRESULT Hr;
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
INIT_API();
if (!PoolInitializeGlobals()) {
EXIT_API();
return E_INVALIDARG;
}
Hr = ListPoolPage(Pool, 0x80000002, PoolData);
if (Hr != S_OK) {
EXIT_API();
return Hr;
}
GetPoolTagDescription = NULL;
#ifndef _EXTFNS_H
if (!GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription)) {
EXIT_API();
return E_INVALIDARG;
}
(*GetPoolTagDescription)(PoolData->PoolTag, &Desc);
if (Desc) {
ULONG strsize = strlen(Desc);
if (strsize > sizeof(PoolData->PoolTagDescription)) {
strsize = sizeof(PoolData->PoolTagDescription);
}
strncpy(PoolData->PoolTagDescription, Desc, strsize);
PoolData->PoolTagDescription[strsize] = 0;
}
#endif
EXIT_API();
return Hr;
}