Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4522 lines
129 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
session.cxx
Abstract:
This file contains the routines to handle session.
Author:
Jason Hartman (JasonHa) 2000-09-28
Environment:
User Mode
--*/
#include "precomp.h"
#define STRSAFE_NO_DEPRECATE
#include "strsafe.h"
//
// Special defines
//
// ddk\inc\ntddk.h:
#define PROTECTED_POOL 0x80000000
// base\ntos\inc\pool.h:
#define POOL_QUOTA_MASK 8
// Information about how to handle a process listing
// and status about how it was handled.
class CProcessListing
{
public:
IN ULONG m_cListLimit;
IN OUT ULONG m_cTotal;
IN OUT ULONG m_cProcessed;
IN OUT ULONG64 m_oStartProcess;
OUT ULONG64 m_oLastProcess;
public:
CProcessListing(ULONG cListLimit = -1) {
m_cListLimit = cListLimit;
m_cTotal = 0;
m_cProcessed = 0;
m_oStartProcess = 0;
m_oLastProcess = 0;
}
void PrepareForNextListing()
{
m_oStartProcess = m_oLastProcess;
}
BOOL Unprocessed()
{
return (m_cTotal > m_cProcessed);
}
};
#define SESSION_SEARCH_LIMIT 50
ULONG SessionId = CURRENT_SESSION;
CHAR SessionStr[16] = "CURRENT";
CachedType HwPte = { FALSE, "NT!HARDWARE_PTE", 0, 0, 0 };
#define NUM_CACHED_SESSIONS 8
struct {
BOOL Valid;
ULONG64 SessionSpaceAddr;
ULONG64 SessionProcess;
} CachedSession[NUM_CACHED_SESSIONS+1] = { { 0, 0 } };
ULONG ExtraCachedSessionId;
#define NUM_CACHED_DIR_BASES 8
struct {
BOOL Valid;
ULONG64 PageDirBase;
} CachedDirBase[NUM_CACHED_DIR_BASES+1] = { { FALSE, 0} };
struct {
BOOL Valid;
ULONG64 PhysAddr;
ULONG64 Data;
} CachedPhysAddr[2] = { { 0, 0, 0} };
class BitFieldInfo {
public:
BitFieldInfo() { Valid = FALSE; };
BitFieldInfo(ULONG InitBitPos, ULONG InitBits) {
Valid = Compose(InitBitPos, InitBits);
}
BOOL Compose(ULONG CBitPos, ULONG CBits)
{
BitPos = CBitPos;
Bits = CBits;
Mask = (((((ULONG64) 1) << Bits) - 1) << BitPos);
return TRUE;
}
BOOL Valid;
ULONG BitPos;
ULONG Bits;
ULONG64 Mask;
};
BitFieldInfo *MMPTEValid = NULL;
BitFieldInfo *MMPTEProto = NULL;
BitFieldInfo *MMPTETrans = NULL;
BitFieldInfo *MMPTEX86LargePage = NULL;
BitFieldInfo *MMPTEpfn = NULL;
HRESULT
GetBitMap(
PDEBUG_CLIENT Client,
ULONG64 pBitMap,
PRTL_BITMAP *pBitMapOut
);
HRESULT
FreeBitMap(
PRTL_BITMAP pBitMap
);
HRESULT
OutputSessionProcesses(
PDEBUG_CLIENT Client,
ULONG Session,
PCSTR args,
CProcessListing *pProcessListing
);
/**************************************************************************\
*
* Routine Name:
*
* SessionInit
*
* Routine Description:
*
* Initialize or reinitialize information to be read from symbols files
*
* Arguments:
*
* Client - PDEBUG_CLIENT
*
* Return Value:
*
* none
*
\**************************************************************************/
void SessionInit(PDEBUG_CLIENT Client)
{
for (int s = 0; s < sizeof(CachedSession)/sizeof(CachedSession[0]); s++)
{
CachedSession[s].Valid = FALSE;
}
ExtraCachedSessionId = INVALID_SESSION;
for (int s = 0; s < sizeof(CachedDirBase)/sizeof(CachedDirBase[0]); s++)
{
CachedDirBase[s].Valid = INVALID_UNIQUE_STATE;
}
if (MMPTEValid != NULL) MMPTEValid->Valid = FALSE;
if (MMPTEProto != NULL) MMPTEProto->Valid = FALSE;
if (MMPTETrans != NULL) MMPTETrans->Valid = FALSE;
if (MMPTEX86LargePage != NULL) MMPTEX86LargePage->Valid = FALSE;
if (MMPTEpfn != NULL) MMPTEpfn->Valid = FALSE;
CachedPhysAddr[0].Valid = FALSE;
CachedPhysAddr[1].Valid = FALSE;
return;
}
/**************************************************************************\
*
* Routine Name:
*
* SessionExit
*
* Routine Description:
*
* Clean up any outstanding allocations or references
*
* Arguments:
*
* none
*
* Return Value:
*
* none
*
\**************************************************************************/
void SessionExit()
{
if (MMPTEValid != NULL)
{
delete MMPTEValid;
MMPTEValid = NULL;
}
if (MMPTEProto != NULL)
{
delete MMPTEProto;
MMPTEProto = NULL;
}
if (MMPTETrans != NULL)
{
delete MMPTETrans;
MMPTETrans = NULL;
}
if (MMPTEX86LargePage != NULL)
{
delete MMPTEX86LargePage;
MMPTEX86LargePage = NULL;
}
if (MMPTEpfn != NULL)
{
delete MMPTEpfn;
MMPTEpfn = NULL;
}
return;
}
#if 0
/**************************************************************************\
*
* Routine Name:
*
* GetMMPTEValid
*
* Routine Description:
*
* Extract Valid value from MMPTE
*
\**************************************************************************/
HRESULT
GetMMPTEValid(
PDEBUG_CLIENT Client,
ULONG64 MMPTE64,
PULONG64 Valid
)
{
HRESULT hr = S_FALSE;
if (MMPTEValid == NULL)
{
MMPTEValid = new BitFieldInfo;
}
if (MMPTEValid == NULL)
{
hr = E_OUTOFMEMORY;
}
else if (MMPTEValid->Valid)
{
hr = S_OK;
}
else
{
ULONG VaildOffset, ValidBits;
if (GetBitFieldOffset("nt!HARDWARE_PTE", "Valid", &VaildOffset, &ValidBits) == S_OK)
{
MMPTEValid->Valid = MMPTEValid->Compose(VaildOffset, ValidBits);
hr = MMPTEValid->Valid ? S_OK : S_FALSE;
}
}
if (Valid != NULL)
{
if (hr == S_OK)
{
*Valid = (MMPTE64 & MMPTEValid->Mask) >> MMPTEValid->BitPos;
}
else
{
*Valid = 0;
}
}
return hr;
}
/**************************************************************************\
*
* Routine Name:
*
* GetMMPTEProto
*
* Routine Description:
*
* Extract Prototype value from MMPTE
*
\**************************************************************************/
HRESULT
GetMMPTEProto(
PDEBUG_CLIENT Client,
ULONG64 MMPTE64,
PULONG64 Proto
)
{
HRESULT hr = S_FALSE;
if (MMPTEProto == NULL)
{
MMPTEProto = new BitFieldInfo;
}
if (MMPTEProto == NULL)
{
hr = E_OUTOFMEMORY;
}
else if (MMPTEProto->Valid)
{
hr = S_OK;
}
else
{
ULONG ProtoOffset, ProtoBits;
if (GetBitFieldOffset("nt!MMPTE_PROTOTYPE", "Prototype", &ProtoOffset, &ProtoBits) == S_OK)
{
MMPTEProto->Valid = MMPTEProto->Compose(ProtoOffset, ProtoBits);
hr = MMPTEProto->Valid ? S_OK : S_FALSE;
}
}
if (Proto != NULL)
{
if (hr == S_OK)
{
*Proto = (MMPTE64 & MMPTEProto->Mask) >> MMPTEProto->BitPos;
}
else
{
*Proto = 0;
}
}
return hr;
}
/**************************************************************************\
*
* Routine Name:
*
* GetMMPTETrans
*
* Routine Description:
*
* Extract Transition value from MMPTE
*
\**************************************************************************/
HRESULT
GetMMPTETrans(
PDEBUG_CLIENT Client,
ULONG64 MMPTE64,
PULONG64 Trans
)
{
HRESULT hr = S_FALSE;
if (MMPTETrans == NULL)
{
MMPTETrans = new BitFieldInfo;
}
if (MMPTETrans == NULL)
{
hr = E_OUTOFMEMORY;
}
else if (MMPTETrans->Valid)
{
hr = S_OK;
}
else
{
ULONG TransOffset, TransBits;
if (GetBitFieldOffset("nt!MMPTE_PROTOTYPE", "Transition", &TransOffset, &TransBits) == S_OK)
{
MMPTETrans->Valid = MMPTETrans->Compose(TransOffset, TransBits);
hr = MMPTETrans->Valid ? S_OK : S_FALSE;
}
}
if (Trans != NULL)
{
if (hr == S_OK)
{
*Trans = (MMPTE64 & MMPTETrans->Mask) >> MMPTETrans->BitPos;
}
else
{
*Trans = 0;
}
}
return hr;
}
/**************************************************************************\
*
* Routine Name:
*
* GetMMPTEX86LargePage
*
* Routine Description:
*
* Extract LargePage value from X86 MMPTE
*
\**************************************************************************/
HRESULT
GetMMPTEX86LargePage(
PDEBUG_CLIENT Client,
ULONG64 MMPTE64,
PULONG64 X86LargePage
)
{
HRESULT hr = S_FALSE;
if (TargetMachine != IMAGE_FILE_MACHINE_I386)
{
if (X86LargePage != NULL) *X86LargePage = 0;
return hr;
}
if (MMPTEX86LargePage == NULL)
{
MMPTEX86LargePage = new BitFieldInfo;
}
if (MMPTEX86LargePage == NULL)
{
hr = E_OUTOFMEMORY;
}
else if (MMPTEX86LargePage->Valid)
{
hr = S_OK;
}
else
{
ULONG LrPgOffset, LrPgBits;
if (GetBitFieldOffset("nt!HARDWARE_PTE", "LargePage", &LrPgOffset, &LrPgBits) == S_OK)
{
MMPTEX86LargePage->Valid = MMPTEX86LargePage->Compose(LrPgOffset, LrPgBits);
hr = MMPTEX86LargePage->Valid ? S_OK : S_FALSE;
}
}
if (X86LargePage != NULL)
{
if (hr == S_OK)
{
*X86LargePage = (MMPTE64 & MMPTEX86LargePage->Mask) >> MMPTEX86LargePage->BitPos;
}
else
{
*X86LargePage = 0;
}
}
return hr;
}
/**************************************************************************\
*
* Routine Name:
*
* GetMMPTEpfn
*
* Routine Description:
*
* Extract Page Frame Number value from MMPTE
*
\**************************************************************************/
#define GET_BITS_UNSHIFTED 1
HRESULT
GetMMPTEpfn(
PDEBUG_CLIENT Client,
ULONG64 MMPTE64,
PULONG64 pfn,
FLONG Flags
)
{
HRESULT hr = S_FALSE;
if (MMPTEpfn == NULL)
{
MMPTEpfn = new BitFieldInfo;
}
if (MMPTEpfn == NULL)
{
hr = E_OUTOFMEMORY;
}
else if (MMPTEpfn->Valid)
{
hr = S_OK;
}
else
{
ULONG pfnPosition, pfnBits;
if (GetBitFieldOffset("nt!HARDWARE_PTE", "PageFrameNumber", &pfnPosition, &pfnBits) == S_OK)
{
MMPTEpfn->Valid = MMPTEpfn->Compose(pfnPosition, pfnBits);
hr = MMPTEpfn->Valid ? S_OK : S_FALSE;
}
}
if (pfn != NULL)
{
if (hr == S_OK)
{
*pfn = (MMPTE64 & MMPTEpfn->Mask);
if (!(Flags & GET_BITS_UNSHIFTED))
{
*pfn >>= MMPTEpfn->BitPos;
}
}
else
{
*pfn = 0;
}
}
return hr;
}
#endif // 0
// Copied from nt\base\ntos\rtl\bitmap.c
static CONST ULONG FillMaskUlong[] = {
0x00000000, 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff
};
ULONG
OSCompat_RtlFindLastBackwardRunClear (
IN PRTL_BITMAP BitMapHeader,
IN ULONG FromIndex,
IN PULONG StartingRunIndex
)
{
ULONG Start;
ULONG End;
PULONG PHunk;
ULONG Hunk;
//
// Take care of the boundary case of the null bitmap
//
if (BitMapHeader->SizeOfBitMap == 0) {
*StartingRunIndex = FromIndex;
return 0;
}
//
// Scan backwards for the first clear bit
//
End = FromIndex;
//
// Build pointer to the ULONG word in the bitmap
// containing the End bit, then read in the bitmap
// hunk. Set the rest of the bits in this word, NOT
// inclusive of the FromIndex bit.
//
PHunk = BitMapHeader->Buffer + (End / 32);
Hunk = *PHunk | ~FillMaskUlong[(End % 32) + 1];
//
// If the first subword is set then we can proceed to
// take big steps in the bitmap since we are now ULONG
// aligned in the search
//
if (Hunk == (ULONG)~0) {
//
// Adjust the pointers backwards
//
End -= (End % 32) + 1;
PHunk--;
while ( PHunk > BitMapHeader->Buffer ) {
//
// Stop at first word with set bits
//
if (*PHunk != (ULONG)~0) break;
PHunk--;
End -= 32;
}
}
//
// Bitwise search backward for the clear bit
//
while ((End != MAXULONG) && (RtlCheckBit( BitMapHeader, End ) == 1)) { End -= 1; }
//
// Scan backwards for the first set bit
//
Start = End;
//
// We know that the clear bit was in the last word we looked at,
// so continue from there to find the next set bit, clearing the
// previous bits in the word.
//
Hunk = *PHunk & FillMaskUlong[Start % 32];
//
// If the subword is unset then we can proceed in big steps
//
if (Hunk == (ULONG)0) {
//
// Adjust the pointers backward
//
Start -= (Start % 32) + 1;
PHunk--;
while ( PHunk > BitMapHeader->Buffer ) {
//
// Stop at first word with set bits
//
if (*PHunk != (ULONG)0) break;
PHunk--;
Start -= 32;
}
}
//
// Bitwise search backward for the set bit
//
while ((Start != MAXULONG) && (RtlCheckBit( BitMapHeader, Start ) == 0)) { Start -= 1; }
//
// Compute the index and return the length
//
*StartingRunIndex = Start + 1;
return (End - Start);
}
HRESULT
GetSessionNumbers(
IN PDEBUG_CLIENT Client,
OUT PULONG CurrentSession,
OUT PULONG DefaultSession,
OUT PULONG TotalSessions,
OUT PRTL_BITMAP *SessionList
)
{
HRESULT hr = S_FALSE;
if (CurrentSession != NULL)
{
ULONG Processor;
ULONG64 Process=0;
Process = GetExpression("@$Proc");
if (!Process || !GetProcessSessionId(Process, CurrentSession))
{
*CurrentSession = INVALID_SESSION;
} else
{
hr = S_OK;
}
}
if (DefaultSession != NULL)
{
*DefaultSession = SessionId;
hr = S_OK;
}
if ((TotalSessions != NULL) ||
(SessionList != NULL))
{
ULONG SessionCount = 0;
PRTL_BITMAP SessionListBitMap = NULL;
ULONG64 SessionIdListPointerAddr = 0;
ULONG64 SessionIdListAddr = 0;
PDEBUG_SYMBOLS Symbols;
PDEBUG_DATA_SPACES Data;
if (TotalSessions)
{
*TotalSessions = 0;
}
if (SessionList)
{
*SessionList = NULL;
}
if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols),
(void **)&Symbols)) != S_OK)
{
return hr;
}
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) != S_OK)
{
Symbols->Release();
return hr;
}
CHAR PointerName[] = "NT!MiSessionIdBitmap";
hr = Symbols->GetOffsetByName(PointerName, &SessionIdListPointerAddr);
if (hr != S_OK)
{
ExtErr("Unable to locate %s\n", PointerName);
}
else
{
hr = Data->ReadPointersVirtual(1, SessionIdListPointerAddr, &SessionIdListAddr);
if ((hr == S_OK) && (SessionIdListAddr != 0))
{
hr = GetBitMap(Client, SessionIdListAddr, &SessionListBitMap);
if (hr == S_OK)
{
SessionCount = RtlNumberOfSetBits(SessionListBitMap);
}
} else
{
ExtErr("Unable to read MiSessionIdBitmap @ %p\n", SessionIdListPointerAddr);
}
}
if (TotalSessions)
{
*TotalSessions = SessionCount;
}
// Free or return BitMap
if (SessionListBitMap)
{
if (SessionList)
{
*SessionList = SessionListBitMap;
}
else
{
FreeBitMap(SessionListBitMap);
}
}
Data->Release();
Symbols->Release();
}
return hr;
}
HRESULT
SetDefaultSession(
IN PDEBUG_CLIENT Client,
IN ULONG NewSession,
OUT OPTIONAL PULONG OldSession
)
{
HRESULT hr = S_FALSE;
ULONG64 SessionProcess;
ULONG PrevSession;
GetSessionNumbers(Client, NULL, &PrevSession, NULL, NULL);
if (OldSession)
{
*OldSession = PrevSession;
}
if ((NewSession == CURRENT_SESSION) ||
(GetSessionSpace(NewSession, NULL, &SessionProcess) == S_OK))
{
if (GetProcessSessionId(SessionProcess, &SessionId) &&
(SessionId != PrevSession))
{
CHAR SetImplicitProcess[100];
hr = StringCbPrintfA(SetImplicitProcess, sizeof(SetImplicitProcess),
".process %p", SessionProcess);
if (hr == S_OK)
{
hr = g_ExtControl->Execute(DEBUG_OUTCTL_AMBIENT,
SetImplicitProcess, DEBUG_EXECUTE_DEFAULT );
}
if (SessionId == CURRENT_SESSION)
{
strcpy(SessionStr, "CURRENT");
}
else
{
_ultoa(SessionId, SessionStr, 10);
}
}
hr = S_OK;
}
return hr;
}
typedef (WINAPI* PENUM_SESSION_CB)(ULONG64 Process, ULONG SessionId, PVOID Context);
BOOL
EnumerateSessionProcesses(
IN ULONG Session,
IN PVOID Context,
IN PENUM_SESSION_CB Callback
)
{
ULONG TotalSessions;
ULONG WsListEntryOffset;
ULONG SessionOffset;
ULONG SessionProcessLinksOffset;
ULONG64 MiSessionWsList;
ULONG64 SessionWsListStart;
ULONG64 Next;
ULONG64 SessionSpace;
ULONG SessionId;
MiSessionWsList = GetExpression("nt!MiSessionWsList");
if (!MiSessionWsList || !ReadPointer(MiSessionWsList, &SessionWsListStart))
{
dprintf("Cannot get nt!MiSessionWsList\n");
return FALSE;
}
if (GetFieldOffset("nt!_MM_SESSION_SPACE", "WsListEntry", &WsListEntryOffset) ||
GetFieldOffset("nt!_MM_SESSION_SPACE", "Session", &SessionOffset))
{
dprintf("Cannot find nt!_MM_SESSION_SPACE type.\n");
return FALSE;
}
if (GetFieldOffset("nt!_EPROCESS", "SessionProcessLinks", &SessionProcessLinksOffset))
{
dprintf("Cannot find nt!_EPROCESS type.\n");
return FALSE;
}
SessionSpace = SessionWsListStart-WsListEntryOffset;
do
{
if (GetFieldValue(SessionSpace, "nt!_MM_SESSION_SPACE", "SessionId", SessionId) ||
!ReadPointer(SessionSpace+WsListEntryOffset, &Next) ||
InitTypeRead(SessionSpace, nt!_MM_SESSION_SPACE) )
{
dprintf("Cannot read nt!_MM_SESSION_SPACE @ %p\n", SessionSpace);
return FALSE;
}
if (CheckControlC())
{
break;
}
if (Session == SessionId || Session == -1)
{
ULONG64 SessionProcessList = ReadField(ProcessList.Flink);
ULONG64 NextProcess = 0;
ULONG64 Process;
CHAR ImageFileName[64];
Process = SessionProcessList - SessionProcessLinksOffset;
if (!ReadPointer(SessionProcessList, &NextProcess))
{
dprintf("Cannot read memory SessionProcessLinks for rocess %p\n", Process);
NextProcess = 0;
break;
}
while (NextProcess && NextProcess != SessionProcessList)
{
if (CheckControlC())
{
break;
}
(*Callback)(Process, SessionId, Context);
Process = NextProcess - SessionProcessLinksOffset;
if (!ReadPointer(NextProcess, &NextProcess))
{
dprintf("Cannot read memory SessionProcessLinks for rocess %p\n", Process);
break;
}
}
}
SessionSpace = Next - WsListEntryOffset;
} while (Next && (Next != MiSessionWsList));
return TRUE;
}
BOOL
DumpSessionInfo(
IN ULONG Flags,
IN ULONG SessionIdToDump,
IN PCHAR ProcessName
)
{
ULONG TotalSessions;
ULONG WsListEntryOffset;
ULONG SessionOffset;
ULONG SessionProcessLinksOffset;
ULONG64 MiSessionWsList;
ULONG64 SessionWsListStart;
ULONG64 Next;
ULONG64 SessionSpace;
ULONG CurrentSessionId;
PCHAR Pad = " ";
if (SessionIdToDump == DEFAULT_SESSION)
{
SessionId = SessionId;
}
if (!(Flags & 1) && (SessionIdToDump == CURRENT_SESSION))
{
GetCurrentSession(&SessionSpace, &SessionIdToDump);
}
if (SessionIdToDump != -1)
{
dprintf("Dumping Session %lx\n", SessionIdToDump);
Pad = "";
}
else
{
PRTL_BITMAP pSessionIdBitmap;
ULONG Id;
pSessionIdBitmap = GetBitmap(GetPointerValue("nt!MiSessionIdBitmap"));
TotalSessions = 0;
if (pSessionIdBitmap)
{
for (Id = 0; Id < pSessionIdBitmap->SizeOfBitMap; ++Id)
{
if (RtlCheckBit(pSessionIdBitmap, Id))
{
TotalSessions++;
}
}
HeapFree( GetProcessHeap(), 0, pSessionIdBitmap );
}
if (TotalSessions)
{
dprintf("Total sessions : %lx\n", TotalSessions);
} else
{
// GetPointerValue already printed error, We might still be able to get
// MiSessionWsList so continue
}
}
MiSessionWsList = GetExpression("nt!MiSessionWsList");
if (!MiSessionWsList || !ReadPointer(MiSessionWsList, &SessionWsListStart))
{
dprintf("Cannot get nt!MiSessionWsList\n");
return FALSE;
}
if (GetFieldOffset("nt!_MM_SESSION_SPACE", "WsListEntry", &WsListEntryOffset) ||
GetFieldOffset("nt!_MM_SESSION_SPACE", "Session", &SessionOffset))
{
dprintf("Cannot find nt!_MM_SESSION_SPACE type.\n");
return FALSE;
}
if (GetFieldOffset("nt!_EPROCESS", "SessionProcessLinks", &SessionProcessLinksOffset))
{
dprintf("Cannot find nt!_EPROCESS type.\n");
return FALSE;
}
SessionSpace = SessionWsListStart-WsListEntryOffset;
do
{
if (GetFieldValue(SessionSpace, "nt!_MM_SESSION_SPACE", "SessionId", CurrentSessionId) ||
!ReadPointer(SessionSpace+WsListEntryOffset, &Next) ||
InitTypeRead(SessionSpace, nt!_MM_SESSION_SPACE) )
{
dprintf("Cannot read nt!_MM_SESSION_SPACE @ %p\n", SessionSpace);
return FALSE;
}
if (CheckControlC())
{
break;
}
if (SessionIdToDump == CurrentSessionId || SessionIdToDump == -1)
{
// Dump Session
dprintf("\n");
if (SessionIdToDump == -1)
{
dprintf("%sSession %lx\n", Pad, CurrentSessionId);
}
dprintf("%s_MM_SESSION_SPACE %p\n", Pad, SessionSpace);
dprintf("%s_MMSESSION %p\n", Pad, SessionSpace+SessionOffset);
if (Flags & 2)
{
// Dump Process in the session
ULONG64 SessionProcessList = ReadField(ProcessList.Flink);
ULONG64 NextProcess = 0;
ULONG64 Process;
CHAR ImageFileName[64];
Process = SessionProcessList - SessionProcessLinksOffset;
if (!ReadPointer(SessionProcessList, &NextProcess))
{
dprintf("Cannot read memory SessionProcessLinks for rocess %p\n", Process);
NextProcess = 0;
break;
}
while (NextProcess && NextProcess != SessionProcessList)
{
if (CheckControlC())
{
break;
}
ZeroMemory(ImageFileName, sizeof(ImageFileName));
if (ProcessName && *ProcessName)
{
if (!GetFieldValue(Process, "nt!_EPROCESS", "ImageFileName", ImageFileName) &&
!_stricmp(ImageFileName, ProcessName))
{
DumpProcess(Pad, Process, Flags >> 2, NULL);
}
} else
{
DumpProcess(Pad, Process, Flags >> 2, NULL);
}
Process = NextProcess - SessionProcessLinksOffset;
if (!ReadPointer(NextProcess, &NextProcess))
{
dprintf("Cannot read memory SessionProcessLinks for rocess %p\n", Process);
break;
}
}
}
}
SessionSpace = Next - WsListEntryOffset;
} while (Next && (Next != MiSessionWsList));
return TRUE;
}
HRESULT
GetCurrentSession(
PULONG64 CurSessionSpace,
PULONG CurSessionId
)
{
static DEBUG_VALUE LastSessionSpace = { 0, DEBUG_VALUE_INVALID };
static DEBUG_VALUE LastSessionId = { INVALID_SESSION, DEBUG_VALUE_INVALID };
ULONG64 SessionSpaceAddr;
HRESULT hr = S_OK;
ULONG CurrentSession;
if (CurSessionSpace != NULL) *CurSessionSpace = 0;
if (CurSessionId != NULL) *CurSessionId = INVALID_SESSION;
// Get the current session space
if (LastSessionSpace.Type == DEBUG_VALUE_INVALID)
{
ULONG Processor;
ULONG64 Process=0, Start;
ULONG SessionProcessLinksOffset;
// Get current process
Process = GetExpression("@$Proc");
Start = 0;
GetFieldOffset("nt!_EPROCESS", "SessionProcessLinks", &SessionProcessLinksOffset);
hr = S_FALSE;
if (!GetProcessSessionId(Process, &CurrentSession))
{
hr = E_FAIL;
} else
{
hr = GetFieldValue(Process, "nt!_EPROCESS", "Session", SessionSpaceAddr);
}
if ((hr != S_OK) || (SessionSpaceAddr == 0))
{
// This process doesn't belong to a session, look for a process is 0 session
hr = GetSessionSpace(0, &SessionSpaceAddr, NULL);
}
}
if (hr == S_OK)
{
if (CurSessionSpace != NULL) *CurSessionSpace = SessionSpaceAddr;
if (CurSessionId != NULL) *CurSessionId = CurrentSession;
}
return hr;
}
HRESULT
GetSessionSpace(
ULONG Session,
PULONG64 pSessionSpace,
PULONG64 pSessionProcess
)
{
ULONG TotalSessions;
ULONG WsListEntryOffset;
ULONG SessionOffset;
ULONG64 MiSessionWsList;
ULONG64 SessionWsListStart;
ULONG64 Next;
ULONG64 SessionSpace;
ULONG SessionIdLookup;
HRESULT hr;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if (Session == CURRENT_SESSION)
{
ULONG64 CurSessionSpace;
ULONG CurSessionId;
hr = GetCurrentSession(&SessionSpace, &SessionIdLookup);
if (hr == S_OK)
{
if (pSessionSpace)
{
*pSessionSpace = SessionSpace;
}
return hr;
}
}
else
{
if (Session < NUM_CACHED_SESSIONS)
{
if (CachedSession[Session].Valid &&
CachedSession[Session].SessionSpaceAddr != 0)
{
if (pSessionSpace != NULL) *pSessionSpace = CachedSession[Session].SessionSpaceAddr;
if (pSessionProcess != NULL) *pSessionProcess = CachedSession[Session].SessionProcess;
return S_OK;
}
}
else if (ExtraCachedSessionId != INVALID_SESSION &&
Session == ExtraCachedSessionId)
{
if (CachedSession[NUM_CACHED_SESSIONS].Valid &&
CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr != 0)
{
if (pSessionSpace != NULL) *pSessionSpace = CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr;
if (pSessionProcess != NULL) *pSessionProcess = CachedSession[NUM_CACHED_SESSIONS].SessionProcess;
return S_OK;
}
}
}
MiSessionWsList = GetExpression("nt!MiSessionWsList");
if (!MiSessionWsList || !ReadPointer(MiSessionWsList, &SessionWsListStart))
{
dprintf("Cannot get nt!MiSessionWsList\n");
return FALSE;
}
if (GetFieldOffset("nt!_MM_SESSION_SPACE", "WsListEntry", &WsListEntryOffset) ||
GetFieldOffset("nt!_MM_SESSION_SPACE", "Session", &SessionOffset))
{
dprintf("Cannot find nt!_MM_SESSION_SPACE type.\n");
return FALSE;
}
SessionSpace = SessionWsListStart-WsListEntryOffset;
do
{
if (GetFieldValue(SessionSpace, "nt!_MM_SESSION_SPACE", "SessionId", SessionIdLookup) ||
!ReadPointer(SessionSpace+WsListEntryOffset, &Next) ||
InitTypeRead(SessionSpace, nt!_MM_SESSION_SPACE) )
{
dprintf("Cannot read nt!_MM_SESSION_SPACE @ %p\n", SessionSpace);
return FALSE;
}
if (CheckControlC())
{
break;
}
if (Session == SessionIdLookup)
{
ULONG SessionProcessLinksOffset;
ULONG64 SessionProcessList = ReadField(ProcessList.Flink);
if (pSessionSpace)
{
*pSessionSpace = SessionSpace;
}
ExtVerb("Session %ld lookup found Session @ 0x%p.\n",
Session, SessionSpace);
if (GetFieldOffset("nt!_EPROCESS", "SessionProcessLinks", &SessionProcessLinksOffset))
{
dprintf("Cannot find nt!_EPROCESS type.\n");
return E_FAIL;
}
if (Session < NUM_CACHED_SESSIONS)
{
CachedSession[Session].Valid = TRUE;
CachedSession[Session].SessionSpaceAddr = SessionSpace;
CachedSession[Session].SessionProcess = SessionProcessList - SessionProcessLinksOffset;
}
else
{
ExtraCachedSessionId = Session;
CachedSession[NUM_CACHED_SESSIONS].Valid = TRUE;
CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr = SessionSpace;
CachedSession[NUM_CACHED_SESSIONS].SessionProcess = SessionProcessList - SessionProcessLinksOffset;
}
if (pSessionProcess)
{
*pSessionProcess = SessionProcessList - SessionProcessLinksOffset;
}
return S_OK;
}
SessionSpace = Next - WsListEntryOffset;
} while (Next && (Next != MiSessionWsList));
return E_FAIL;
}
HRESULT
GetSessionDirBase(
PDEBUG_CLIENT Client,
ULONG Session,
PULONG64 PageDirBase
)
{
HRESULT hr = S_FALSE;
ULONG64 SessionSpaceOffset;
ULONG64 Process = -1;
ULONG SessionIdCheck;
ULONG64 dvPageDirBase;
CHAR szCommand[MAX_PATH];
static ULONG LastSession = -2;
static ULONG64 LastSessionPageDirBase = 0;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if (Session == LastSession &&
LastSessionPageDirBase != 0)
{
*PageDirBase = LastSessionPageDirBase;
return S_OK;
}
*PageDirBase = 0;
if ((hr == GetSessionSpace(Session, &SessionSpaceOffset, NULL)) == S_OK)
{
ULONG SessionProcessLinksOffset;
if ((hr = GetFieldOffset("nt!_EPROCESS", "SessionProcessLinks", &SessionProcessLinksOffset)) == S_OK)
{
dprintf("Cannot find nt!_EPROCESS type.\n");
return E_FAIL;
}
ULONG64 SessionProcessListAddr;
if (GetFieldValue(SessionSpaceOffset, "nt!_MM_SESSION_SPACE",
"ProcessList.Flink", SessionProcessListAddr) == S_OK)
{
Process = SessionProcessListAddr - SessionProcessLinksOffset;
} else
{
dprintf("Cannot read nt!_MM_SESSION_SPACE @ %p\n", SessionSpaceOffset);
return E_FAIL;
}
}
else
{
dprintf("GetSessionSpace returned HRESULT 0x%lx.\n", hr);
}
if (GetFieldValue(Process, "nt!_KPROCESS", "DirectoryTableBase[0]", dvPageDirBase) == S_OK &&
GetProcessSessionId(Process, &SessionIdCheck) == S_OK)
{
*PageDirBase = dvPageDirBase;
if (Session != CURRENT_SESSION &&
Session != SessionIdCheck)
{
hr = S_FALSE;
}
else
{
LastSession = Session;
LastSessionPageDirBase = dvPageDirBase;
}
}
return hr;
}
#if 0
HRESULT
ReadPageTableEntry(
PDEBUG_DATA_SPACES Data,
ULONG64 PageTableBase,
ULONG64 PageTableIndex,
PULONG64 PageTableEntry
)
{
HRESULT hr;
ULONG64 PhysAddr = PageTableBase + PageTableIndex * HwPte.Size;
ULONG BytesRead;
*PageTableEntry = 0;
if (CachedPhysAddr[0].Valid &&
CachedPhysAddr[0].PhysAddr == PhysAddr)
{
*PageTableEntry = CachedPhysAddr[0].Data;
return S_OK;
}
else if (CachedPhysAddr[1].Valid &&
CachedPhysAddr[1].PhysAddr == PhysAddr)
{
*PageTableEntry = CachedPhysAddr[1].Data;
return S_OK;
}
hr = Data->ReadPhysical(PhysAddr,
PageTableEntry,
HwPte.Size,
&BytesRead);
if (hr == S_OK)
{
if (BytesRead < HwPte.Size)
{
hr = S_FALSE;
}
else
{
static CacheToggle = 1;
CacheToggle = (CacheToggle+1) % 2;
CachedPhysAddr[CacheToggle].Valid = TRUE;
CachedPhysAddr[CacheToggle].PhysAddr = *PageTableEntry;
}
}
return hr;
}
HRESULT
GetPageFrameNumber(
PDEBUG_CLIENT Client,
PDEBUG_DATA_SPACES Data,
ULONG64 PageTableBase,
ULONG64 PageTableIndex,
PULONG64 PageFrameNumber,
PBOOL Large
)
{
HRESULT hr;
ULONG64 PageTableEntry;
ULONG64 Valid, Proto, Trans, LargePage;
ULONG64 pfn;
if ((hr = ReadPageTableEntry(Data, PageTableBase, PageTableIndex, &PageTableEntry)) == S_OK)
{
if ((hr = GetMMPTEValid(Client, PageTableEntry, &Valid)) == S_OK)
{
if (Valid)
{
hr = GetMMPTEpfn(Client, PageTableEntry, PageFrameNumber, GET_BITS_UNSHIFTED);
if (hr == S_OK)
{
if (GetMMPTEX86LargePage(Client, PageTableEntry, &LargePage) == S_OK &&
LargePage != 0)
{
// Large pages map 4MB of space - there shouldn't
// be any bits set below that.
if (*PageFrameNumber & (4*1024*1024 - 1))
{
#if DBG
DbgPrint("Found large X86 page with bad frame number.\n");
DbgBreakPoint();
#endif
}
if (Large == NULL)
{
#if DBG
DbgPrint("Unexpected large X86 page found.\n");
DbgBreakPoint();
#endif
}
else
{
*Large = TRUE;
}
}
else if (Large != NULL)
{
*Large = FALSE;
}
}
}
else
{
if ((hr = GetMMPTEProto(Client, PageTableEntry, &Proto)) == S_OK &&
(hr = GetMMPTETrans(Client, PageTableEntry, &Trans)) == S_OK)
{
if (Proto == 0 && Trans == 1)
{
hr = GetMMPTEpfn(Client, PageTableEntry, PageFrameNumber, GET_BITS_UNSHIFTED);
}
else
{
hr = S_FALSE;
}
}
}
}
}
return hr;
}
HRESULT
GetNextResidentPage(
PDEBUG_CLIENT Client,
ULONG64 PageDirBase,
ULONG64 VirtAddrStart,
ULONG64 VirtAddrLimit,
PULONG64 VirtPage,
PULONG64 PhysPage
)
{
HRESULT hr;
BOOL Interrupted = FALSE;
PDEBUG_CONTROL Control = NULL;
PDEBUG_DATA_SPACES Data = NULL;
ULONG64 PageDirIndex;
ULONG64 PageTableIndex;
ULONG64 PageDirIndexLimit;
ULONG64 PageTableIndexLimit;
ULONG64 PageTableBase;
BOOL LargePage;
ULONG64 TempAddr;
if (VirtPage == NULL) VirtPage = &TempAddr;
if (PhysPage == NULL) PhysPage = &TempAddr;
*VirtPage = 0;
*PhysPage = 0;
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) == S_OK &&
(hr = Client->QueryInterface(__uuidof(IDebugControl),
(void **)&Control)) == S_OK)
{
if (!HwPte.Valid)
{
PDEBUG_SYMBOLS Symbols;
if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols),
(void **)&Symbols)) == S_OK)
{
if ((hr = Symbols->GetSymbolTypeId(HwPte.Type, &HwPte.TypeId, &HwPte.Module)) == S_OK &&
(hr = Symbols->GetTypeSize(HwPte.Module, HwPte.TypeId, &HwPte.Size)) == S_OK &&
HwPte.Size != 0)
{
HwPte.Valid = TRUE;
}
else if (hr == S_OK)
{
hr = E_FAIL;
}
Symbols->Release();
}
}
if (HwPte.Valid)
{
ULONG TableEntries = PageSize / HwPte.Size;
ULONG64 Addr;
*VirtPage = PAGE_ALIGN64(VirtAddrStart);
Addr = VirtAddrStart >> PageShift;
PageTableIndex = Addr % TableEntries;
PageDirIndex = (Addr / TableEntries) % TableEntries;
Addr = VirtAddrLimit >> PageShift;
PageTableIndexLimit = Addr % TableEntries;
PageDirIndexLimit = (Addr / TableEntries) % TableEntries;
if (VirtAddrLimit & (PageSize-1))
{
PageTableIndexLimit++;
}
hr = S_FALSE;
while (PageDirIndex < PageDirIndexLimit && hr != S_OK)
{
if ((hr = GetPageFrameNumber(Client, Data,
PageDirBase, PageDirIndex,
&PageTableBase, &LargePage)) == S_OK)
{
if (LargePage)
{
*PhysPage = PageTableBase;
}
else
{
do
{
if ((hr = GetPageFrameNumber(Client, Data,
PageTableBase, PageTableIndex,
PhysPage, NULL)) != S_OK)
{
hr = Control->GetInterrupt();
if (hr == S_OK)
{
Interrupted = TRUE;
Control->SetInterrupt(DEBUG_INTERRUPT_PASSIVE);
}
else
{
PageTableIndex++;
*VirtPage += PageSize;
}
}
} while (PageTableIndex < TableEntries && hr != S_OK);
}
}
else
{
*VirtPage += PageSize * (TableEntries - PageTableIndex);
}
if (hr != S_OK)
{
hr = Control->GetInterrupt();
if (hr == S_OK)
{
Interrupted = TRUE;
Control->SetInterrupt(DEBUG_INTERRUPT_PASSIVE);
}
else
{
PageTableIndex = 0;
PageDirIndex++;
}
}
}
if (PageDirIndex == PageDirIndexLimit &&
PageTableIndex < PageTableIndexLimit &&
hr != S_OK)
{
if ((hr = GetPageFrameNumber(Client, Data,
PageDirBase, PageDirIndex,
&PageTableBase, &LargePage)) == S_OK)
{
if (LargePage)
{
*PhysPage = PageTableBase;
}
else
{
do
{
if ((hr = GetPageFrameNumber(Client, Data,
PageTableBase, PageTableIndex,
PhysPage, NULL)) != S_OK)
{
hr = Control->GetInterrupt();
if (hr == S_OK)
{
Interrupted = TRUE;
Control->SetInterrupt(DEBUG_INTERRUPT_PASSIVE);
}
else
{
PageTableIndex++;
*VirtPage += PageSize;
}
}
} while (PageTableIndex < PageTableIndexLimit && hr != S_OK);
}
}
}
}
}
if (Control != NULL) Control->Release();
if (Data != NULL) Data->Release();
return ((Interrupted) ? E_ABORT : hr);
}
HRESULT
GetNextResidentAddress(
PDEBUG_CLIENT Client,
ULONG Session,
ULONG64 VirtAddrStart,
ULONG64 VirtAddrLimit,
PULONG64 VirtAddr,
PULONG64 PhysAddr
)
{
if (Client == NULL) return E_INVALIDARG;
HRESULT hr = S_OK;
ULONG64 PageDirBase;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if (Session < NUM_CACHED_DIR_BASES &&
CachedDirBase[Session].Valid)
{
PageDirBase = CachedDirBase[Session].PageDirBase;
}
else if (SessionId == CURRENT_SESSION &&
CachedDirBase[NUM_CACHED_DIR_BASES].Valid)
{
PageDirBase = CachedDirBase[NUM_CACHED_DIR_BASES].PageDirBase;
}
else
{
DEBUG_VALUE SessionIdCheck;
DEBUG_VALUE dvPageDirBase;
BOOL ShortProcessList = (Session == CURRENT_SESSION);
ULONG Processor;
ULONG64 Process=0;
ULONG64 ProcessListHead = 0;
ULONG64 ProcessListNext = 0;
ULONG ActiveProcessLinksOff;
ULONG CurrentSession;
if (!GetCurrentProcessor(g_ExtClient, &Processor, NULL))
{
Processor = 0;
}
GetCurrentProcessAddr(Processor, 0, &Process);
GetProcessHead(&ProcessListHead, &ProcessListNext);
if (GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActiveProcessLinksOff))
{
dprintf("Unable to get EPROCESS.ActiveProcessLinks offset\n");
hr = E_FAIL;;
ProcessListNext = 0;
}
hr = S_FALSE;
while ((ProcessListNext != ProcessListHead) && ProcessListNext &&
(hr != S_OK))
{
if (!ShortProcessList)
{
Process = ProcessListNext - ActiveProcessLinksOff;
if (!ReadPointer(ProcessListNext, &ProcessListNext))
{
dprintf("Cannot read EPROCESS at %p\n", Process);
hr = E_FAIL;
break;
}
if (CheckControlC())
{
hr = E_FAIL;
break;
}
}
if (!GetProcessSessionId(Process, &CurrentSession))
{
hr = E_FAIL;
break;
}
GetFieldValue(Process, "nt!_KPROCESS", "DirectoryTableBase[0]", PageDirBase);
if (!IsPtr64())
{
PageDirBase = (ULONG64) (LONG64) (LONG) PageDirBase;
}
if ((Session == CurrentSession) || ShortProcessList)
{
hr = S_OK;
if (Session < NUM_CACHED_DIR_BASES)
{
CachedDirBase[Session].Valid = TRUE;
CachedDirBase[Session].PageDirBase = PageDirBase;
}
else if (Session == CURRENT_SESSION)
{
CachedDirBase[NUM_CACHED_DIR_BASES].Valid = TRUE;
CachedDirBase[NUM_CACHED_DIR_BASES].PageDirBase = PageDirBase;
}
}
}
}
if (hr == S_OK)
{
hr = GetNextResidentPage(Client,
PageDirBase,
VirtAddrStart,
VirtAddrLimit,
VirtAddr,
PhysAddr);
}
else
{
ExtVerb("Page Directory Base lookup failed.\n");
}
return hr;
}
#endif
DECLARE_API( dss )
/*++
Routine Description:
Dumps the session space structure
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Result;
ULONG64 MmSessionSpace;
ULONG64 MmSessionSpacePtr = 0;
ULONG64 Wsle;
MmSessionSpacePtr = GetExpression(args);
if( MmSessionSpacePtr == 0 ) {
MmSessionSpacePtr = GetExpression("nt!MmSessionSpace");
if( !MmSessionSpacePtr ) {
dprintf("Unable to get address of MmSessionSpace\n");
return E_INVALIDARG;
}
if (!ReadPointer( MmSessionSpacePtr, &MmSessionSpace)) {
dprintf("Unable to get value of MmSessionSpace\n");
return E_INVALIDARG;
}
} else {
MmSessionSpace = MmSessionSpacePtr;
}
dprintf("MM_SESSION_SPACE at 0x%p\n",
MmSessionSpace
);
if (GetFieldValue(MmSessionSpace, "MM_SESSION_SPACE", "Wsle", Wsle)) {
dprintf("Unable to get value of MM_SESSION_SPACE at 0x%p\n",MmSessionSpace);
return E_INVALIDARG;
}
GetFieldOffset("MM_SESSION_SPACE", "PageTables", &Result);
dprintf("&PageTables %p\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "PagedPoolInfo", &Result);
dprintf("&MM_PAGED_POOL_INFO %x\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "Vm", &Result);
dprintf("&MMSUPPORT %p\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "Wsle", &Result);
dprintf("&PMMWSLE %p\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "Session", &Result);
dprintf("&MMSESSION %p\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "WorkingSetLockOwner", &Result);
dprintf("&WorkingSetLockOwner %p\n",
MmSessionSpace + Result
);
GetFieldOffset("MM_SESSION_SPACE", "PagedPool", &Result);
dprintf("&POOL_DESCRIPTOR %p\n",
MmSessionSpace + Result
);
return S_OK;
}
DECLARE_API( session )
{
INIT_API();
HRESULT hr;
ULONG NewSession = CURRENT_SESSION;
ULONG CurrentSession = INVALID_SESSION;
ULONG SessionCount = 0;
BOOL SetSession = FALSE;
PRTL_BITMAP SessionList = NULL;
DEBUG_VALUE DebugValue;
ULONG Remainder;
while (*args && isspace(*args)) args++;
if (args[0] == '-' || args[0] == '/')
{
if (args[1] == '?')
{
ExtOut("session displays number of sessions on machine and\n"
" the default SessionId used by session related extensions.\n"
"\n"
"Usage: session [ [-s] SessionId]\n"
" -s - sets default session used for session extensions\n"
"Note: Use !sprocess to dump session process\n");
EXIT_API();
return S_OK;
} else if (args[1] == 's')
{
args+=2;
SetSession = TRUE;
}
}
hr = g_ExtControl->Evaluate(args, DEBUG_VALUE_INT32, &DebugValue, &Remainder);
if (hr == S_OK)
{
args += Remainder;
}
if (GetSessionNumbers(Client, &CurrentSession, NULL, &SessionCount, &SessionList) == S_OK)
{
if (SessionCount != 0)
{
ExtOut("Sessions on machine: %lu\n", SessionCount);
// If a session wasn't specified,
// list valid sessions (up to a point).
if (hr != S_OK)
{
ULONG SessionLimit = SessionList->SizeOfBitMap;
ExtOut("Valid Sessions:");
for (ULONG CheckSession = 0; CheckSession <= SessionLimit; CheckSession++)
{
if (RtlCheckBit(SessionList, CheckSession)
/*GetSessionSpace(Client, CheckSession, NULL) == S_OK*/)
{
ExtOut(" %lu", CheckSession);
SessionCount--;
if (SessionCount == 0) break;
}
if (g_ExtControl->GetInterrupt() == S_OK)
{
ExtWarn("\n User aborted.\n");
break;
}
}
if (SessionCount > 0)
{
ExtOut(" ...?");
}
ExtOut("\n");
}
}
else if (SessionList)
{
ExtOut("There are ZERO session on machine.\n");
}
else
{
ExtErr("Couldn't determine number of sessions.\n");
}
if (SessionList)
{
FreeBitMap(SessionList);
}
if (CurrentSession != INVALID_SESSION)
{
ExtVerb("Running session: %lu\n", CurrentSession);
}
}
if ((hr == S_OK) && SetSession)
{
NewSession = DebugValue.I32;
ExtVerb("Previous Default Session: %s\n", SessionStr);
if (SetDefaultSession(Client, NewSession, NULL) != S_OK)
{
ExtErr("Couldn't set Session %lu.\n", NewSession);
}
ExtOut("Using session %s", SessionStr);
}
if (SessionId == CURRENT_SESSION)
{
if (CurrentSession != INVALID_SESSION)
{
ExtOut("Current Session %d", CurrentSession);
}
else
{
ExtOut("Error in reading current session");
}
}
ExtOut("\n");
EXIT_API();
return S_OK;
}
DECLARE_API( svtop )
{
INIT_API();
HRESULT hr;
DEBUG_VALUE SessVirtAddr;
ULONG64 PhysAddr;
if (S_OK == g_ExtControl->Evaluate(args, DEBUG_VALUE_INT64, &SessVirtAddr, NULL))
{
ExtOut("Use !vtop 0 %p\n", SessVirtAddr.I64);
}
else
{
ExtOut("Use !vtop 0 VirtualAddress\n");
}
EXIT_API();
return S_OK;
}
DECLARE_API( sprocess )
{
INIT_API();
HRESULT hr;
DEBUG_VALUE Session;
ULONG RemainingArgIndex;
DEBUG_VALUE Flag;
while (*args && isspace(*args)) args++;
if (args[0] == '-' && args[1] == '?')
{
ExtOut("sprocess is like !process, but for the SessionId specified.\n"
"\n"
"Usage: sprocess [SessionId [Flags]]\n"
" SessionId - specifies which session to dump.\n"
" Special SessionId values:\n"
" -1 - current session\n"
" Flags - see !process help\n");
EXIT_API();
return S_OK;
}
ULONG OldRadix;
g_ExtControl->GetRadix(&OldRadix);
g_ExtControl->SetRadix(10);
hr = g_ExtControl->Evaluate(args, DEBUG_VALUE_INT32, &Session, &RemainingArgIndex);
g_ExtControl->SetRadix(OldRadix);
Flag.I32 = 0;
if (hr != S_OK)
{
Session.I32 = SessionId;
hr = S_OK;
}
else
{
args += RemainingArgIndex;
hr = g_ExtControl->Evaluate(args, DEBUG_VALUE_INT32, &Flag, &RemainingArgIndex);
if (hr == S_OK)
{
args += RemainingArgIndex;
}
}
Flag.I32 = (Flag.I32 << 2) | 2;
while (*args && *args == ' ') ++args;
hr = DumpSessionInfo(Flag.I32, Session.I32, (PCHAR) (*args ? args : NULL));
EXIT_API();
return hr;
}
HRESULT
SearchLinkedList(
PDEBUG_CLIENT Client,
ULONG64 StartAddr,
ULONG64 NextLinkOffset,
ULONG64 SearchAddr,
PULONG LinksTraversed
)
{
if (LinksTraversed != NULL)
{
*LinksTraversed = 0;
}
INIT_API();
HRESULT hr = S_OK;
ULONG64 PhysAddr;
ULONG64 NextAddr = StartAddr;
ULONG LinkCount = 0;
ULONG PointerSize;
ULONG BytesRead;
PointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;
do
{
if ((hr = g_ExtData->ReadVirtual(NextLinkOffset + NextLinkOffset,
&NextAddr,
PointerSize,
&BytesRead)) == S_OK)
{
if (BytesRead == PointerSize)
{
LinkCount++;
if (PointerSize != 8)
{
NextAddr = DEBUG_EXTEND64(NextAddr);
}
ExtVerb("NextAddr: %p\n", NextAddr);
}
else
{
hr = S_FALSE;
}
}
} while (hr == S_OK &&
NextAddr != SearchAddr &&
NextAddr != 0 &&
LinkCount < 4 &&
NextAddr != StartAddr);
if (LinksTraversed != NULL)
{
*LinksTraversed = LinkCount;
}
// Did we really find SearchAddr?
if (hr == S_OK &&
NextAddr != SearchAddr)
{
hr = S_FALSE;
}
EXIT_API();
return hr;
}
DECLARE_API( walklist )
{
INIT_API();
HRESULT hr;
BOOL NeedHelp = FALSE;
BOOL SearchSessions = FALSE;
DEBUG_VALUE StartAddr;
DEBUG_VALUE OffsetToNextField = { -1, DEBUG_VALUE_INVALID };//FIELD_OFFSET(Win32PoolHead, pNext);
DEBUG_VALUE SearchAddr;
ULONG NextArg;
ULONG SessionCount;
ULONG Session = 0;
ULONG OldDefSession;
ULONG LinksToDest = 0;
while (*args && isspace(*args)) args++;
while (args[0] == '-' && !NeedHelp)
{
if (tolower(args[1]) == 'a' && isspace(args[2]))
{
SearchSessions = TRUE;
args += 2;
while (*args && isspace(*args)) args++;
}
else if (tolower(args[1]) == 'o' &&
GetExpressionEx(args+2,
&OffsetToNextField.I64, &args) == TRUE)
{
while (*args && isspace(*args)) args++;
}
else
{
NeedHelp = TRUE;
}
}
if (!NeedHelp &&
S_OK == g_ExtControl->Evaluate(args, DEBUG_VALUE_INT64, &StartAddr, &NextArg))
{
args += NextArg;
if (S_OK != g_ExtControl->Evaluate(args, DEBUG_VALUE_INT64, &SearchAddr, &NextArg))
{
SearchAddr.I64 = 0;
}
if (OffsetToNextField.Type == DEBUG_VALUE_INVALID)
{
ExtWarn("Assuming next field's offset is +8.\n");
OffsetToNextField.I64 = 8;
}
else
{
ExtOut("Using field at offset +0x%I64u for next.\n", OffsetToNextField.I64);
}
if (SearchSessions &&
GetSessionNumbers(Client, NULL, &OldDefSession, &SessionCount, NULL) == S_OK &&
SessionCount > 0)
{
ExtOut("Searching all sessions lists @ %p for %p\n", StartAddr.I64, SearchAddr.I64);
do
{
while (SetDefaultSession(Client, Session, NULL) != S_OK &&
Session <= SESSION_SEARCH_LIMIT)
{
Session++;
}
if (Session <= SESSION_SEARCH_LIMIT)
{
if ((hr = SearchLinkedList(Client, StartAddr.I64, OffsetToNextField.I64, SearchAddr.I64, &LinksToDest)) == S_OK)
{
ExtOut("Session %lu: Found %p after walking %lu linked list entries.\n", Session, SearchAddr.I64, LinksToDest);
}
else
{
ExtOut("Session %lu: Couldn't find %p after walking %lu linked list entries.\n", Session, SearchAddr.I64, LinksToDest);
}
Session++;
SessionCount--;
}
} while (SessionCount > 0 && Session <= SESSION_SEARCH_LIMIT);
if (SessionCount)
{
ExtErr("%lu sessions beyond session %lu were not searched.\n",
SessionCount, SESSION_SEARCH_LIMIT);
}
SetDefaultSession(Client, OldDefSession, NULL);
}
else
{
ExtOut("Searching Session %s list @ %p for %p\n", SessionStr, StartAddr.I64, SearchAddr.I64);
if ((hr = SearchLinkedList(Client, StartAddr.I64, OffsetToNextField.I64, SearchAddr.I64, &LinksToDest)) == S_OK)
{
ExtOut("Found %p after walking %lu linked list entries.\n", SearchAddr.I64, LinksToDest);
}
else
{
ExtOut("Couldn't find %p after walking %lu linked list entries.\n", SearchAddr.I64, LinksToDest);
}
}
}
else
{
NeedHelp = TRUE;
}
if (NeedHelp)
{
ExtOut("Usage: walklist [-a] StartAddress [SearchAddr]\n");
}
EXIT_API();
return S_OK;
}
HRESULT
GetBitMap(
PDEBUG_CLIENT Client,
ULONG64 pBitMap,
PRTL_BITMAP *pBitMapOut
)
{
HRESULT hr;
PRTL_BITMAP p;
ULONG Size;
ULONG64 Buffer;
ULONG BufferLen;
ULONG BytesRead = 0;
*pBitMapOut = NULL;
if ((GetFieldValue(pBitMap, "nt!_RTL_BITMAP", "SizeOfBitMap", Size) == S_OK) &&
(GetFieldValue(pBitMap, "nt!_RTL_BITMAP", "Buffer", Buffer) == S_OK))
{
PDEBUG_DATA_SPACES Data;
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) == S_OK)
{
BufferLen = (Size + 7) / 8;
#if DBG
ExtVerb("Reading RTL_BITMAP @ 0x%p:\n"
" SizeOfBitMap: %lu\n"
" Buffer: 0x%p\n"
" Length in bytes: 0x%lx\n",
pBitMap,
Size,
Buffer,
BufferLen);
#endif
p = (PRTL_BITMAP) HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) + BufferLen );
if (p != NULL)
{
RtlInitializeBitMap(p, (PULONG)(p + 1), Size);
hr = Data->ReadVirtual(Buffer, p->Buffer, BufferLen, &BytesRead);
if (hr != S_OK)
{
ExtErr("Error reading bitmap contents @ 0x%p\n", Buffer);
}
else if (BytesRead < BufferLen)
{
ExtErr("Error reading bitmap contents @ 0x%p\n", Buffer + BytesRead);
hr = E_FAIL;
}
if (hr != S_OK)
{
HeapFree( GetProcessHeap(), 0, p );
p = NULL;
}
else
{
*pBitMapOut = p;
}
}
else
{
hr = E_OUTOFMEMORY;
}
Data->Release();
}
else
{
ExtErr("Error setting up debugger interface.\n");
}
}
else
{
ExtErr("Error reading bitmap header @ 0x%p.\n", pBitMap);
}
return hr;
}
HRESULT
FreeBitMap(
PRTL_BITMAP pBitMap
)
{
return (HeapFree( GetProcessHeap(), 0, pBitMap) ? S_OK : S_FALSE);
}
HRESULT
CheckSingleFilter(
PUCHAR Tag,
PUCHAR Filter
)
{
ULONG i;
UCHAR tc;
UCHAR fc;
for ( i = 0; i < 4; i++ )
{
tc = (UCHAR) *Tag++;
fc = (UCHAR) *Filter++;
if ( fc == '*' ) return S_OK;
if ( fc == '?' ) continue;
if (i == 3 && (tc & ~(PROTECTED_POOL >> 24)) == fc) continue;
if ( tc != fc ) return S_FALSE;
}
return S_OK;
}
HRESULT
AccumAllFilter(
ULONG64 PoolAddr,
ULONG TagFilter,
ULONG64 PoolHeader,
PDEBUG_VALUE Tag,
ULONG BlockSize,
BOOL bQuotaWithTag,
PVOID Context
)
{
HRESULT hr;
DEBUG_VALUE PoolType;
PALLOCATION_STATS AllocStatsAccum = (PALLOCATION_STATS)Context;
if (AllocStatsAccum == NULL)
{
return E_INVALIDARG;
}
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType.I32);
if (hr == S_OK)
{
if (PoolType.I32 == 0)
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
else
{
DEBUG_VALUE PoolIndex;
if (!NewPool)
{
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex.I32);
}
if (hr == S_OK)
{
if (NewPool ? (PoolType.I32 & 0x04) : (PoolIndex.I32 & 0x80))
{
AllocStatsAccum->Allocated++;
AllocStatsAccum->AllocatedSize += BlockSize;
if (AllocStatsAccum->Allocated % 100 == 0)
{
ExtOut(".");
if (AllocStatsAccum->Allocated % 8000 == 0)
{
ExtOut("\n");
}
}
}
else
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
else
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
}
else
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
return hr;
}
HRESULT
CheckPrintAndAccumFilter(
ULONG64 PoolAddr,
ULONG TagFilter,
ULONG64 PoolHeader,
PDEBUG_VALUE Tag,
ULONG BlockSize,
BOOL bQuotaWithTag,
PVOID Context
)
{
HRESULT hr;
DEBUG_VALUE PoolType;
PALLOCATION_STATS AllocStatsAccum = (PALLOCATION_STATS)Context;
if (CheckSingleFilter(Tag->RawBytes, (PUCHAR)&TagFilter) != S_OK)
{
return S_FALSE;
}
ExtOut("0x%p size: %5lx ",//previous size: %4lx ",
PoolAddr,
BlockSize << POOL_BLOCK_SHIFT/*,
PreviousSize << POOL_BLOCK_SHIFT*/);
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType.I32);
if (hr == S_OK)
{
if (PoolType.I32 == 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.
//
ExtOut(" (Free)");
ExtOut(" %c%c%c%c\n",
Tag->RawBytes[0],
Tag->RawBytes[1],
Tag->RawBytes[2],
Tag->RawBytes[3]
);
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
else
{
DEBUG_VALUE PoolIndex;
DEBUG_VALUE ProcessBilled;
if (!NewPool)
{
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex.I32);
}
if (hr == S_OK)
{
if (NewPool ? (PoolType.I32 & 0x04) : (PoolIndex.I32 & 0x80))
{
ExtOut(" (Allocated)");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Allocated++;
AllocStatsAccum->AllocatedSize += BlockSize;
}
}
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.
//
ExtOut(" (Free ) ");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
}
else
{
ExtOut(" (?) ");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
if (!(PoolType.I32 & POOL_QUOTA_MASK) ||
bQuotaWithTag)
{
ExtOut(" %c%c%c%c%s",
Tag->RawBytes[0],
Tag->RawBytes[1],
Tag->RawBytes[2],
(Tag->RawBytes[3] & ~(PROTECTED_POOL >> 24)),
((Tag->I32 & PROTECTED_POOL) ? " (Protected)" : "")
);
}
if (PoolType.I32 & POOL_QUOTA_MASK &&
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled.I64) == S_OK &&
ProcessBilled.I64 != 0)
{
ExtOut(" Process: 0x%p\n", ProcessBilled.I64);
}
else
{
ExtOut("\n");
}
}
}
else
{
ExtErr(" Couldn't determine PoolType\n");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
return hr;
}
typedef struct _TAG_BUCKET : public ALLOCATION_STATS {
ULONG Tag;
_TAG_BUCKET *pNextTag;
} TAG_BUCKET, *PTAG_BUCKET;
typedef enum {
AllocatedPool,
FreePool,
IndeterminatePool,
} PoolType;
class AccumTagUsage : public ALLOCATION_STATS {
public:
AccumTagUsage(ULONG TagFilter);
~AccumTagUsage();
HRESULT Valid();
HRESULT Add(ULONG Tag, PoolType Type, ULONG Size);
HRESULT OutputResults(BOOL TagSort);
void Reset();
private:
ULONG GetHashIndex(ULONG Tag);
PTAG_BUCKET GetBucket(ULONG Tag);
ULONG SetTagFilter(ULONG TagFilter);
static const HashBitmaskLimit = 10; // For little-endian, must <= 16
HANDLE hHeap;
ULONG Buckets;
PTAG_BUCKET *Bucket; // Array of buckets
#if BIG_ENDIAN
ULONG HighMask;
ULONG HighShift;
ULONG LowMask;
ULONG LowShift;
#else
ULONG HighShiftLeft;
ULONG HighShiftRight;
ULONG LowShiftRight;
ULONG LowMask;
#endif
};
AccumTagUsage::AccumTagUsage(
ULONG TagFilter
)
{
hHeap = GetProcessHeap();
Buckets = SetTagFilter(TagFilter);
if (Buckets != 0)
{
Bucket = (PTAG_BUCKET *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, Buckets*sizeof(*Bucket));
Reset();
}
else
{
Bucket = NULL;
}
}
AccumTagUsage::~AccumTagUsage()
{
PTAG_BUCKET pB, pBNext;
ULONG i;
if (Bucket != NULL)
{
for (i = 0; i < Buckets; i++)
{
pB = Bucket[i];
while (pB != NULL)
{
pBNext = pB->pNextTag;
HeapFree(hHeap, 0, pB);
pB = pBNext;
}
}
HeapFree(hHeap, 0, Bucket);
}
}
HRESULT
AccumTagUsage::Valid()
{
return ((Bucket != NULL) ? S_OK : S_FALSE);
}
HRESULT
AccumTagUsage::Add(
ULONG Tag,
PoolType Type,
ULONG Size
)
{
PTAG_BUCKET pBucket = GetBucket(Tag);
if (pBucket == NULL) return E_FAIL;
switch (Type)
{
case AllocatedPool:
pBucket->Allocated++;
pBucket->AllocatedSize += Size;
break;
case FreePool:
pBucket->Free++;
pBucket->FreeSize += Size;
break;
case IndeterminatePool:
default:
pBucket->Indeterminate++;
pBucket->IndeterminateSize += Size;
break;
}
return S_OK;
}
HRESULT
AccumTagUsage::OutputResults(
BOOL AllocSort
)
{
HRESULT hr;
PTAG_BUCKET pB, pBNext;
ULONG i;
if (Bucket == NULL)
{
ExtOut(" No results\n");
}
else
{
CHAR szNormal[] = "%4.4s %8lu %12I64u %8lu %12I64u\n";
CHAR szShowIndeterminate[] = "%4.4s %8lu %12I64u %8lu %12I64u %8lu %12I64u\n";
PSZ pszOutFormat = szNormal;
ExtOut("\n"
" %I64u bytes in %lu allocated pages\n"
" %I64u bytes in %lu large allocations\n"
" %I64u bytes in %lu free pages\n"
" %I64u bytes available in %lu expansion pages\n",
((ULONG64) AllocatedPages) << PageShift,
AllocatedPages,
((ULONG64) LargePages) << PageShift,
LargeAllocs,
((ULONG64) FreePages) << PageShift,
FreePages,
((ULONG64) ExpansionPages) << PageShift,
ExpansionPages);
ExtOut("\nTag Allocs Bytes Freed Bytes");
if (Indeterminate != 0)
{
ExtOut(" Unknown Bytes");
pszOutFormat = szShowIndeterminate;
}
ExtOut("\n");
if (AllocSort)
{
ExtWarn(" Sorting by allocation size isn't supported.\n");
}
//else
{
// Output results sorted by Tag (natural storage order)
for (i = 0; i < Buckets; i++)
{
for (pB = Bucket[i]; pB != NULL; pB = pB->pNextTag)
{
if (pB->Allocated)
{
ExtOut(pszOutFormat,
&pB->Tag,
pB->Allocated, ((ULONG64)pB->AllocatedSize) << POOL_BLOCK_SHIFT,
pB->Free, ((ULONG64)pB->FreeSize) << POOL_BLOCK_SHIFT,
pB->Indeterminate, ((ULONG64)pB->IndeterminateSize) << POOL_BLOCK_SHIFT
);
}
}
}
}
ExtOut("-------------------------------------------------------------------------------\n");
ExtOut(pszOutFormat,
"Ttl:",
Allocated, ((ULONG64)AllocatedSize) << POOL_BLOCK_SHIFT,
Free, ((ULONG64)FreeSize) << POOL_BLOCK_SHIFT,
Indeterminate, ((ULONG64)IndeterminateSize) << POOL_BLOCK_SHIFT
);
}
return S_OK;
}
void
AccumTagUsage::Reset()
{
PTAG_BUCKET pB, pBNext;
ULONG i;
AllocatedPages = 0;
LargePages = 0;
LargeAllocs = 0;
FreePages = 0;
ExpansionPages = 0;
Allocated = 0;
AllocatedSize = 0;
Free = 0;
FreeSize = 0;
Indeterminate = 0;
IndeterminateSize = 0;
if (Bucket != NULL)
{
for (i = 0; i < Buckets; i++)
{
pB = Bucket[i];
if (pB != NULL)
{
do
{
pBNext = pB->pNextTag;
HeapFree(hHeap, 0, pB);
pB = pBNext;
} while (pB != NULL);
Bucket[i] = NULL;
}
}
}
}
ULONG
AccumTagUsage::GetHashIndex(
ULONG Tag
)
{
#if BIG_ENDIAN
return (((Tag & HighMask) >> HighShift) | ((Tag & LowMask) >> LowShift));
#else
return ((((Tag << HighShiftLeft) >> HighShiftRight) & ~LowMask) | ((Tag >> LowShiftRight) & LowMask));
#endif
}
PTAG_BUCKET
AccumTagUsage::GetBucket(
ULONG Tag
)
{
ULONG Index = GetHashIndex(Tag);
PTAG_BUCKET pB = Bucket[Index];
if (pB == NULL ||
#if BIG_ENDIAN
pB->Tag > Tag
#else
strncmp((char *)&pB->Tag, (char *)&Tag, 4) > 0
#endif
)
{
pB = (PTAG_BUCKET)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(TAG_BUCKET));
if (pB != NULL)
{
pB->Tag = Tag;
pB->pNextTag = Bucket[Index];
Bucket[Index] = pB;
}
}
else
{
while (pB->pNextTag != NULL)
{
if (
#if BIG_ENDIAN
pB->pNextTag->Tag > Tag
#else
strncmp((char *)&pB->pNextTag->Tag, (char *)&Tag, 4) > 0
#endif
)
{
break;
}
pB = pB->pNextTag;
}
if (pB->Tag != Tag)
{
PTAG_BUCKET pBPrev = pB;
pB = (PTAG_BUCKET)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(TAG_BUCKET));
if (pB != NULL)
{
pB->Tag = Tag;
pB->pNextTag = pBPrev->pNextTag;
pBPrev->pNextTag = pB;
}
}
}
return pB;
}
ULONG
AccumTagUsage::SetTagFilter(
ULONG TagFilter
)
{
ULONG NumBuckets;
UCHAR *Filter = (UCHAR *)&TagFilter;
ULONG i;
ULONG HighMaskBits, LowMaskBits;
UCHAR fc;
#if BIG_ENDIAN
ULONG Mask = 0;
ULONG MaskBits = 0;
if (Filter[0] == '*')
{
Mask = -1;
MaskBits = 32;
}
else
{
for ( i = 0; i < 32; i += 8 )
{
Mask <<= 8;
fc = *Filter++;
if ( fc == '*' )
{
Mask |= ((1 << i) - 1);
MaskBits += 32 - i;
break;
}
if ( fc == '?' )
{
Mask |= 0xFF;
MaskBits += 8;
}
}
}
if (MaskBits > HashBitmaskLimit)
{
MaskBits = HashBitmaskLimit;
}
NumBuckets = (1 << MaskBits);
for (HighShift = 32, HighMaskBits = 0;
HighShift > 0 && HighMaskBits < MaskBits;
HighShift--)
{
if (Mask & (1 << (HighShift-1)))
{
HighMaskBits++;
}
else if (HighMaskBits)
{
break;
}
}
HighMask = Mask & ~((1 << HighShift) - 1);
Mask &= ~HighMask;
MaskBits -= HighMaskBits;
HighShift -= MaskBits;
for (LowShift = HighShift, LowMaskBits = 0;
LowShift > 0 && LowMaskBits < MaskBits;
LowShift--)
{
if (Mask & (1 << (LowShift-1)))
{
LowMaskBits++;
}
else if (LowMaskBits)
{
break;
}
}
LowMask = Mask & ~((1 << LowShift) - 1);
#else
HighMaskBits = 0;
LowMaskBits = 0;
HighShiftLeft = 32;
HighShiftRight = 32;
LowShiftRight = 32;
LowMask = 0;
for ( i = 0; i < 32; i += 8 )
{
fc = *Filter++;
if ( fc == '*' )
{
if (HighMaskBits == 0)
{
HighMaskBits = min(8, HashBitmaskLimit);
HighShiftLeft = 32 - HighMaskBits - i;
HighShiftRight = 32 - HighMaskBits;
LowMaskBits = ((HighShiftLeft != 0) ?
min(8, HashBitmaskLimit - HighMaskBits) :
0);
HighShiftRight -= LowMaskBits;
LowShiftRight = (8 - LowMaskBits) + HighMaskBits + i;
LowMask = (1 << LowMaskBits) - 1;
}
else
{
LowMaskBits = min(8, HashBitmaskLimit - HighMaskBits);
HighShiftRight -= LowMaskBits;
LowShiftRight = (8 - LowMaskBits) + i;
LowMask = (1 << LowMaskBits) - 1;
}
break;
}
if ( fc == '?' )
{
if (HighMaskBits == 0)
{
HighMaskBits = min(8, HashBitmaskLimit);
HighShiftLeft = 32 - HighMaskBits - i;
HighShiftRight = 32 - HighMaskBits;
}
else
{
LowMaskBits = min(8, HashBitmaskLimit - HighMaskBits);
HighShiftRight -= LowMaskBits;
LowShiftRight = (8 - LowMaskBits) + i;
LowMask = (1 << LowMaskBits) - 1;
break;
}
}
}
NumBuckets = 1 << (HighMaskBits + LowMaskBits);
#endif
if (NumBuckets-1 != GetHashIndex(-1))
{
DbgPrint("AccumTagUsage::SetTagFilter: Invalid hash was generated.\n");
NumBuckets = 0;
}
return NumBuckets;
}
HRESULT
AccumTagUsageFilter(
ULONG64 PoolAddr,
ULONG TagFilter,
ULONG64 PoolHeader,
PDEBUG_VALUE Tag,
ULONG BlockSize,
BOOL bQuotaWithTag,
PVOID Context
)
{
HRESULT hr;
DEBUG_VALUE PoolType;
AccumTagUsage *atu = (AccumTagUsage *)Context;
PALLOCATION_STATS AllocStatsAccum = (PALLOCATION_STATS)atu;
if (CheckSingleFilter(Tag->RawBytes, (PUCHAR)&TagFilter) != S_OK)
{
return S_FALSE;
}
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType.I32);
if (hr == S_OK)
{
if (PoolType.I32 == 0)
{
hr = atu->Add(Tag->I32, FreePool, BlockSize);
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
else
{
DEBUG_VALUE PoolIndex;
if (!(PoolType.I32 & POOL_QUOTA_MASK) ||
bQuotaWithTag)
{
Tag->I32 &= ~PROTECTED_POOL;
}
else if (PoolType.I32 & POOL_QUOTA_MASK)
{
Tag->I32 = 'CORP';
}
if (!NewPool)
{
hr = GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex.I32);
}
if (hr == S_OK)
{
if (NewPool ? (PoolType.I32 & 0x04) : (PoolIndex.I32 & 0x80))
{
hr = atu->Add(Tag->I32, AllocatedPool, BlockSize);
AllocStatsAccum->Allocated++;
AllocStatsAccum->AllocatedSize += BlockSize;
if (AllocStatsAccum->Allocated % 100 == 0)
{
ExtOut(".");
if (AllocStatsAccum->Allocated % 8000 == 0)
{
ExtOut("\n");
}
}
}
else
{
hr = atu->Add(Tag->I32, FreePool, BlockSize);
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
else
{
hr = atu->Add(Tag->I32, IndeterminatePool, BlockSize);
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
}
else
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
return hr;
}
HRESULT
SearchSessionPool(
PDEBUG_CLIENT Client,
ULONG Session,
ULONG TagName,
FLONG Flags,
ULONG64 RestartAddr,
PoolFilterFunc Filter,
PALLOCATION_STATS AllocStats,
PVOID Context
)
/*++
Routine Description:
Engine to search session 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.
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:
HRESULT
--*/
{
HRESULT hr;
PDEBUG_SYMBOLS Symbols;
PDEBUG_DATA_SPACES Data;
LOGICAL PhysicallyContiguous;
ULONG PoolBlockSize;
ULONG64 PoolHeader;
ULONG64 PoolPage;
ULONG64 StartPage;
ULONG64 Pool;
ULONG Previous;
ULONG64 PoolStart;
ULONG64 PoolStartPage;
ULONG64 PoolPteAddress;
ULONG64 PoolEnd;
BOOL PageReadFailed;
ULONG64 PagesRead;
ULONG64 PageReadFailures;
ULONG64 PageReadFailuresAtEnd;
ULONG64 LastPageRead;
ULONG PoolTypeFlags = Flags & (SEARCH_POOL_NONPAGED | SEARCH_POOL_PAGED);
ULONG64 NTModuleBase;
ULONG PoolHeadTypeID;
ULONG SessionHeadTypeID;
ULONG HdrSize;
ULONG64 SessionSpace;
if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols),
(void **)&Symbols)) != S_OK)
{
return hr;
}
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) != S_OK)
{
Symbols->Release();
return hr;
}
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if ((hr = Symbols->GetSymbolTypeId("NT!_POOL_HEADER", &PoolHeadTypeID, &NTModuleBase)) == S_OK &&
(hr = Symbols->GetTypeSize(NTModuleBase, PoolHeadTypeID, & HdrSize)) == S_OK &&
(hr = GetSessionSpace(Session, &SessionSpace, NULL)) == S_OK)
{
ULONG PoolTagOffset, ProcessBilledOffset;
BOOL bQuotaWithTag;
ULONG ReadSessionId;
ULONG64 PagedPoolInfo;
ULONG64 NonPagedPoolBytes;
ULONG64 NonPagedPoolAllocations;
ULONG64 NonPagedPoolStart;
ULONG64 NonPagedPoolEnd;
ULONG64 PagedPoolPages;
ULONG64 PagedPoolBytes;
ULONG64 PagedPoolAllocations;
ULONG64 PagedPoolStart;
ULONG64 PagedPoolEnd;
ULONG SessionSpaceTypeID;
ULONG PagedPoolInfoOffset;
BOOL SearchingPaged = FALSE;
PRTL_BITMAP PagedPoolAllocationMap = NULL;
PRTL_BITMAP EndOfPagedPoolBitmap = NULL;
ULONG BusyStart;
PRTL_BITMAP PagedPoolLargeSessionAllocationMap = NULL;
BOOL Continue = TRUE;
bQuotaWithTag = (Symbols->GetFieldOffset(NTModuleBase, PoolHeadTypeID, "PoolTag", &PoolTagOffset) == S_OK &&
Symbols->GetFieldOffset(NTModuleBase, PoolHeadTypeID, "ProcessBilled", &ProcessBilledOffset) == S_OK &&
PoolTagOffset != ProcessBilledOffset);
// General parser setup and dump MM_SESSION_SPACE structure
if ((hr = Symbols->GetTypeId(NTModuleBase, "MM_SESSION_SPACE", &SessionSpaceTypeID)) == S_OK &&
(hr = GetFieldValue(SessionSpace, "nt!MM_SESSION_SPACE", "SessionId", ReadSessionId)) == S_OK &&
(hr = Symbols->GetFieldOffset(NTModuleBase, SessionSpaceTypeID, "PagedPoolInfo", &PagedPoolInfoOffset)) == S_OK &&
(InitTypeRead(SessionSpace, nt!MM_SESSION_SPACE) == S_OK)
// (hr = OutState.OutputTypeVirtual(SessionSpace + PagedPoolInfoOffset, "NT!_MM_PAGED_POOL_INFO", 0)) == S_OK
)
{
ExtOut("Searching session %ld pool.\n", ReadSessionId);
// Remaining type output goes to PoolHead reader
}
else
{
ExtErr("Error getting basic session pool information.\n");
}
while (hr == S_OK && Continue)
{
ExtOut("\n");
if (PoolTypeFlags & SEARCH_POOL_NONPAGED)
{
NonPagedPoolBytes = ReadField(NonPagedPoolBytes);// NOFIELD
NonPagedPoolAllocations = ReadField(NonPagedPoolAllocations);// NOFIELD
if (NonPagedPoolBytes != 0 &&
NonPagedPoolAllocations != 0)
{
ExtOut("NonPaged pool: %I64u bytes in %I64u allocations\n",
NonPagedPoolBytes, NonPagedPoolAllocations);
}
ExtOut(" NonPaged pool range reader isn't implemented.\n");
PoolStart = 0;
PoolEnd = 0;
SearchingPaged = FALSE;
}
else
{
PagedPoolBytes = ReadField(PagedPoolBytes); // NOFIELD
PagedPoolAllocations = ReadField(PagedPoolAllocations); // NOFIELD
if (PagedPoolBytes != 0 &&
PagedPoolAllocations != 0)
{
ExtOut("Paged pool: %I64u bytes in %I64u allocations\n",
PagedPoolBytes, PagedPoolAllocations);
PagedPoolPages = ReadField(AllocatedPagedPool); // NOFIELD
if (PagedPoolPages != 0)
{
ExtOut(" Paged Pool Info: %I64u pages allocated\n",
PagedPoolPages);
}
}
PagedPoolStart = ReadField(PagedPoolStart);
PagedPoolEnd = ReadField(PagedPoolEnd);
if (PagedPoolStart == 0 || PagedPoolEnd == 0)
{
ExtErr(" Couldn't get PagedPool range.\n");
}
else
{
PoolStart = PagedPoolStart;
PoolEnd = PagedPoolEnd;
SearchingPaged = TRUE;
ULONG64 PagedBitMap, EndOfPagedBitMap;
PagedBitMap = ReadField(PagedPoolInfo.PagedPoolAllocationMap);
EndOfPagedBitMap = ReadField(PagedPoolInfo.EndOfPagedPoolBitmap);
if (GetBitMap(Client, PagedBitMap, &PagedPoolAllocationMap) == S_OK &&
GetBitMap(Client, EndOfPagedBitMap, &EndOfPagedPoolBitmap) == S_OK)
{
ULONG PositionAfterLastAlloc;
ULONG AllocBits;
ULONG UnusedBusyBits;
if (RtlCheckBit(EndOfPagedPoolBitmap, EndOfPagedPoolBitmap->SizeOfBitMap - 1))
{
BusyStart = PagedPoolAllocationMap->SizeOfBitMap;
UnusedBusyBits = 0;
}
else
{
OSCompat_RtlFindLastBackwardRunClear(
EndOfPagedPoolBitmap,
EndOfPagedPoolBitmap->SizeOfBitMap - 1,
&PositionAfterLastAlloc);
BusyStart = RtlFindSetBits(PagedPoolAllocationMap, 1, PositionAfterLastAlloc);
if (BusyStart < PositionAfterLastAlloc || BusyStart == -1)
{
BusyStart = PagedPoolAllocationMap->SizeOfBitMap;
UnusedBusyBits = 0;
}
else
{
UnusedBusyBits = PagedPoolAllocationMap->SizeOfBitMap - BusyStart;
}
}
AllocBits = RtlNumberOfSetBits(PagedPoolAllocationMap) - UnusedBusyBits;
AllocStats->AllocatedPages += AllocBits;
AllocStats->FreePages += (BusyStart - AllocBits);
AllocStats->ExpansionPages += UnusedBusyBits;
PagedBitMap = ReadField(PagedPoolInfo.PagedPoolLargeSessionAllocationMap); // NOFIELD
if (PagedBitMap != 0 &&
GetBitMap(Client, PagedBitMap, &PagedPoolLargeSessionAllocationMap) == S_OK)
{
ULONG AllocStart, AllocEnd;
ULONG LargeAllocs = RtlNumberOfSetBits(PagedPoolLargeSessionAllocationMap);
AllocStats->LargeAllocs += LargeAllocs;
AllocStart = 0;
AllocEnd = -1;
while (LargeAllocs > 0)
{
AllocStart = RtlFindSetBits(PagedPoolLargeSessionAllocationMap, 1, AllocStart);
if (AllocStart >= AllocEnd+1 && AllocStart != -1)
{
AllocEnd = RtlFindSetBits(EndOfPagedPoolBitmap, 1, AllocStart);
if (AllocEnd >= AllocStart && AllocEnd != -1)
{
AllocStats->LargePages += AllocEnd - AllocStart + 1;
AllocStart++;
LargeAllocs--;
}
else
{
ExtWarn(" Warning Large Pool Allocation Map or End Of Pool Map is invalid.\n");
break;
}
}
else
{
ExtWarn(" Warning Large Pool Allocation Map is invalid.\n");
break;
}
}
if (LargeAllocs != 0)
{
ExtWarn(" %lu large allocations weren't calculated.\n", LargeAllocs);
AllocStats->LargeAllocs -= LargeAllocs;
}
}
}
}
}
if (hr == S_OK)
{
ExtOut("Searching %s pool (0x%p : 0x%p) for Tag: %c%c%c%c\r\n\n",
((PoolTypeFlags & SEARCH_POOL_NONPAGED) ? "NonPaged" : "Paged"),
PoolStart,
PoolEnd,
TagName,
TagName >> 8,
TagName >> 16,
TagName >> 24);
PageReadFailed = FALSE;
PoolStartPage = PAGE_ALIGN64(PoolStart);
PoolPage = PoolStart;
PagesRead = 0;
PageReadFailures = 0;
PageReadFailuresAtEnd = 0;
LastPageRead = PAGE_ALIGN64(PoolPage);
while (PoolPage < PoolEnd && hr == S_OK)
{
Pool = PAGE_ALIGN64(PoolPage);
StartPage = Pool;
Previous = 0;
if (Session != CURRENT_SESSION)
{
ExtOut("Currently only searching the current session is supported.\n");
PoolPage = PoolEnd;
break;
}
if (CheckControlC())
{
ExtOut("\n...terminating - searched pool to 0x%p\n",
Pool-1);
hr = E_ABORT;
break;
}
if (SearchingPaged)
{
if (PagedPoolAllocationMap != NULL)
{
ULONG StartPosition, EndPosition;
StartPosition = (ULONG)((Pool - PoolStartPage) >> PageShift);
EndPosition = RtlFindSetBits(EndOfPagedPoolBitmap, 1, StartPosition);
if (EndPosition < StartPosition) EndPosition = -1;
if (!RtlCheckBit(PagedPoolAllocationMap, StartPosition))
{
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) >> PageShift;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (EndPosition == -1)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn("No remaining pool allocations from 0x%p to 0x%p.\n", Pool, PoolEnd);
}
PoolPage = PoolEnd;
}
else
{
PoolPage = PoolStartPage + (((ULONG64)EndPosition + 1) << PageShift);
}
continue;
}
else if (EndOfPagedPoolBitmap != NULL)
{
if (PagedPoolLargeSessionAllocationMap != NULL &&
RtlCheckBit(PagedPoolLargeSessionAllocationMap, StartPosition))
{
if (EndPosition == -1)
{
ExtWarn("No end to large pool allocation @ 0x%p found.\n", Pool);
PoolPage = PoolEnd;
}
else
{
EndPosition++;
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) >> PageShift;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
PoolPage = PoolStartPage + (((ULONG64)EndPosition) << PageShift);
if (Flags & SEARCH_POOL_PRINT_LARGE)
{
ExtOut("0x%p size: %5I64x %s UNTAGGED Large\n",
StartPage,
PoolPage - StartPage,
((RtlAreBitsSet(PagedPoolAllocationMap, StartPosition, EndPosition - StartPosition)) ?
"(Allocated)" :
((RtlAreBitsClear(PagedPoolAllocationMap, StartPosition, EndPosition - StartPosition)) ?
"(! Free !) " :
"(!! Partially Allocated !!)"))
);
}
if (Flags & SEARCH_POOL_LARGE_ONLY)
{
// Quickly locate next large allocation
StartPosition = RtlFindSetBits(PagedPoolLargeSessionAllocationMap, 1, EndPosition);
if (StartPosition < EndPosition || StartPosition == -1)
{
ExtVerb(" No large allocations found after 0x%p\n", PoolPage-1);
PoolPage = PoolEnd;
}
else
{
PoolPage = PoolStartPage + (((ULONG64)StartPosition) << PageShift);
}
}
}
continue;
}
else if (EndPosition == -1)
{
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) >> PageShift;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn("No remaining pool allocations from 0x%p to 0x%p.\n", Pool, PoolEnd);
}
PoolPage = PoolEnd;
continue;
}
else if (StartPosition >= BusyStart)
{
ExtWarn("Found end of allocation at %lu within expansion pages starting at %lu.\n",
EndPosition, BusyStart);
}
}
}
}
if (Flags & SEARCH_POOL_LARGE_ONLY)
{
ExtErr(" Unable to identify large pages. Terminating search at 0x%p.\n", StartPage);
PoolPage = PoolEnd;
hr = E_FAIL;
continue;
}
// Search page for small allocations
while (PAGE_ALIGN64(Pool) == StartPage && hr == S_OK)
{
DEBUG_VALUE HdrPoolTag, BlockSize, PreviousSize, AllocatorBackTraceIndex, PoolTagHash;
ULONG PoolType;
if ((hr = GetFieldValue(Pool, "nt!_POOL_HEADER", "PoolTag", HdrPoolTag.I32)) != S_OK)
{
if (hr != S_OK)
{
PSTR psz;
ExtErr("Type read error %lx @ 0x%p.\n", hr, Pool);
ExtWarn("Failed to read an allocated page @ 0x%p.\n", StartPage);
if (hr == MEMORY_READ_ERROR)
{
hr = S_OK;
}
else
{
ExtOut("\n...terminating - searched pool to 0x%p\n",
Pool);
hr = E_ABORT;
}
if (hr == E_ABORT)
{
break;
}
}
if (!PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" Couldn't read pool from 0x%p", Pool);
}
PagesRead += (StartPage - LastPageRead) / PageSize;
LastPageRead = StartPage;
PageReadFailed = TRUE;
}
#if 0
if ((hr = GetNextResidentAddress(Client,
Session,
StartPage + PageSize,
PoolEnd,
&PoolPage,
NULL)) != S_OK)
#endif
if ((PoolPage = GetNextResidentAddress(StartPage + PageSize,
PoolEnd)) == 0)
{
if (hr != E_ABORT)
{
hr = S_OK;
}
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p.\n", PoolEnd);
ExtWarn("No remaining resident page found.\n");
}
PageReadFailuresAtEnd = (PoolEnd - LastPageRead) / PageSize;
PageReadFailures += PageReadFailuresAtEnd;
LastPageRead = PoolEnd;
PageReadFailed = FALSE;
PoolPage = PoolEnd;
}
break;
}
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) / PageSize;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (GetFieldValue(Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize.I32) != S_OK)
{
ExtErr("Error reading BlockSize @ 0x%p.\n", Pool);
break;
}
if ((BlockSize.I32 << POOL_BLOCK_SHIFT) > PageSize)//POOL_PAGE_SIZE)
{
ExtVerb("Bad allocation size @ 0x%p, too large\n", Pool);
break;
}
if (BlockSize.I32 == 0)
{
ExtVerb("Bad allocation size @ 0x%p, zero is invalid\n", Pool);
break;
}
if (GetFieldValue(Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize.I32) != S_OK ||
PreviousSize.I32 != Previous)
{
ExtVerb("Bad previous allocation size @ 0x%p, last size was 0x%lx\n", Pool, Previous);
break;
}
Filter(Pool,
TagName,
Pool,
&HdrPoolTag,
BlockSize.I32,
bQuotaWithTag,
Context
);
Previous = BlockSize.I32;
Pool += (Previous << POOL_BLOCK_SHIFT);
if ( CheckControlC())
{
ExtOut("\n...terminating - searched pool to 0x%p\n",
PoolPage-1);
hr = E_ABORT;
}
}
if (hr == S_OK)
{
PoolPage = (PoolPage + PageSize);
}
}
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
ExtWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailuresAtEnd = (PoolPage - LastPageRead) / PageSize;
PageReadFailures += PageReadFailuresAtEnd;
PageReadFailed = FALSE;
}
else
{
PagesRead += (PoolPage - LastPageRead) / PageSize;
}
ExtOut(" Pages Read: %I64d\n"
" Failures: %I64d (%I64d at end of search)\n",
PagesRead, PageReadFailures, PageReadFailuresAtEnd);
}
if (PoolTypeFlags == (SEARCH_POOL_NONPAGED | SEARCH_POOL_PAGED))
{
PoolTypeFlags = SEARCH_POOL_PAGED;
}
else
{
Continue = FALSE;
}
}
if (PagedPoolAllocationMap != NULL) FreeBitMap(PagedPoolAllocationMap);
if (EndOfPagedPoolBitmap != NULL) FreeBitMap(EndOfPagedPoolBitmap);
if (PagedPoolLargeSessionAllocationMap != NULL) FreeBitMap(PagedPoolLargeSessionAllocationMap);
}
return hr;
}
HRESULT
GetTagFilter(
PDEBUG_CLIENT Client,
PCSTR *pArgs,
PDEBUG_VALUE TagFilter
)
{
HRESULT hr;
PCSTR args;
PCSTR TagArg;
ULONG TagLen;
CHAR TagEnd;
ULONG WildCardPos;
TagArg = args = *pArgs;
TagFilter->Type = DEBUG_VALUE_INVALID;
do
{
args++;
} while (*args != '\0' && !isspace(*args));
while (isspace(*args)) args++;
if (TagArg[0] == '0' && TagArg[1] == 'x')
{
TagFilter->I64 = GetExpression(TagArg);
}
else
{
if (TagArg[0] == '`' || TagArg[0] == '\'' || TagArg[0] == '\"')
{
TagEnd = TagArg[0];
TagArg++;
args = TagArg;
while (args - TagArg < 4 &&
*args != '\0' &&
*args != TagEnd)
{
args++;
}
TagLen = (ULONG)(args - TagArg);
if (*args == TagEnd) args++;
while (isspace(*args)) args++;
}
else
{
TagLen = (ULONG)(args - TagArg);
TagEnd = '\0';
}
if (TagLen == 0 ||
(TagLen < 4 &&
TagArg[TagLen-1] != '*'
) ||
(TagLen >= 4 &&
TagArg[4] != '\0' &&
!isspace(TagArg[4]) &&
(TagArg[4] != TagEnd || (TagArg[5] != '\0' && !isspace(TagArg[5])))
)
)
{
ExtErr(" Invalid Tag filter.\n");
hr = E_INVALIDARG;
}
else
{
hr = S_OK;
for (WildCardPos = 0; WildCardPos < TagLen; WildCardPos++)
{
if (TagArg[WildCardPos] == '*')
{
ULONG NewTagLen = WildCardPos + 1;
if (NewTagLen < TagLen)
{
ExtWarn(" Ignoring %lu characters after * in Tag.\n",
TagLen - NewTagLen);
}
TagLen = NewTagLen;
// loop will terminate
}
}
if (TagLen < 4)
{
TagFilter->I32 = ' ';
while (TagLen-- > 0)
{
TagFilter->RawBytes[TagLen] = TagArg[TagLen];
}
}
else
{
TagFilter->I32 = TagArg[0] | (TagArg[1] << 8) | (TagArg[2] << 16) | (TagArg[3] << 24);
}
TagFilter->Type = DEBUG_VALUE_INT32;
}
}
if (hr == S_OK)
{
*pArgs = args;
}
return hr;
}
HRESULT
OutputAllocStats(
PALLOCATION_STATS AllocStats,
BOOL PartialResults
)
{
ExtOut("\n"
" %I64u bytes in %lu allocated pages\n"
" %I64u bytes in %lu large allocations\n"
" %I64u bytes in %lu free pages\n"
" %I64u bytes available in %lu expansion pages\n"
"\n"
"%s found (small allocations only): %lu\n"
" Allocated: %I64u bytes in %lu entries\n"
" Free: %I64u bytes in %lu entries\n"
" Undetermined: %I64u bytes in %lu entries\n",
((ULONG64) AllocStats->AllocatedPages) << PageShift,
AllocStats->AllocatedPages,
((ULONG64) AllocStats->LargePages) << PageShift,
AllocStats->LargeAllocs,
((ULONG64) AllocStats->FreePages) << PageShift,
AllocStats->FreePages,
((ULONG64) AllocStats->ExpansionPages) << PageShift,
AllocStats->ExpansionPages,
((PartialResults) ? "PARTIAL entries" : "Entries"),
AllocStats->Allocated + AllocStats->Free + AllocStats->Indeterminate,
((ULONG64)AllocStats->AllocatedSize) << POOL_BLOCK_SHIFT,
AllocStats->Allocated,
((ULONG64)AllocStats->FreeSize) << POOL_BLOCK_SHIFT,
AllocStats->Free,
((ULONG64)AllocStats->IndeterminateSize) << POOL_BLOCK_SHIFT,
AllocStats->Indeterminate
);
return S_OK;
}
DECLARE_API( spoolfind )
{
HRESULT hr = S_OK;
INIT_API( );
BOOL BadArg = FALSE;
ULONG RemainingArgIndex;
DEBUG_VALUE TagName = { 0, DEBUG_VALUE_INVALID };
FLONG Flags = 0;
DEBUG_VALUE Session = { DEFAULT_SESSION, DEBUG_VALUE_INVALID };
while (isspace(*args)) args++;
while (!BadArg && hr == S_OK)
{
while (isspace(*args)) args++;
if (*args == '-')
{
// Process switches
args++;
BadArg = (*args == '\0' || isspace(*args));
while (*args != '\0' && !isspace(*args))
{
switch (tolower(*args))
{
case 'f':
Flags |= SEARCH_POOL_PRINT_UNREAD;
args++;
break;
case 'l':
Flags |= SEARCH_POOL_PRINT_LARGE;
args++;
break;
case 'n':
Flags |= SEARCH_POOL_NONPAGED;
args++;
break;
case 'p':
Flags |= SEARCH_POOL_PAGED;
args++;
break;
case 's':
if (Session.Type != DEBUG_VALUE_INVALID)
{
ExtErr("Session argument specified multiple times.\n");
BadArg = TRUE;
}
else
{
args++;
hr = GetExpressionEx(args, &Session.I64, &args);
if (hr == FALSE)
{
ExtErr("Invalid Session.\n");
}
}
break;
default:
BadArg = TRUE;
break;
}
if (BadArg) break;
}
}
else
{
if (*args == '\0') break;
if (TagName.Type == DEBUG_VALUE_INVALID)
{
hr = GetTagFilter(Client, &args, &TagName);
}
else
{
ExtErr("Unrecognized argument @ %s\n", args);
BadArg = TRUE;
}
}
}
if (!BadArg && hr == S_OK)
{
if (TagName.Type == DEBUG_VALUE_INVALID)
{
if (Flags & SEARCH_POOL_PRINT_LARGE)
{
TagName.I32 = ' *';
Flags |= SEARCH_POOL_LARGE_ONLY;
}
else
{
ExtErr("Missing Tag.\n");
hr = E_INVALIDARG;
}
}
}
if (BadArg || hr != S_OK)
{
if (*args == '?')
{
ExtOut("spoolfind is like !kdexts.poolfind, but for the SessionId specified.\n"
"\n");
}
ExtOut("Usage: spoolfind [-lnpf] [-s SessionId] Tag\n"
" -f - show read failure ranges\n"
" -l - show large allocations\n"
" -n - show non-paged pool\n"
" -p - show paged pool\n"
"\n"
" Tag - Pool tag to search for\n"
" Can be 4 character string or\n"
" hex value in 0xXXXX format\n"
"\n"
" SessionId - session to dump\n"
" Special SessionId values:\n"
" -1 - current session\n"
" -2 - last !session SessionId (default)\n"
);
}
else
{
ALLOCATION_STATS AllocStats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if ((Flags & (SEARCH_POOL_PAGED | SEARCH_POOL_NONPAGED)) == 0)
{
Flags |= SEARCH_POOL_PAGED | SEARCH_POOL_NONPAGED;
}
if (Session.Type == DEBUG_VALUE_INVALID)
{
Session.I32 = DEFAULT_SESSION;
}
hr = SearchSessionPool(Client,
Session.I32, TagName.I32, Flags,
0,
CheckPrintAndAccumFilter, &AllocStats, &AllocStats);
if (hr == S_OK || hr == E_ABORT)
{
OutputAllocStats(&AllocStats, (hr != S_OK));
}
else
{
ExtWarn("SearchSessionPool returned %lx\n", hr);
}
}
return hr;
}
DECLARE_API( spoolsum )
{
HRESULT hr = S_OK;
INIT_API( );
ULONG RemainingArgIndex;
FLONG Flags = 0;
DEBUG_VALUE Session = { DEFAULT_SESSION, DEBUG_VALUE_INVALID };
while (isspace(*args)) args++;
while (hr == S_OK)
{
while (isspace(*args)) args++;
if (*args == '-')
{
// Process switches
args++;
if (*args == '\0' || isspace(*args)) hr = E_INVALIDARG;
while (*args != '\0' && !isspace(*args))
{
switch (tolower(*args))
{
case 'f':
Flags |= SEARCH_POOL_PRINT_UNREAD;
args++;
break;
case 'n':
Flags |= SEARCH_POOL_NONPAGED;
args++;
break;
case 'p':
Flags |= SEARCH_POOL_PAGED;
args++;
break;
case 's':
if (Session.Type != DEBUG_VALUE_INVALID)
{
ExtErr("Session argument specified multiple times.\n");
hr = E_INVALIDARG;
}
else
{
args++;
hr = GetExpressionEx(args, &Session.I64, &args);
if (hr == FALSE)
{
ExtErr("Invalid Session.\n");
}
}
break;
default:
hr = E_INVALIDARG;
break;
}
if (hr != S_OK) break;
}
}
else
{
if (*args == '\0') break;
if (Session.Type == DEBUG_VALUE_INVALID)
{
hr = GetExpressionEx(args, &Session.I64, &args);
if (hr == FALSE)
{
ExtErr("Invalid Session.\n");
}
}
else
{
ExtErr("Unrecognized argument @ %s\n", args);
hr = E_INVALIDARG;
}
}
}
if (hr != S_OK)
{
if (*args == '?')
{
ExtOut("spoolsum summarizes session pool information for SessionId specified.\n"
"\n");
hr = S_OK;
}
ExtOut("Usage: spoolsum [-fnp] [[-s] SessionId]\n"
" f - show read failure ranges\n"
" n - show non-paged pool\n"
" p - show paged pool (Default)\n"
"\n"
" SessionId - session to dump\n"
" Special SessionId values:\n"
" -1 - current session\n"
" -2 - last !session SessionId (default)\n"
);
}
else
{
ALLOCATION_STATS AllocStats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if ((Flags & (SEARCH_POOL_PAGED | SEARCH_POOL_NONPAGED)) == 0)
{
Flags |= SEARCH_POOL_PAGED;
}
hr = SearchSessionPool(Client,
DEFAULT_SESSION, ' *', Flags,
0,
AccumAllFilter, &AllocStats, &AllocStats);
if (hr == S_OK || hr == E_ABORT)
{
OutputAllocStats(&AllocStats, (hr != S_OK));
}
else
{
ExtWarn("SearchSessionPool returned %lx\n", hr);
}
}
return hr;
}
DECLARE_API( spoolused )
{
HRESULT hr = S_OK;
INIT_API( );
BOOL BadArg = FALSE;
ULONG RemainingArgIndex;
DEBUG_VALUE TagName = { 0, DEBUG_VALUE_INVALID };
BOOL NonPagedUsage = FALSE;
BOOL PagedUsage = FALSE;
BOOL AllocSort = FALSE;
DEBUG_VALUE Session = { DEFAULT_SESSION, DEBUG_VALUE_INVALID };
while (isspace(*args)) args++;
while (!BadArg && hr == S_OK)
{
while (isspace(*args)) args++;
if (*args == '-')
{
// Process switches
args++;
BadArg = (*args == '\0' || isspace(*args));
while (*args != '\0' && !isspace(*args))
{
switch (tolower(*args))
{
case 'a':
AllocSort = TRUE;
args++;
break;
case 'n':
NonPagedUsage = TRUE;
args++;
break;
case 'p':
PagedUsage = TRUE;
args++;
break;
case 's':
if (Session.Type != DEBUG_VALUE_INVALID)
{
ExtErr("Session argument specified multiple times.\n");
BadArg = TRUE;
}
else
{
args++;
hr = GetExpressionEx(args, &Session.I64, &args);
if (hr == FALSE)
{
ExtErr("Invalid Session.\n");
}
}
break;
default:
BadArg = TRUE;
break;
}
if (BadArg) break;
}
}
else
{
if (*args == '\0') break;
if (TagName.Type == DEBUG_VALUE_INVALID)
{
hr = GetTagFilter(Client, &args, &TagName);
}
else
{
ExtErr("Unrecognized argument @ %s\n", args);
BadArg = TRUE;
}
}
}
if (BadArg || hr != S_OK)
{
if (*args == '?')
{
ExtOut("spoolused is like !kdexts.poolused, but for the SessionId specified.\n"
"\n");
}
ExtOut("Usage: spoolused [-anp] [-s SessionId] [Tag]\n"
" -a - sort by allocation size (Not Implemented)\n"
" -n - show non-paged pool\n"
" -p - show paged pool\n"
"\n"
" SessionId - session to dump\n"
" Special SessionId values:\n"
" -1 - current session\n"
" -2 - last !session SessionId (default)\n"
"\n"
" Tag - Pool tag filter\n"
" Can be 4 character string or\n"
" hex value in 0xXXXX format\n"
);
}
else
{
if (!NonPagedUsage && !PagedUsage)
{
NonPagedUsage = TRUE;
PagedUsage = TRUE;
}
if (Session.Type == DEBUG_VALUE_INVALID)
{
Session.I32 = DEFAULT_SESSION;
}
if (TagName.Type == DEBUG_VALUE_INVALID)
{
TagName.I32 = ' *';
}
AccumTagUsage atu(TagName.I32);
if (atu.Valid() != S_OK)
{
ExtErr("Error: failed to prepare tag usage reader.\n");
hr = E_FAIL;
}
if (hr == S_OK && NonPagedUsage)
{
hr = SearchSessionPool(Client,
Session.I32, TagName.I32, SEARCH_POOL_NONPAGED,
0,
AccumTagUsageFilter, &atu, &atu);
if (hr == S_OK || hr == E_ABORT)
{
atu.OutputResults(AllocSort);
}
else
{
ExtWarn("SearchSessionPool returned %lx\n", hr);
}
}
if (hr == S_OK && PagedUsage)
{
if (NonPagedUsage) atu.Reset();
hr = SearchSessionPool(Client,
Session.I32, TagName.I32, SEARCH_POOL_PAGED,
0,
AccumTagUsageFilter, &atu, &atu);
if (hr == S_OK || hr == E_ABORT)
{
atu.OutputResults(AllocSort);
}
else
{
ExtWarn("SearchSessionPool returned %lx\n", hr);
}
}
}
return hr;
}