Author: sir_richard
Date: Sat Feb 25 23:14:37 2012
New Revision: 55872
URL: 
http://svn.reactos.org/svn/reactos?rev=55872&view=rev
Log:
[NTOS]: Implement pool tag tracking and pool tag querying. Big Pool tags are not yet
supported, but will be shortly. Expansion is not yet supported.
Modified:
    trunk/reactos/ntoskrnl/ex/sysinfo.c
    trunk/reactos/ntoskrnl/include/internal/ex.h
    trunk/reactos/ntoskrnl/mm/ARM3/expool.c
    trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
Modified: trunk/reactos/ntoskrnl/ex/sysinfo.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ex/sysinfo.c?rev=…
==============================================================================
--- trunk/reactos/ntoskrnl/ex/sysinfo.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/ex/sysinfo.c [iso-8859-1] Sat Feb 25 23:14:37 2012
@@ -1239,9 +1239,8 @@
 /* Class 22 - Pool Tag Information */
 QSI_DEF(SystemPoolTagInformation)
 {
-    /* FIXME */
-    DPRINT1("NtQuerySystemInformation - SystemPoolTagInformation not
implemented\n");
-    return STATUS_NOT_IMPLEMENTED;
+    if (Size < sizeof(SYSTEM_POOLTAG_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
+    return ExGetPoolTagInfo(Buffer, Size, ReqSize);
 }
 /* Class 23 - Interrupt Information for all processors */
Modified: trunk/reactos/ntoskrnl/include/internal/ex.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/ex.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/include/internal/ex.h [iso-8859-1] Sat Feb 25 23:14:37 2012
@@ -130,6 +130,14 @@
 #define ExpChangePushlock(x, y, z) InterlockedCompareExchangePointer((PVOID*)x, (PVOID)y,
(PVOID)z)
 #define ExpSetRundown(x, y) InterlockedExchangePointer(&x->Ptr, (PVOID)y)
+NTSTATUS
+NTAPI
+ExGetPoolTagInfo(
+    IN PSYSTEM_POOLTAG_INFORMATION SystemInformation,
+    IN ULONG SystemInformationLength,
+    IN OUT PULONG ReturnLength OPTIONAL
+);
+
 /* INITIALIZATION FUNCTIONS *************************************************/
 VOID
Modified: trunk/reactos/ntoskrnl/mm/ARM3/expool.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/expool.c?…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/expool.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/expool.c [iso-8859-1] Sat Feb 25 23:14:37 2012
@@ -20,6 +20,14 @@
 /* GLOBALS ********************************************************************/
+typedef struct _POOL_DPC_CONTEXT
+{
+    PPOOL_TRACKER_TABLE PoolTrackTable;
+    SIZE_T PoolTrackTableSize;
+    PPOOL_TRACKER_TABLE PoolTrackTableExpansion;
+    SIZE_T PoolTrackTableSizeExpansion;
+} POOL_DPC_CONTEXT, *PPOOL_DPC_CONTEXT;
+
 ULONG ExpNumberOfPagedPools;
 POOL_DESCRIPTOR NonPagedPoolDescriptor;
 PPOOL_DESCRIPTOR ExpPagedPoolDescriptor[16 + 1];
@@ -27,8 +35,11 @@
 PKGUARDED_MUTEX ExpPagedPoolMutex;
 SIZE_T PoolTrackTableSize, PoolTrackTableMask;
 SIZE_T PoolBigPageTableSize, PoolBigPageTableHash;
-PPOOL_TRACKER_TABLE PoolTrackTable, PoolBigPageTable;
+PPOOL_TRACKER_TABLE PoolTrackTable;
+PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;
 KSPIN_LOCK ExpTaggedPoolLock;
+ULONG PoolHitTag;
+BOOLEAN ExStopBadTags;
 /* Pool block/header/list access macros */
 #define POOL_ENTRY(x)       (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
@@ -286,7 +297,344 @@
     }
 }
+FORCEINLINE
+ULONG
+ExpComputeHashForTag(IN ULONG Tag,
+                     IN SIZE_T BucketMask)
+{
+    //
+    // Compute the hash by multiplying with a large prime number and then XORing
+    // with the HIDWORD of the result.
+    //
+    // Finally, AND with the bucket mask to generate a valid index/bucket into
+    // the table
+    //
+    ULONGLONG Result = 40543 * Tag;
+    return BucketMask & (Result ^ (Result >> 32));
+}
+
 /* PRIVATE FUNCTIONS **********************************************************/
+
+VOID
+NTAPI
+INIT_FUNCTION
+ExpSeedHotTags(VOID)
+{
+    ULONG i, Key, Hash, Index;
+    PPOOL_TRACKER_TABLE TrackTable = PoolTrackTable;
+    ULONG TagList[] =
+    {
+        '  oI',
+        ' laH',
+        'PldM',
+        'LooP',
+        'tSbO',
+        ' prI',
+        'bdDN',
+        'LprI',
+        'pOoI',
+        ' ldM',
+        'eliF',
+        'aVMC',
+        'dSeS',
+        'CFtN',
+        'looP',
+        'rPCT',
+        'bNMC',
+        'dTeS',
+        'sFtN',
+        'TPCT',
+        'CPCT',
+        ' yeK',
+        'qSbO',
+        'mNoI',
+        'aEoI',
+        'cPCT',
+        'aFtN',
+        '0ftN',
+        'tceS',
+        'SprI',
+        'ekoT',
+        '  eS',
+        'lCbO',
+        'cScC',
+        'lFtN',
+        'cAeS',
+        'mfSF',
+        'kWcC',
+        'miSF',
+        'CdfA',
+        'EdfA',
+        'orSF',
+        'nftN',
+        'PRIU',
+        'rFpN',
+        'RFpN',
+        'aPeS',
+        'sUeS',
+        'FpcA',
+        'MpcA',
+        'cSeS',
+        'mNbO',
+        'sFpN',
+        'uLeS',
+        'DPcS',
+        'nevE',
+        'vrqR',
+        'ldaV',
+        '  pP',
+        'SdaV',
+        ' daV',
+        'LdaV',
+        'FdaV',
+        ' GIB',
+    };
+
+    //
+    // Loop all 64 hot tags
+    //
+    ASSERT((sizeof(TagList) / sizeof(ULONG)) == 64);
+    for (i = 0; i < sizeof(TagList) / sizeof(ULONG); i++)
+    {
+        //
+        // Get the current tag, and compute its hash in the tracker table
+        //
+        Key = TagList[i];
+        Hash = ExpComputeHashForTag(Key, PoolTrackTableMask);
+
+        //
+        // Loop all the hashes in this index/bucket
+        //
+        Index = Hash;
+        while (TRUE)
+        {
+            //
+            // Find an empty entry, and make sure this isn't the last hash that
+            // can fit.
+            //
+            // On checked builds, also make sure this is the first time we are
+            // seeding this tag.
+            //
+            ASSERT(TrackTable[Hash].Key != Key);
+            if (!(TrackTable[Hash].Key) && (Hash != PoolTrackTableSize - 1))
+            {
+                //
+                // It has been seeded, move on to the next tag
+                //
+                TrackTable[Hash].Key = Key;
+                break;
+            }
+
+            //
+            // This entry was already taken, compute the next possible hash while
+            // making sure we're not back at our initial index.
+            //
+            ASSERT(TrackTable[Hash].Key != Key);
+            Hash = (Hash + 1) & PoolTrackTableMask;
+            if (Hash == Index) break;
+        }
+    }
+}
+
+VOID
+NTAPI
+ExpRemovePoolTracker(IN ULONG Key,
+                     IN SIZE_T NumberOfBytes,
+                     IN POOL_TYPE PoolType)
+{
+    ULONG Hash, Index;
+    PPOOL_TRACKER_TABLE Table, TableEntry;
+    SIZE_T TableMask, TableSize;
+
+    //
+    // Remove the PROTECTED_POOL flag which is not part of the tag
+    //
+    Key &= ~PROTECTED_POOL;
+
+    //
+    // With WinDBG you can set a tag you want to break on when an allocation is
+    // attempted
+    //
+    if (Key == PoolHitTag) DbgBreakPoint();
+
+    //
+    // Why the double indirection? Because normally this function is also used
+    // when doing session pool allocations, which has another set of tables,
+    // sizes, and masks that live in session pool. Now we don't support session
+    // pool so we only ever use the regular tables, but I'm keeping the code this
+    // way so that the day we DO support session pool, it won't require that
+    // many changes
+    //
+    Table = PoolTrackTable;
+    TableMask = PoolTrackTableMask;
+    TableSize = PoolTrackTableSize;
+
+    //
+    // Compute the hash for this key, and loop all the possible buckets
+    //
+    Hash = ExpComputeHashForTag(Key, TableMask);
+    Index = Hash;
+    while (TRUE)
+    {
+        //
+        // Have we found the entry for this tag? */
+        //
+        TableEntry = &Table[Hash];
+        if (TableEntry->Key == Key)
+        {
+            //
+            // Decrement the counters depending on if this was paged or nonpaged
+            // pool
+            //
+            if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool)
+            {
+                InterlockedIncrement(&TableEntry->NonPagedFrees);
+                InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes,
-NumberOfBytes);
+                return;
+            }
+            InterlockedIncrement(&TableEntry->PagedFrees);
+            InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, -NumberOfBytes);
+            return;
+        }
+
+        //
+        // We should have only ended up with an empty entry if we've reached
+        // the last bucket
+        //
+        if (!TableEntry->Key) ASSERT(Hash == TableMask);
+
+        //
+        // This path is hit when we don't have an entry, and the current bucket
+        // is full, so we simply try the next one
+        //
+        Hash = (Hash + 1) & TableMask;
+        if (Hash == Index) break;
+    }
+
+    //
+    // And finally this path is hit when all the buckets are full, and we need
+    // some expansion. This path is not yet supported in ReactOS and so we'll
+    // ignore the tag
+    //
+    DPRINT1("Out of pool tag space, ignoring...\n");
+}
+
+VOID
+NTAPI
+ExpInsertPoolTracker(IN ULONG Key,
+                     IN SIZE_T NumberOfBytes,
+                     IN POOL_TYPE PoolType)
+{
+    ULONG Hash, Index;
+    KIRQL OldIrql;
+    PPOOL_TRACKER_TABLE Table, TableEntry;
+    SIZE_T TableMask, TableSize;
+
+    //
+    // Remove the PROTECTED_POOL flag which is not part of the tag
+    //
+    Key &= ~PROTECTED_POOL;
+
+    //
+    // With WinDBG you can set a tag you want to break on when an allocation is
+    // attempted
+    //
+    if (Key == PoolHitTag) DbgBreakPoint();
+
+    //
+    // There is also an internal flag you can set to break on malformed tags
+    //
+    if (ExStopBadTags) ASSERT(Key & 0xFFFFFF00);
+
+    //
+    // ASSERT on ReactOS features not yet supported
+    //
+    ASSERT(!(PoolType & SESSION_POOL_MASK));
+    ASSERT(KeGetCurrentProcessorNumber() == 0);
+
+    //
+    // Why the double indirection? Because normally this function is also used
+    // when doing session pool allocations, which has another set of tables,
+    // sizes, and masks that live in session pool. Now we don't support session
+    // pool so we only ever use the regular tables, but I'm keeping the code this
+    // way so that the day we DO support session pool, it won't require that
+    // many changes
+    //
+    Table = PoolTrackTable;
+    TableMask = PoolTrackTableMask;
+    TableSize = PoolTrackTableSize;
+
+    //
+    // Compute the hash for this key, and loop all the possible buckets
+    //
+    Hash = ExpComputeHashForTag(Key, TableMask);
+    Index = Hash;
+    while (TRUE)
+    {
+        //
+        // Do we already have an entry for this tag? */
+        //
+        TableEntry = &Table[Hash];
+        if (TableEntry->Key == Key)
+        {
+            //
+            // Increment the counters depending on if this was paged or nonpaged
+            // pool
+            //
+            if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool)
+            {
+                InterlockedIncrement(&TableEntry->NonPagedAllocs);
+                InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes,
NumberOfBytes);
+                return;
+            }
+            InterlockedIncrement(&TableEntry->PagedAllocs);
+            InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, NumberOfBytes);
+            return;
+        }
+
+        //
+        // We don't have an entry yet, but we've found a free bucket for it
+        //
+        if (!(TableEntry->Key) && (Hash != PoolTrackTableSize - 1))
+        {
+            //
+            // We need to hold the lock while creating a new entry, since other
+            // processors might be in this code path as well
+            //
+            ExAcquireSpinLock(&ExpTaggedPoolLock, &OldIrql);
+            if (!PoolTrackTable[Hash].Key)
+            {
+                //
+                // We've won the race, so now create this entry in the bucket
+                //
+                ASSERT(Table[Hash].Key == 0);
+                PoolTrackTable[Hash].Key = Key;
+                TableEntry->Key = Key;
+            }
+            ExReleaseSpinLock(&ExpTaggedPoolLock, OldIrql);
+
+            //
+            // Now we force the loop to run again, and we should now end up in
+            // the code path above which does the interlocked increments...
+            //
+            continue;
+        }
+
+        //
+        // This path is hit when we don't have an entry, and the current bucket
+        // is full, so we simply try the next one
+        //
+        Hash = (Hash + 1) & TableMask;
+        if (Hash == Index) break;
+    }
+
+    //
+    // And finally this path is hit when all the buckets are full, and we need
+    // some expansion. This path is not yet supported in ReactOS and so we'll
+    // ignore the tag
+    //
+    DPRINT1("Out of pool tag space, ignoring...\n");
+}
 VOID
 NTAPI
@@ -499,6 +847,7 @@
         PoolBigPageTableHash = PoolBigPageTableSize - 1;
         RtlZeroMemory(PoolBigPageTable,
                       PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
+        for (i = 0; i < PoolBigPageTableSize; i++) PoolBigPageTable[i].Va = (PVOID)1;
         //
         // During development, print this out so we can see what's happening
@@ -507,6 +856,14 @@
                 PoolTrackTable, PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE));
         DPRINT1("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
                 PoolBigPageTable, PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
+
+        //
+        // Insert the generic tracker for all of big pool
+        //
+        ExpInsertPoolTracker('looP',
+                             ROUND_TO_PAGES(PoolBigPageTableSize *
+                                            sizeof(POOL_TRACKER_BIG_PAGES)),
+                             NonPagedPool);
         //
         // No support for NUMA systems at this time
@@ -565,6 +922,13 @@
                                    0,
                                    Threshold,
                                    ExpPagedPoolMutex);
+
+        //
+        // Insert the generic tracker for all of nonpaged pool
+        //
+        ExpInsertPoolTracker('looP',
+                             ROUND_TO_PAGES(PoolTrackTableSize *
sizeof(POOL_TRACKER_TABLE)),
+                             NonPagedPool);
     }
 }
@@ -614,6 +978,146 @@
         //
         KeReleaseGuardedMutex(Descriptor->LockAddress);
     }
+}
+
+VOID
+NTAPI
+ExpGetPoolTagInfoTarget(IN PKDPC Dpc,
+                        IN PVOID DeferredContext,
+                        IN PVOID SystemArgument1,
+                        IN PVOID SystemArgument2)
+{
+    PPOOL_DPC_CONTEXT Context = DeferredContext;
+    UNREFERENCED_PARAMETER(Dpc);
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+    //
+    // Make sure we win the race, and if we did, copy the data atomically
+    //
+    if (KeSignalCallDpcSynchronize(SystemArgument2))
+    {
+        RtlCopyMemory(Context->PoolTrackTable,
+                      PoolTrackTable,
+                      Context->PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE));
+
+        //
+        // This is here because ReactOS does not yet support expansion
+        //
+        ASSERT(Context->PoolTrackTableSizeExpansion == 0);
+    }
+
+    //
+    // Regardless of whether we won or not, we must now synchronize and then
+    // decrement the barrier since this is one more processor that has completed
+    // the callback.
+    //
+    KeSignalCallDpcSynchronize(SystemArgument2);
+    KeSignalCallDpcDone(SystemArgument1);
+}
+
+NTSTATUS
+NTAPI
+ExGetPoolTagInfo(IN PSYSTEM_POOLTAG_INFORMATION SystemInformation,
+                 IN ULONG SystemInformationLength,
+                 IN OUT PULONG ReturnLength OPTIONAL)
+{
+    SIZE_T TableSize, CurrentLength;
+    ULONG EntryCount;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PSYSTEM_POOLTAG TagEntry;
+    PPOOL_TRACKER_TABLE Buffer, TrackerEntry;
+    POOL_DPC_CONTEXT Context;
+    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+    //
+    // Keep track of how much data the caller's buffer must hold
+    //
+    CurrentLength = FIELD_OFFSET(SYSTEM_POOLTAG_INFORMATION, TagInfo);
+
+    //
+    // Initialize the caller's buffer
+    //
+    TagEntry = &SystemInformation->TagInfo[0];
+    SystemInformation->Count = 0;
+
+    //
+    // Capture the number of entries, and the total size needed to make a copy
+    // of the table
+    //
+    EntryCount = PoolTrackTableSize;
+    TableSize = EntryCount * sizeof(POOL_TRACKER_TABLE);
+
+    //
+    // Allocate the "Generic DPC" temporary buffer
+    //
+    Buffer = ExAllocatePoolWithTag(NonPagedPool, TableSize, 'ofnI');
+    if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
+
+    //
+    // Do a "Generic DPC" to atomically retrieve the tag and allocation data
+    //
+    Context.PoolTrackTable = Buffer;
+    Context.PoolTrackTableSize = PoolTrackTableSize;
+    Context.PoolTrackTableExpansion = NULL;
+    Context.PoolTrackTableSizeExpansion = 0;
+    KeGenericCallDpc(ExpGetPoolTagInfoTarget, &Context);
+
+    //
+    // Now parse the results
+    //
+    for (TrackerEntry = Buffer; TrackerEntry < (Buffer + EntryCount); TrackerEntry++)
+    {
+        //
+        // If the entry is empty, skip it
+        //
+        if (!TrackerEntry->Key) continue;
+
+        //
+        // Otherwise, add one more entry to the caller's buffer, and ensure that
+        // enough space has been allocated in it
+        //
+        SystemInformation->Count++;
+        CurrentLength += sizeof(*TagEntry);
+        if (SystemInformationLength < CurrentLength)
+        {
+            //
+            // The caller's buffer is too small, so set a failure code. The
+            // caller will know the count, as well as how much space is needed.
+            //
+            // We do NOT break out of the loop, because we want to keep incrementing
+            // the Count as well as CurrentLength so that the caller can know the
+            // final numbers
+            //
+            Status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            //
+            // Small sanity check that our accounting is working correctly
+            //
+            ASSERT(TrackerEntry->PagedAllocs >= TrackerEntry->PagedFrees);
+            ASSERT(TrackerEntry->NonPagedAllocs >= TrackerEntry->NonPagedFrees);
+
+            //
+            // Return the data into the caller's buffer
+            //
+            TagEntry->TagUlong = TrackerEntry->Key;
+            TagEntry->PagedAllocs = TrackerEntry->PagedAllocs;
+            TagEntry->PagedFrees = TrackerEntry->PagedFrees;
+            TagEntry->PagedUsed = TrackerEntry->PagedBytes;
+            TagEntry->NonPagedAllocs = TrackerEntry->NonPagedAllocs;
+            TagEntry->NonPagedFrees = TrackerEntry->NonPagedFrees;
+            TagEntry->NonPagedUsed = TrackerEntry->NonPagedBytes;
+            TagEntry++;
+        }
+    }
+
+    //
+    // Free the "Generic DPC" temporary buffer, return the buffer length and
status
+    //
+    ExFreePool(Buffer);
+    if (ReturnLength) *ReturnLength = CurrentLength;
+    return Status;
 }
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -852,6 +1356,13 @@
             ExUnlockPool(PoolDesc, OldIrql);
             //
+            // Track this allocation
+            //
+            ExpInsertPoolTracker(Tag,
+                                 Entry->BlockSize * POOL_BLOCK_SIZE,
+                                 PoolType);
+
+            //
             // Return the pool allocation
             //
             Entry->PoolTag = Tag;
@@ -865,8 +1376,7 @@
     // There were no free entries left, so we have to allocate a new fresh page
     //
     Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE);
-    if (Entry == NULL)
-        return NULL;
+    if (Entry == NULL) return NULL;
     Entry->Ulong1 = 0;
     Entry->BlockSize = i;
@@ -912,6 +1422,13 @@
     }
     //
+    // Track this allocation
+    //
+    ExpInsertPoolTracker(Tag,
+                         Entry->BlockSize * POOL_BLOCK_SIZE,
+                         PoolType);
+
+    //
     // And return the pool allocation
     //
     ExpCheckPoolBlocks(Entry);
@@ -946,6 +1463,7 @@
     KIRQL OldIrql;
     POOL_TYPE PoolType;
     PPOOL_DESCRIPTOR PoolDesc;
+    ULONG Tag;
     BOOLEAN Combined = FALSE;
     //
@@ -983,6 +1501,19 @@
     BlockSize = Entry->BlockSize;
     PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK;
     PoolDesc = PoolVector[PoolType];
+
+    //
+    // Get the pool tag and get rid of the PROTECTED_POOL flag
+    //
+    Tag = Entry->PoolTag;
+    if (Tag & PROTECTED_POOL) Tag &= ~PROTECTED_POOL;
+
+    //
+    // Stop tracking this allocation
+    //
+    ExpRemovePoolTracker(Tag,
+                         BlockSize * POOL_BLOCK_SIZE,
+                         Entry->PoolType - 1);
     //
     // Get the pointer to the next entry
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?r…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Sat Feb 25 23:14:37 2012
@@ -348,11 +348,11 @@
 typedef struct _POOL_TRACKER_TABLE
 {
     ULONG Key;
-    ULONG NonPagedAllocs;
-    ULONG NonPagedFrees;
+    LONG NonPagedAllocs;
+    LONG NonPagedFrees;
     SIZE_T NonPagedBytes;
-    ULONG PagedAllocs;
-    ULONG PagedFrees;
+    LONG PagedAllocs;
+    LONG PagedFrees;
     SIZE_T PagedBytes;
 } POOL_TRACKER_TABLE, *PPOOL_TRACKER_TABLE;