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.
 
 
 
 
 
 

4649 lines
143 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.hxx"
#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 {
ULONG UniqueState;
ULONG64 SessionSpaceAddr;
} CachedSession[NUM_CACHED_SESSIONS+1] = { { 0, 0 } };
ULONG ExtraCachedSessionId;
#define NUM_CACHED_DIR_BASES 8
struct {
ULONG UniqueState;
ULONG64 PageDirBase;
} CachedDirBase[NUM_CACHED_DIR_BASES+1] = { { FALSE, 0} };
struct {
ULONG UniqueState;
ULONG64 PhysAddr;
ULONG64 Data;
} CachedPhysAddr[2] = { { 0, 0, 0} };
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,
OutputControl *OutCtl,
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].UniqueState = INVALID_UNIQUE_STATE;
}
ExtraCachedSessionId = INVALID_SESSION;
for (int s = 0; s < sizeof(CachedDirBase)/sizeof(CachedDirBase[0]); s++)
{
CachedDirBase[s].UniqueState = 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].UniqueState = INVALID_UNIQUE_STATE;
CachedPhysAddr[1].UniqueState = INVALID_UNIQUE_STATE;
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;
}
/**************************************************************************\
*
* 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
{
BitFieldParser BitFieldReader(Client, MMPTEValid);
OutputState OutState(Client);
if ((hr = BitFieldReader.Ready()) == S_OK &&
(hr = OutState.Setup(0, &BitFieldReader)) == S_OK)
{
// Read Valid bit field from symbol file
if (OutState.Execute("dt NT!HARDWARE_PTE Valid") != S_OK ||
BitFieldReader.ParseOutput() != S_OK ||
BitFieldReader.Complete() != S_OK)
{
MMPTEValid->Valid = MMPTEValid->Compose(0, 1);
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
{
BitFieldParser BitFieldReader(Client, MMPTEProto);
OutputState OutState(Client);
if ((hr = BitFieldReader.Ready()) == S_OK &&
(hr = OutState.Setup(0, &BitFieldReader)) == S_OK)
{
// Read Prototype bit field from symbol file
if (OutState.Execute("dt NT!MMPTE_PROTOTYPE Prototype") != S_OK ||
BitFieldReader.ParseOutput() != S_OK ||
BitFieldReader.Complete() != S_OK)
{
switch (TargetMachine)
{
case IMAGE_FILE_MACHINE_I386:
MMPTEProto->Valid = MMPTEProto->Compose(10, 1);
break;
case IMAGE_FILE_MACHINE_IA64:
MMPTEProto->Valid = MMPTEProto->Compose(1, 1);
break;
default:
{
OutputControl OutCtl(Client);
OutCtl.OutErr("Couldn't find MMPTE Prototype bit in type info.\n");
}
}
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
{
BitFieldParser BitFieldReader(Client, MMPTETrans);
OutputState OutState(Client);
if ((hr = BitFieldReader.Ready()) == S_OK &&
(hr = OutState.Setup(0, &BitFieldReader)) == S_OK)
{
// Read Transition bit field from symbol file
if (OutState.Execute("dt NT!PROTOTYPE_PTE Transition") != S_OK ||
BitFieldReader.ParseOutput() != S_OK ||
BitFieldReader.Complete() != S_OK)
{
switch (TargetMachine)
{
case IMAGE_FILE_MACHINE_I386:
MMPTETrans->Valid = MMPTETrans->Compose(11, 1);
break;
case IMAGE_FILE_MACHINE_IA64:
MMPTETrans->Valid = MMPTETrans->Compose(7, 1);
break;
default:
{
OutputControl OutCtl(Client);
OutCtl.OutErr("Couldn't find MMPTE Transition bit in type info.\n");
}
}
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
{
BitFieldParser BitFieldReader(Client, MMPTEX86LargePage);
OutputState OutState(Client);
if ((hr = BitFieldReader.Ready()) == S_OK &&
(hr = OutState.Setup(0, &BitFieldReader)) == S_OK)
{
// Read LargePage bit field from symbol file
if (OutState.Execute("dt NT!HARDWARE_PTE LargePage") != S_OK ||
BitFieldReader.ParseOutput() != S_OK ||
BitFieldReader.Complete() != S_OK)
{
MMPTEX86LargePage->Valid = MMPTEX86LargePage->Compose(7, 1);
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
*
\**************************************************************************/
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
{
BitFieldParser BitFieldReader(Client, MMPTEpfn);
OutputState OutState(Client);
if ((hr = BitFieldReader.Ready()) == S_OK &&
(hr = OutState.Setup(0, &BitFieldReader)) == S_OK)
{
// Read PageFrameNumber bit field from symbol file
if (OutState.Execute("dt NT!HARDWARE_PTE PageFrameNumber") != S_OK ||
BitFieldReader.ParseOutput() != S_OK ||
BitFieldReader.Complete() != S_OK)
{
switch (TargetMachine)
{
case IMAGE_FILE_MACHINE_I386:
MMPTEpfn->Valid = MMPTEpfn->Compose(12, PaeEnabled ? 24 : 20);
break;
case IMAGE_FILE_MACHINE_IA64:
if (PageSize)
{
MMPTEpfn->Valid = MMPTEpfn->Compose(PageShift, 50-PageShift);
break;
}
default:
{
OutputControl OutCtl(Client);
OutCtl.OutErr("Couldn't find MMPTE pfn in type info.\n");
}
}
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;
}
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)
{
DEBUG_VALUE RunningSession;
BasicOutputParser SessionReader(Client, 1);
OutputState OutState(Client);
if ((hr = SessionReader.LookFor(&RunningSession, "SessionId:", DEBUG_VALUE_INT32)) == S_OK &&
(hr = OutState.Setup(0, &SessionReader)) == S_OK &&
(hr = OutState.Execute("!process -1 0")) == S_OK &&
(hr = SessionReader.ParseOutput()) == S_OK &&
(hr = SessionReader.Complete()) == S_OK)
{
*CurrentSession = RunningSession.I32;
}
else
{
*CurrentSession = INVALID_SESSION;
}
}
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;
}
OutputControl OutCtl(Client);
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)
{
OutCtl.OutErr("Unable to locate %s\n", PointerName);
}
else
{
hr = Data->ReadPointersVirtual(1, SessionIdListPointerAddr, &SessionIdListAddr);
if (hr == S_OK)
{
hr = GetBitMap(Client, SessionIdListAddr, &SessionListBitMap);
if (hr == S_OK)
{
SessionCount = RtlNumberOfSetBits(SessionListBitMap);
}
}
}
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;
GetSessionNumbers(Client, NULL, OldSession, NULL, NULL);
if ((NewSession == CURRENT_SESSION) ||
(GetSessionSpace(Client, NewSession, NULL) == S_OK))
{
if (NewSession != SessionId)
{
SessionId = NewSession;
if (SessionId == CURRENT_SESSION)
{
strcpy(SessionStr, "CURRENT");
}
else
{
_ultoa(SessionId, SessionStr, 10);
}
}
hr = S_OK;
}
return hr;
}
HRESULT
GetCurrentSession(
PDEBUG_CLIENT Client,
PULONG64 CurSessionSpace,
PULONG CurSessionId
)
{
static ULONG LastCachedUniqueState = INVALID_UNIQUE_STATE;
static DEBUG_VALUE LastSessionSpace = { 0, DEBUG_VALUE_INVALID };
static DEBUG_VALUE LastSessionId = { INVALID_SESSION, DEBUG_VALUE_INVALID };
HRESULT hr = S_OK;
OutputControl OutCtl(Client);
BasicOutputParser SessionSpaceReader(Client);
BasicOutputParser SessionIdReader(Client);
OutputState OutState(Client);
CHAR szCommand[MAX_PATH];
ULONG CurrentUniqueState = UniqueTargetState;
if (CurSessionSpace != NULL) *CurSessionSpace = 0;
if (CurSessionId != NULL) *CurSessionId = INVALID_SESSION;
// Get the current session space
if (CurrentUniqueState == INVALID_UNIQUE_STATE ||
LastCachedUniqueState != CurrentUniqueState ||
LastSessionSpace.Type == DEBUG_VALUE_INVALID)
{
PDEBUG_SYSTEM_OBJECTS System;
ULONG64 ProcessAddr;
if ((hr = Client->QueryInterface(__uuidof(IDebugSystemObjects),
(void **)&System)) == S_OK)
{
if ((hr = System->GetCurrentProcessDataOffset(&ProcessAddr)) == S_OK)
{
// Set Output callbacks
if ((hr = SessionSpaceReader.LookFor(&LastSessionSpace, "Session", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &SessionSpaceReader)) == S_OK)
{
sprintf(szCommand, "dt NT!EPROCESS Session 0x%I64x", ProcessAddr);
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = SessionSpaceReader.ParseOutput()) == S_OK &&
(hr = SessionSpaceReader.Complete()) == S_OK)
{
if (LastSessionSpace.I64 == 0)
{
hr = E_FAIL;
}
}
}
}
else
{
OutCtl.OutErr("IDebugSystemObjects::GetCurrentProcessDataOffset returned %s.\n", pszHRESULT(hr));
}
System->Release();
}
if (hr != S_OK)
{
LastSessionSpace.Type = DEBUG_VALUE_INVALID;
}
else
{
OutCtl.OutVerb("Caching current session space @ 0x%p.\n", LastSessionSpace.I64);
// Update CachedUniqueState and make sure SessionID
// is refreshed.
if (CurrentUniqueState == INVALID_UNIQUE_STATE ||
LastCachedUniqueState != CurrentUniqueState)
{
LastCachedUniqueState = CurrentUniqueState;
LastSessionId.Type = DEBUG_VALUE_INVALID;
}
}
}
if (hr == S_OK &&
CurSessionId != NULL &&
LastSessionId.Type == DEBUG_VALUE_INVALID)
{
DEBUG_VALUE CurrentSessionId;
// Set Output callbacks
if ((hr = SessionIdReader.LookFor(&LastSessionId, "SessionId", DEBUG_VALUE_INT32)) == S_OK &&
(hr = OutState.Setup(0, &SessionIdReader)) == S_OK)
{
sprintf(szCommand, "dt NT!MM_SESSION_SPACE SessionId 0x%I64x", LastSessionSpace.I64);
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = SessionIdReader.ParseOutput()) == S_OK &&
(hr = SessionIdReader.Complete()) == S_OK)
{
if (LastSessionId.I32 == INVALID_SESSION)
{
hr = E_FAIL;
}
}
}
if (hr != S_OK)
{
LastSessionId.Type = DEBUG_VALUE_INVALID;
}
}
if (hr == S_OK)
{
if (CurSessionSpace != NULL) *CurSessionSpace = LastSessionSpace.I64;
if (CurSessionId != NULL) *CurSessionId = LastSessionId.I32;
}
return hr;
}
HRESULT
GetSessionSpace(
PDEBUG_CLIENT Client,
ULONG Session,
PULONG64 SessionSpace
)
{
HRESULT hr;
OutputControl OutCtl(Client);
DEBUG_VALUE FoundSession;
DEBUG_VALUE SessionSpaceAddr;
ULONG CurrentUniqueState = UniqueTargetState;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if (Session == CURRENT_SESSION)
{
ULONG64 CurSessionSpace;
ULONG CurSessionId;
hr = GetCurrentSession(Client, &SessionSpaceAddr.I64, &FoundSession.I32);
}
else
{
if (CurrentUniqueState != INVALID_UNIQUE_STATE)
{
if (Session < NUM_CACHED_SESSIONS)
{
if (CachedSession[Session].UniqueState == CurrentUniqueState &&
CachedSession[Session].SessionSpaceAddr != 0)
{
if (SessionSpace != NULL) *SessionSpace = CachedSession[Session].SessionSpaceAddr;
return S_OK;
}
}
else if (ExtraCachedSessionId != INVALID_SESSION &&
Session == ExtraCachedSessionId)
{
if (CachedSession[NUM_CACHED_SESSIONS].UniqueState == CurrentUniqueState &&
CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr != 0)
{
if (SessionSpace != NULL) *SessionSpace = CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr;
return S_OK;
}
}
}
ULONG64 FirstSessionSpace;
DEBUG_VALUE SessionSpaceWsList;
DEBUG_VALUE SessionSpaceWsListOffset;
BasicOutputParser OffsetReader(Client);
BasicOutputParser FlinkReader(Client);
BasicOutputParser SessionReader(Client);
OutputState OutState(Client);
CHAR szCommand[MAX_PATH];
// Set Output callbacks
// Get ListEntry offset
if ((hr = OffsetReader.LookFor(&SessionSpaceWsListOffset, " +", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &OffsetReader)) == S_OK)
{
sprintf(szCommand, "dt NT!MM_SESSION_SPACE WsListEntry");
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = OffsetReader.ParseOutput()) == S_OK)
{
hr = OffsetReader.Complete();
}
}
// Get first session space in list
if (hr == S_OK &&
(hr = FlinkReader.LookFor(&SessionSpaceWsList, "Flink", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &FlinkReader)) == S_OK)
{
sprintf(szCommand, "dt NT!MiSessionWsList");
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = FlinkReader.ParseOutput()) == S_OK)
{
hr = FlinkReader.Complete();
}
}
// Add SessionId to reader search
if (hr == S_OK &&
(hr = SessionReader.LookFor(&FoundSession, "SessionId", DEBUG_VALUE_INT32)) == S_OK &&
(hr = SessionReader.LookFor(&SessionSpaceWsList, "Flink", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &SessionReader)) == S_OK)
{
SessionSpaceAddr.I64 = SessionSpaceWsList.I64 - SessionSpaceWsListOffset.I64;
FirstSessionSpace = SessionSpaceAddr.I64;
OutCtl.OutVerb("First session space @ %p.\n", FirstSessionSpace);
do
{
// Get SessionId for this session space
SessionReader.DiscardOutput();
SessionReader.Relook();
sprintf(szCommand,
"dt NT!MM_SESSION_SPACE SessionId WsListEntry.Flink 0x%I64x",
SessionSpaceAddr.I64);
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = SessionReader.ParseOutput()) == S_OK &&
(hr = SessionReader.Complete()) == S_OK)
{
if (Session == FoundSession.I32)
{
break;
}
// Compute next session space
SessionSpaceAddr.I64 = SessionSpaceWsList.I64 - SessionSpaceWsListOffset.I64;
if (SessionSpaceAddr.I64 == FirstSessionSpace)
{
hr = S_FALSE;
}
}
else
{
OutCtl.OutErr("Couldn't get SessionId or next session from \'%s\'\n", szCommand);
PSTR pszOutput;
if (SessionReader.GetOutputCopy(&pszOutput) == S_OK)
{
OutCtl.OutVerb(" dt output:\n%s\n", pszOutput);
SessionReader.FreeOutputCopy(pszOutput);
}
}
} while (hr == S_OK);
}
}
if (hr == S_OK)
{
OutCtl.OutVerb("Session %ld lookup found Session #%ld @ 0x%p.\n",
Session, FoundSession.I32, SessionSpaceAddr.I64);
if (FoundSession.I32 < NUM_CACHED_SESSIONS)
{
CachedSession[FoundSession.I32].UniqueState = CurrentUniqueState;
CachedSession[FoundSession.I32].SessionSpaceAddr = SessionSpaceAddr.I64;
}
else
{
ExtraCachedSessionId = FoundSession.I32;
CachedSession[NUM_CACHED_SESSIONS].UniqueState = CurrentUniqueState;
CachedSession[NUM_CACHED_SESSIONS].SessionSpaceAddr = SessionSpaceAddr.I64;
}
if (SessionSpace != NULL) *SessionSpace = SessionSpaceAddr.I64;
}
return hr;
}
HRESULT
GetSessionDirBase(
PDEBUG_CLIENT Client,
ULONG Session,
PULONG64 PageDirBase
)
{
HRESULT hr = S_FALSE;
OutputControl OutCtl(Client);
ULONG64 SessionSpaceOffset;
ULONG64 Process = -1;
DEBUG_VALUE SessionIdCheck;
DEBUG_VALUE dvPageDirBase;
BasicOutputParser ProcessReader(Client);
OutputState OutState(Client);
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(Client, Session, &SessionSpaceOffset)) == S_OK)
{
DEBUG_VALUE ProcessSessionListOffset;
BasicOutputParser OffsetReader(Client);
if ((hr = OffsetReader.LookFor(&ProcessSessionListOffset, " +", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &OffsetReader)) == S_OK)
{
sprintf(szCommand, "dt NT!_EPROCESS SessionProcessLinks");
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = OffsetReader.ParseOutput()) == S_OK)
{
hr = OffsetReader.Complete();
}
}
DEBUG_VALUE SessionProcessListAddr;
BasicOutputParser FlinkReader(Client);
if (hr == S_OK &&
(hr = FlinkReader.LookFor(&SessionProcessListAddr, "Flink", DEBUG_VALUE_INT64)) == S_OK &&
(hr = OutState.Setup(0, &FlinkReader)) == S_OK)
{
sprintf(szCommand, "dt NT!MM_SESSION_SPACE ProcessList.Flink 0x%I64x", SessionSpaceOffset);
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = FlinkReader.ParseOutput()) == S_OK &&
(hr = FlinkReader.Complete()) == S_OK)
{
Process = SessionProcessListAddr.I64 - ProcessSessionListOffset.I64;
}
}
}
else
{
OutCtl.OutVerb("GetSessionSpace returned HRESULT 0x%lx.\n", hr);
}
if (hr == S_OK &&
(hr = ProcessReader.LookFor(&SessionIdCheck, "SessionId:", DEBUG_VALUE_INT32)) == S_OK &&
(hr = ProcessReader.LookFor(&dvPageDirBase, "DirBase:", DEBUG_VALUE_INT64, 16)) == S_OK &&
(hr = OutState.Setup(0, &ProcessReader)) == S_OK)
{
sprintf(szCommand, "!process 0x%I64x 0", Process);
if ((hr = OutState.Execute(szCommand)) == S_OK &&
(hr = ProcessReader.ParseOutput()) == S_OK &&
(hr = ProcessReader.Complete()) == S_OK)
{
*PageDirBase = dvPageDirBase.I64;
OutCtl.OutVerb("DirBase for session %lu is %p.\n", SessionIdCheck.I32, dvPageDirBase.I64);
if (Session != CURRENT_SESSION &&
Session != SessionIdCheck.I32)
{
hr = S_FALSE;
}
else
{
LastSession = Session;
LastSessionPageDirBase = dvPageDirBase.I64;
}
}
}
return hr;
}
HRESULT
ReadPageTableEntry(
PDEBUG_DATA_SPACES Data,
ULONG64 PageTableBase,
ULONG64 PageTableIndex,
PULONG64 PageTableEntry
)
{
HRESULT hr;
ULONG64 PhysAddr = PageTableBase + PageTableIndex * HwPte.Size;
ULONG BytesRead;
ULONG CurrentUniqueState = UniqueTargetState;
*PageTableEntry = 0;
if (CurrentUniqueState != INVALID_UNIQUE_STATE)
{
if (CachedPhysAddr[0].UniqueState == CurrentUniqueState &&
CachedPhysAddr[0].PhysAddr == PhysAddr)
{
*PageTableEntry = CachedPhysAddr[0].Data;
return S_OK;
}
else if (CachedPhysAddr[1].UniqueState == CurrentUniqueState &&
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].UniqueState = CurrentUniqueState;
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))
{
RIP("Found large X86 page with bad frame number.\n");
}
if (Large == NULL)
{
RIP("Unexpected large X86 page found.\n");
}
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
GetPhysicalBase(
PDEBUG_CLIENT Client,
ULONG64 PageDirBase,
ULONG64 PageDirIndex,
ULONG64 PageTableIndex,
PULONG64 PhysicalBase
)
{
HRESULT hr;
PDEBUG_DATA_SPACES Data;
ULONG64 PageTableBase;
BOOL Large;
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) == S_OK)
{
if ((hr = GetPageFrameNumber(Client, Data,
PageDirBase, PageDirIndex,
&PageTableBase, &Large)) == S_OK)
{
if (!Large)
{
hr = GetPageFrameNumber(Client, Data,
PageTableBase, PageTableIndex,
PhysicalBase, NULL);
}
else
{
*PhysicalBase = PageTableBase;
}
}
Data->Release();
}
return hr;
}
HRESULT
GetPhysicalAddress(
PDEBUG_CLIENT Client,
ULONG Session,
ULONG64 VirtAddr,
PULONG64 PhysAddr
)
{
if (Client == NULL) return E_INVALIDARG;
HRESULT hr = S_OK;
ULONG64 PageDirIndex;
ULONG64 PageTableIndex;
ULONG64 PageByteIndex;
ULONG64 PageDirBase;
OutputControl OutCtl(Client);
ULONG CurrentUniqueState = UniqueTargetState;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
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;
}
Symbols->Release();
}
}
if (HwPte.Valid)
{
ULONG TableEntries = PageSize / HwPte.Size;
PageByteIndex = VirtAddr & (PageSize-1);
VirtAddr >>= PageShift;
PageTableIndex = VirtAddr % TableEntries;
PageDirIndex = (VirtAddr / TableEntries) % TableEntries;
if (Session < NUM_CACHED_DIR_BASES &&
CurrentUniqueState != INVALID_UNIQUE_STATE &&
CachedDirBase[Session].UniqueState == CurrentUniqueState &&
(hr = GetPhysicalBase(Client,
CachedDirBase[Session].PageDirBase,
PageDirIndex,
PageTableIndex,
PhysAddr)) == S_OK)
{
*PhysAddr |= PageByteIndex;
}
else
{
DEBUG_VALUE SessionIdCheck;
DEBUG_VALUE dvPageDirBase;
BasicOutputParser ProcessReader(Client);
OutputState OutState(Client);
OutputControl OutCtlToProcessReader;
BOOL ShortProcessList = (Session == CURRENT_SESSION);
BOOL fOutputNewLine = FALSE;
CProcessListing ProcessListing(40);
if ((hr = ProcessReader.LookFor(&SessionIdCheck, "SessionId:", DEBUG_VALUE_INT32)) == S_OK &&
(hr = ProcessReader.LookFor(&dvPageDirBase, "DirBase:", DEBUG_VALUE_INT64, 16)) == S_OK &&
(hr = OutState.Setup(0, &ProcessReader)) == S_OK &&
(hr = OutCtlToProcessReader.SetControl(
DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_NOT_LOGGED |
DEBUG_OUTCTL_OVERRIDE_MASK,
OutState.Client)) == S_OK &&
(hr = (ShortProcessList ?
OutState.Execute("!process -1 0") :
OutputSessionProcesses(OutState.Client, &OutCtlToProcessReader, Session, "0", &ProcessListing)
/*OutState.Execute("!process 0 0")*/
)) == S_OK)
{
hr = S_FALSE;
while (hr != S_OK &&
OutCtl.GetInterrupt() != S_OK)
{
if (ProcessReader.ParseOutput() != S_OK ||
ProcessReader.Complete() != S_OK)
{
if (ShortProcessList ||
!ProcessListing.Unprocessed())
{
break;
}
// At this point, we have processed some, but
// not all so get the next listing. Since there
// can be a huge process list, output a progress
// indicator.
OutCtl.Output(".");
fOutputNewLine = TRUE;
ProcessListing.PrepareForNextListing();
ProcessReader.DiscardOutput();
hr = OutputSessionProcesses(OutState.Client, &OutCtlToProcessReader, Session, "0", &ProcessListing);
if (hr != S_OK)
{
OutCtl.OutErr("OutputSessionProcesses returned 0x%X (!= S_OK)\n", hr);
}
ProcessReader.Relook();
if (ProcessReader.ParseOutput() != S_OK ||
ProcessReader.Complete() != S_OK)
{
break;
}
}
ProcessReader.Relook();
if (Session == CURRENT_SESSION)
{
// The current process is always first
// due to the '!process -1 0' above.
Session = SessionIdCheck.I32;
}
if (Session != SessionIdCheck.I32)
{
continue;
}
hr = GetPhysicalBase(Client,
dvPageDirBase.I64,
PageDirIndex,
PageTableIndex,
PhysAddr);
if (hr == S_OK)
{
*PhysAddr |= PageByteIndex;
if (Session < NUM_CACHED_DIR_BASES)
{
CachedDirBase[Session].UniqueState = CurrentUniqueState;
CachedDirBase[Session].PageDirBase = dvPageDirBase.I64;
}
else if (Session == CURRENT_SESSION)
{
CachedDirBase[NUM_CACHED_DIR_BASES].UniqueState = CurrentUniqueState;
CachedDirBase[NUM_CACHED_DIR_BASES].PageDirBase = dvPageDirBase.I64;
}
}
else if ((ShortProcessList &&
SessionId == CURRENT_SESSION))
{
ShortProcessList = FALSE;
hr = OutputSessionProcesses(OutState.Client, &OutCtlToProcessReader, Session, "0", &ProcessListing);
if (hr != S_OK)
{
OutCtl.OutErr("OutputSessionProcesses returned 0x%X (!= S_OK)\n", hr);
}
hr = S_FALSE;
}
}
if (fOutputNewLine)
{
OutCtl.Output("\n");
}
}
else
{
OutCtl.OutVerb("Process reading setup failed.\n");
}
}
}
else
{
OutCtl.OutVerb("GetPageLookupData returned 0x%lx.\n", hr);
}
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;
OutputControl OutCtl(Client);
ULONG CurrentUniqueState = UniqueTargetState;
if (Session == DEFAULT_SESSION)
{
Session = SessionId;
}
if (Session < NUM_CACHED_DIR_BASES &&
CurrentUniqueState != INVALID_UNIQUE_STATE &&
CachedDirBase[Session].UniqueState == CurrentUniqueState)
{
PageDirBase = CachedDirBase[Session].PageDirBase;
}
else if (SessionId == CURRENT_SESSION &&
CurrentUniqueState != INVALID_UNIQUE_STATE &&
CachedDirBase[NUM_CACHED_DIR_BASES].UniqueState == CurrentUniqueState)
{
PageDirBase = CachedDirBase[NUM_CACHED_DIR_BASES].PageDirBase;
}
else
{
DEBUG_VALUE SessionIdCheck;
DEBUG_VALUE dvPageDirBase;
BasicOutputParser ProcessReader(Client);
OutputState OutState(Client);
BOOL ShortProcessList = (Session == CURRENT_SESSION);
if ((hr = ProcessReader.LookFor(&SessionIdCheck, "SessionId:", DEBUG_VALUE_INT32)) == S_OK &&
(hr = ProcessReader.LookFor(&dvPageDirBase, "DirBase:", DEBUG_VALUE_INT64, 16)) == S_OK &&
(hr = OutState.Setup(0, &ProcessReader)) == S_OK &&
(hr = (ShortProcessList ?
OutState.Execute("!process -1 0") :
OutState.Execute("!process 0 0"))) == S_OK)
{
hr = S_FALSE;
while (hr != S_OK &&
OutCtl.GetInterrupt() != S_OK &&
ProcessReader.ParseOutput() == S_OK &&
ProcessReader.Complete() == S_OK)
{
ProcessReader.Relook();
if (Session == CURRENT_SESSION)
{
// The current process is always first
// due to the '!process -1 0' above.
Session = SessionIdCheck.I32;
}
if (Session == SessionIdCheck.I32)
{
hr = S_OK;
PageDirBase = dvPageDirBase.I64;
if (Session < NUM_CACHED_DIR_BASES)
{
CachedDirBase[Session].UniqueState = CurrentUniqueState;
CachedDirBase[Session].PageDirBase = dvPageDirBase.I64;
}
else if (Session == CURRENT_SESSION)
{
CachedDirBase[NUM_CACHED_DIR_BASES].UniqueState = CurrentUniqueState;
CachedDirBase[NUM_CACHED_DIR_BASES].PageDirBase = dvPageDirBase.I64;
}
}
}
}
else
{
OutCtl.OutVerb("Process reading setup failed.\n");
}
}
if (hr == S_OK)
{
hr = GetNextResidentPage(Client,
PageDirBase,
VirtAddrStart,
VirtAddrLimit,
VirtAddr,
PhysAddr);
}
else
{
OutCtl.OutVerb("Page Directory Base lookup failed.\n");
}
return hr;
}
DECLARE_API( session )
{
INIT_API();
HRESULT hr;
ULONG NewSession = CURRENT_SESSION;
ULONG CurrentSession;
ULONG SessionCount = 0;
PRTL_BITMAP SessionList = NULL;
DEBUG_VALUE DebugValue;
while (*args && isspace(*args)) args++;
if (args[0] == '-' && args[1] == '?')
{
ExtOut("session displays number of sessions on machine and\n"
" the default SessionId used by session related extensions.\n"
"\n"
"Usage: session [SessionId]\n"
" SessionId - sets default session used for session extensions\n");
EXIT_API(S_OK);
}
ULONG OldRadix;
g_pExtControl->GetRadix(&OldRadix);
g_pExtControl->SetRadix(10);
hr = g_pExtControl->Evaluate(args, DEBUG_VALUE_INT32, &DebugValue, NULL);
g_pExtControl->SetRadix(OldRadix);
if (GetSessionNumbers(Client, &CurrentSession, NULL, &SessionCount, &SessionList) == S_OK)
{
if (SessionCount != 0)
{
ExtOut("Sesssions 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_pExtControl->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)
{
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 (GetSessionNumbers(Client, &CurrentSession, NULL, NULL, NULL) == S_OK &&
CurrentSession != INVALID_SESSION)
{
ExtOut(" (%d)", CurrentSession);
}
else
{
ExtOut(" (?)");
}
}
ExtOut("\n");
EXIT_API(S_OK);
}
DECLARE_API( svtop )
{
INIT_API();
HRESULT hr;
DEBUG_VALUE SessVirtAddr;
ULONG64 PhysAddr;
if (S_OK == g_pExtControl->Evaluate(args, DEBUG_VALUE_INT64, &SessVirtAddr, NULL))
{
if ((hr = GetPhysicalAddress(Client, SessionId, SessVirtAddr.I64, &PhysAddr)) == S_OK)
{
ExtOut("Session %s: %p -> %p\n", SessionStr, SessVirtAddr.I64, PhysAddr);
}
else
{
ExtErr("Failed to translate virtual address %p from session %s\n"
" HRESULT: 0x%lx\n",
SessVirtAddr.I64, SessionStr, hr);
}
}
else
{
ExtOut("Usage: svtop SessionVirtualAddress\n");
}
EXIT_API(S_OK);
}
DECLARE_API( sprocess )
{
INIT_API();
HRESULT hr;
DEBUG_VALUE Session;
ULONG RemainingArgIndex;
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"
" -2 - last !session SessionId (default)\n"
" Flags - see !process help\n");
EXIT_API(S_OK);
}
ULONG OldRadix;
g_pExtControl->GetRadix(&OldRadix);
g_pExtControl->SetRadix(10);
hr = g_pExtControl->Evaluate(args, DEBUG_VALUE_INT32, &Session, &RemainingArgIndex);
g_pExtControl->SetRadix(OldRadix);
if (hr != S_OK)
{
Session.I32 = DEFAULT_SESSION;
args = "0";
hr = S_OK;
}
else
{
args += RemainingArgIndex;
}
CProcessListing ProcessListing;
hr = OutputSessionProcesses(Client, NULL, Session.I32, args, &ProcessListing);
EXIT_API(hr);
}
HRESULT
OutputSessionProcesses(
PDEBUG_CLIENT Client,
OutputControl *OutCtl,
ULONG Session,
PCSTR args,
CProcessListing *pProcessListing
)
{
HRESULT hr;
PDEBUG_CONTROL Control = NULL;
ULONG64 SessionSpace;
if (OutCtl == NULL)
{
OutCtl = new OutputControl(Client);
if (OutCtl == NULL)
{
return E_OUTOFMEMORY;
}
}
else
{
OutCtl->AddRef();
}
if ((hr = GetSessionSpace(Client, Session, &SessionSpace)) == S_OK)
{
DEBUG_VALUE ProcessSessionListAddr;
DEBUG_VALUE ProcessSessionListOffset;
DEBUG_VALUE ProcessesInSession;
DEBUG_VALUE Process;
BasicOutputParser OffsetReader(Client, 1);
BasicOutputParser SessionReader(Client, 2);
BasicOutputParser FlinkReader(Client, 1);
BasicOutputParser ErrorChecker(Client, 1);
OutputFilter OutFilter(Client);
OutputState OutState(Client);
CHAR szCommand[MAX_PATH];
// General parser setup and get ListEntry offset in _EPROCESS structure
if ((hr = OffsetReader.LookFor(&ProcessSessionListOffset, " +", DEBUG_VALUE_INT64)) == S_OK &&
(hr = FlinkReader.LookFor(&ProcessSessionListAddr, "Flink", DEBUG_VALUE_INT64)) == S_OK &&
(hr = ErrorChecker.LookFor(NULL, "Could not find _EPROCESS")) == S_OK &&
(hr = OutState.Setup(0, &OffsetReader)) == S_OK)
{
if ((hr = StringCbCopyA(szCommand, sizeof(szCommand),
"dt NT!_EPROCESS SessionProcessLinks")) == S_OK &&
(hr = OutState.Execute(szCommand)) == S_OK &&
(hr = OffsetReader.ParseOutput()) == S_OK)
{
hr = OffsetReader.Complete();
}
}
// Find first process and number of processes
if (hr == S_OK)
{
if (pProcessListing->m_oStartProcess &&
pProcessListing->m_cTotal)
{
Process.I64 = pProcessListing->m_oStartProcess;
ProcessesInSession.I32 =
pProcessListing->m_cTotal - pProcessListing->m_cProcessed;
}
else
{
if ((hr = SessionReader.LookFor(&ProcessSessionListAddr, "Flink", DEBUG_VALUE_INT64)) == S_OK &&
(hr = SessionReader.LookFor(&ProcessesInSession, "ProcessReferenceToSession", DEBUG_VALUE_INT32)) == S_OK &&
(hr = OutState.Setup(0, &SessionReader)) == S_OK)
{
if ((hr = StringCbPrintfA(
szCommand, sizeof(szCommand),
"dt NT!MM_SESSION_SPACE ProcessList.Flink ProcessReferenceToSession 0x%I64x",
SessionSpace)) == S_OK &&
(hr = OutState.Execute(szCommand)) == S_OK &&
(hr = SessionReader.ParseOutput()) == S_OK &&
(hr = SessionReader.Complete()) == S_OK)
{
Process.I64 = ProcessSessionListAddr.I64 - ProcessSessionListOffset.I64;
pProcessListing->m_cTotal = ProcessesInSession.I32;
pProcessListing->m_cProcessed = 0;
pProcessListing->m_oStartProcess = Process.I64;
pProcessListing->m_oLastProcess = Process.I64;
}
}
}
}
if (hr == S_OK)
{
ULONG cProcessed = 0;
OutCtl->Output("Processes is session: %lu\n", ProcessesInSession.I32);
// Dump all processes
while ((hr == S_OK) &&
(ProcessesInSession.I32 > 0) &&
(cProcessed < pProcessListing->m_cListLimit))
{
hr = StringCbPrintfA(
szCommand, sizeof(szCommand),
"!process 0x%I64x %s",
Process.I64, args);
// dump process and check that the process was valid
if (SUCCEEDED(hr) &&
(hr = OutState.Setup(0, &OutFilter)) == S_OK)
{
OutFilter.DiscardOutput();
hr = OutState.Execute(szCommand);
}
OutState.Setup(0, NULL);
OutFilter.OutputText(OutCtl, DEBUG_OUTPUT_NORMAL);
if (hr == S_OK)
{
// check that the process was valid
PSTR pszText = NULL;
if (SUCCEEDED(OutFilter.GetOutputCopy(&pszText)) &&
ErrorChecker.Ready() == S_OK &&
ErrorChecker.Parse(pszText, NULL) == S_OK &&
ErrorChecker.Complete() == S_OK)
{
hr = S_FALSE;
}
if (pszText)
{
OutFilter.FreeOutputCopy(pszText);
}
}
if (hr == S_OK)
{
// Get next process
FlinkReader.DiscardOutput();
FlinkReader.Relook();
hr = StringCbPrintfA(
szCommand, sizeof(szCommand),
"dt NT!_EPROCESS SessionProcessLinks.Flink 0x%I64x",
Process.I64);
if (SUCCEEDED(hr))
{
if ((hr = OutState.Setup(0, &FlinkReader)) == S_OK &&
(hr = OutState.Execute(szCommand)) == S_OK &&
(hr = FlinkReader.ParseOutput()) == S_OK &&
(hr = FlinkReader.Complete()) == S_OK)
{
Process.I64 = ProcessSessionListAddr.I64 - ProcessSessionListOffset.I64;
ProcessesInSession.I32--;
}
else
{
OutCtl->OutVerb("Couldn't get next process from '%s'\n", szCommand);
}
}
else
{
OutCtl->OutVerb("dt command string is too long for buffer.\n");
}
}
if (hr == S_OK)
{
cProcessed++;
pProcessListing->m_cProcessed++;
pProcessListing->m_oLastProcess = Process.I64;
}
}
if (ProcessesInSession.I32 > 0)
{
OutCtl->OutErr("%lu processes weren't dumped.\n", ProcessesInSession.I32);
}
}
}
else
{
OutCtl->OutErr("Couldn't get session %lu's data.\n", Session);
}
OutCtl->Release();
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_pExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;
do
{
if ((hr = GetPhysicalAddress(Client, DEFAULT_SESSION, NextAddr, &PhysAddr)) == S_OK)
{
if ((hr = g_pExtData->ReadPhysical(PhysAddr + 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(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' &&
Evaluate(Client, args+2,
DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX,
&OffsetToNextField, &NextArg,
NULL, 0) == S_OK)
{
args += 2 + NextArg;
while (*args && isspace(*args)) args++;
}
else
{
NeedHelp = TRUE;
}
}
if (!NeedHelp &&
S_OK == g_pExtControl->Evaluate(args, DEBUG_VALUE_INT64, &StartAddr, &NextArg))
{
args += NextArg;
if (S_OK != g_pExtControl->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(S_OK);
}
HRESULT
GetBitMap(
PDEBUG_CLIENT Client,
ULONG64 pBitMap,
PRTL_BITMAP *pBitMapOut
)
{
HRESULT hr;
PRTL_BITMAP p;
DEBUG_VALUE Size;
DEBUG_VALUE Buffer;
ULONG BufferLen;
ULONG BytesRead = 0;
OutputControl OutCtl(Client);
TypeOutputParser BitMapParser(Client);
OutputState OutState(Client);
*pBitMapOut = NULL;
if ((hr = OutState.Setup(0, &BitMapParser)) == S_OK &&
(hr = OutState.OutputTypeVirtual(pBitMap, "NT!_RTL_BITMAP", 0)) == S_OK &&
(hr = BitMapParser.Get(&Size, "SizeOfBitMap", DEBUG_VALUE_INT32)) == S_OK &&
(hr = BitMapParser.Get(&Buffer, "Buffer", DEBUG_VALUE_INT64)) == S_OK)
{
PDEBUG_DATA_SPACES Data;
if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&Data)) == S_OK)
{
BufferLen = (Size.I32 + 7) / 8;
#if DBG
OutCtl.OutVerb("Reading RTL_BITMAP @ 0x%p:\n"
" SizeOfBitMap: %lu\n"
" Buffer: 0x%p\n"
" Length in bytes: 0x%lx\n",
pBitMap,
Size.I32,
Buffer.I64,
BufferLen);
#endif
p = (PRTL_BITMAP) HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) + BufferLen );
if (p != NULL)
{
RtlInitializeBitMap(p, (PULONG)(p + 1), Size.I32);
hr = Data->ReadVirtual(Buffer.I64, p->Buffer, BufferLen, &BytesRead);
if (hr != S_OK)
{
OutCtl.OutErr("Error reading bitmap contents @ 0x%p\n", Buffer.I64);
}
else if (BytesRead < BufferLen)
{
OutCtl.OutErr("Error reading bitmap contents @ 0x%p\n", Buffer.I64 + BytesRead);
hr = E_FAIL;
}
if (hr != S_OK)
{
HeapFree( GetProcessHeap(), 0, p );
p = NULL;
}
else
{
*pBitMapOut = p;
}
}
else
{
hr = E_OUTOFMEMORY;
}
Data->Release();
}
else
{
OutCtl.OutErr("Error setting up debugger interface.\n");
}
}
else
{
OutCtl.OutErr("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(
OutputControl *OutCtl,
ULONG64 PoolAddr,
ULONG TagFilter,
TypeOutputParser *PoolHeadReader,
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 = PoolHeadReader->Get(&PoolType, "PoolType", DEBUG_VALUE_INT32);
if (hr == S_OK)
{
if (PoolType.I32 == 0)
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
else
{
DEBUG_VALUE PoolIndex;
if (!NewPool)
{
hr = PoolHeadReader->Get(&PoolIndex, "PoolIndex", DEBUG_VALUE_INT32);
}
if (hr == S_OK)
{
if (NewPool ? (PoolType.I32 & 0x04) : (PoolIndex.I32 & 0x80))
{
AllocStatsAccum->Allocated++;
AllocStatsAccum->AllocatedSize += BlockSize;
if (AllocStatsAccum->Allocated % 100 == 0)
{
OutCtl->Output(".");
if (AllocStatsAccum->Allocated % 8000 == 0)
{
OutCtl->Output("\n");
}
}
}
else
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
else
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
}
else
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
return hr;
}
HRESULT
CheckPrintAndAccumFilter(
OutputControl *OutCtl,
ULONG64 PoolAddr,
ULONG TagFilter,
TypeOutputParser *PoolHeadReader,
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;
}
OutCtl->Output("0x%p size: %5lx ",//previous size: %4lx ",
PoolAddr,
BlockSize << POOL_BLOCK_SHIFT/*,
PreviousSize << POOL_BLOCK_SHIFT*/);
hr = PoolHeadReader->Get(&PoolType, "PoolType", DEBUG_VALUE_INT32);
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.
//
OutCtl->Output(" (Free)");
OutCtl->Output(" %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 = PoolHeadReader->Get(&PoolIndex, "PoolIndex", DEBUG_VALUE_INT32);
}
if (hr == S_OK)
{
if (NewPool ? (PoolType.I32 & 0x04) : (PoolIndex.I32 & 0x80))
{
OutCtl->Output(" (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.
//
OutCtl->Output(" (Free ) ");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Free++;
AllocStatsAccum->FreeSize += BlockSize;
}
}
}
else
{
OutCtl->Output(" (?) ");
if (AllocStatsAccum != NULL)
{
AllocStatsAccum->Indeterminate++;
AllocStatsAccum->IndeterminateSize += BlockSize;
}
}
if (!(PoolType.I32 & POOL_QUOTA_MASK) ||
bQuotaWithTag)
{
OutCtl->Output(" %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 &&
PoolHeadReader->Get(&ProcessBilled, "ProcessBilled", DEBUG_VALUE_INT64) == S_OK &&
ProcessBilled.I64 != 0)
{
OutCtl->Output(" Process: 0x%p\n", ProcessBilled.I64);
}
else
{
OutCtl->Output("\n");
}
}
}
else
{
OutCtl->OutErr(" 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(OutputControl *OutCtl, 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(
OutputControl *OutCtl,
BOOL AllocSort
)
{
HRESULT hr;
PTAG_BUCKET pB, pBNext;
ULONG i;
if (Bucket == NULL)
{
hr = OutCtl->Output(" 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;
OutCtl->Output("\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);
OutCtl->Output("\nTag Allocs Bytes Freed Bytes");
if (Indeterminate != 0)
{
OutCtl->Output(" Unknown Bytes");
pszOutFormat = szShowIndeterminate;
}
OutCtl->Output("\n");
if (AllocSort)
{
OutCtl->OutWarn(" 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)
{
OutCtl->Output(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
);
}
}
}
}
OutCtl->Output("-------------------------------------------------------------------------------\n");
hr = OutCtl->Output(pszOutFormat,
"Ttl:",
Allocated, ((ULONG64)AllocatedSize) << POOL_BLOCK_SHIFT,
Free, ((ULONG64)FreeSize) << POOL_BLOCK_SHIFT,
Indeterminate, ((ULONG64)IndeterminateSize) << POOL_BLOCK_SHIFT
);
}
return hr;
}
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(
OutputControl *OutCtl,
ULONG64 PoolAddr,
ULONG TagFilter,
TypeOutputParser *PoolHeadReader,
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 = PoolHeadReader->Get(&PoolType, "PoolType", DEBUG_VALUE_INT32);
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 = PoolHeadReader->Get(&PoolIndex, "PoolIndex", DEBUG_VALUE_INT32);
}
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)
{
OutCtl->Output(".");
if (AllocStatsAccum->Allocated % 8000 == 0)
{
OutCtl->Output("\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;
OutputControl OutCtl(Client);
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(Client, Session, &SessionSpace)) == S_OK)
{
ULONG PoolTagOffset, ProcessBilledOffset;
BOOL bQuotaWithTag;
DEBUG_VALUE ReadSessionId;
DEBUG_VALUE PagedPoolInfo;
DEBUG_VALUE NonPagedPoolBytes;
DEBUG_VALUE NonPagedPoolAllocations;
DEBUG_VALUE NonPagedPoolStart;
DEBUG_VALUE NonPagedPoolEnd;
DEBUG_VALUE PagedPoolPages;
DEBUG_VALUE PagedPoolBytes;
DEBUG_VALUE PagedPoolAllocations;
DEBUG_VALUE PagedPoolStart;
DEBUG_VALUE PagedPoolEnd;
ULONG SessionSpaceTypeID;
ULONG PagedPoolInfoOffset;
TypeOutputParser SessionParser(Client); // Dump of MM_SESSION_SPACE
TypeOutputParser PoolHeadReader(Client);
OutputState OutState(Client);
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 = OutState.Setup(0, &SessionParser)) == S_OK &&
(hr = Symbols->GetTypeId(NTModuleBase, "MM_SESSION_SPACE", &SessionSpaceTypeID)) == S_OK &&
((hr = OutState.OutputTypeVirtual(SessionSpace, NTModuleBase, SessionSpaceTypeID, DEBUG_OUTTYPE_BLOCK_RECURSE)) == S_OK ||
((hr = OutState.OutputTypeVirtual(SessionSpace, NTModuleBase, SessionSpaceTypeID, 0)) == S_OK &&
(hr = Symbols->GetFieldOffset(NTModuleBase, SessionSpaceTypeID, "PagedPoolInfo", &PagedPoolInfoOffset)) == S_OK &&
(hr = OutState.OutputTypeVirtual(SessionSpace + PagedPoolInfoOffset, "NT!_MM_PAGED_POOL_INFO", 0)) == S_OK
)) &&
(hr = SessionParser.Get(&ReadSessionId, "SessionId", DEBUG_VALUE_INT32)) == S_OK)
{
OutCtl.Output("Searching session %ld pool.\n", ReadSessionId.I32);
// Remaining type output goes to PoolHead reader
hr = OutState.Setup(0, &PoolHeadReader);
}
else
{
OutCtl.OutErr("Error getting basic session pool information.\n");
}
while (hr == S_OK && Continue)
{
OutCtl.Output("\n");
if (PoolTypeFlags & SEARCH_POOL_NONPAGED)
{
if (SessionParser.Get(&NonPagedPoolBytes, "NonPagedPoolBytes", DEBUG_VALUE_INT64) == S_OK &&
SessionParser.Get(&NonPagedPoolAllocations, "NonPagedPoolAllocations", DEBUG_VALUE_INT64) == S_OK)
{
OutCtl.Output("NonPaged pool: %I64u bytes in %I64u allocations\n",
NonPagedPoolBytes.I64, NonPagedPoolAllocations.I64);
}
OutCtl.Output(" NonPaged pool range reader isn't implemented.\n");
PoolStart = 0;
PoolEnd = 0;
SearchingPaged = FALSE;
}
else
{
if (SessionParser.Get(&PagedPoolBytes, "PagedPoolBytes", DEBUG_VALUE_INT64) == S_OK &&
SessionParser.Get(&PagedPoolAllocations, "PagedPoolAllocations", DEBUG_VALUE_INT64) == S_OK)
{
OutCtl.Output("Paged pool: %I64u bytes in %I64u allocations\n",
PagedPoolBytes.I64, PagedPoolAllocations.I64);
if (SessionParser.Get(&PagedPoolPages, "AllocatedPagedPool", DEBUG_VALUE_INT64) == S_OK)
{
OutCtl.Output(" Paged Pool Info: %I64u pages allocated\n",
PagedPoolPages.I64);
}
}
if ((hr = SessionParser.Get(&PagedPoolStart, "PagedPoolStart", DEBUG_VALUE_INT64)) != S_OK ||
(hr = SessionParser.Get(&PagedPoolEnd, "PagedPoolEnd", DEBUG_VALUE_INT64)) != S_OK)
{
OutCtl.OutErr(" Couldn't get PagedPool range.\n");
}
else
{
PoolStart = PagedPoolStart.I64;
PoolEnd = PagedPoolEnd.I64;
SearchingPaged = TRUE;
DEBUG_VALUE PagedBitMap;
if (SessionParser.Get(&PagedBitMap, "PagedPoolAllocationMap", DEBUG_VALUE_INT64) == S_OK &&
GetBitMap(Client, PagedBitMap.I64, &PagedPoolAllocationMap) == S_OK &&
SessionParser.Get(&PagedBitMap, "EndOfPagedPoolBitmap", DEBUG_VALUE_INT64) == S_OK &&
GetBitMap(Client, PagedBitMap.I64, &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;
if (SessionParser.Get(&PagedBitMap, "PagedPoolLargeSessionAllocationMap", DEBUG_VALUE_INT64) == S_OK &&
GetBitMap(Client, PagedBitMap.I64, &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
{
OutCtl.OutWarn(" Warning Large Pool Allocation Map or End Of Pool Map is invalid.\n");
break;
}
}
else
{
OutCtl.OutWarn(" Warning Large Pool Allocation Map is invalid.\n");
break;
}
}
if (LargeAllocs != 0)
{
OutCtl.OutWarn(" %lu large allocations weren't calculated.\n", LargeAllocs);
AllocStats->LargeAllocs -= LargeAllocs;
}
}
}
}
}
if (hr == S_OK)
{
OutCtl.Output("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)
{
OutCtl.Output("Currently only searching the current session is supported.\n");
PoolPage = PoolEnd;
break;
}
if (OutCtl.GetInterrupt() == S_OK)
{
OutCtl.Output("\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)
{
OutCtl.OutWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) >> PageShift;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (EndPosition == -1)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
OutCtl.OutWarn("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)
{
OutCtl.OutWarn("No end to large pool allocation @ 0x%p found.\n", Pool);
PoolPage = PoolEnd;
}
else
{
EndPosition++;
if (PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
OutCtl.OutWarn(" 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)
{
OutCtl.Output("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)
{
OutCtl.OutVerb(" 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)
{
OutCtl.OutWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) >> PageShift;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
OutCtl.OutWarn("No remaining pool allocations from 0x%p to 0x%p.\n", Pool, PoolEnd);
}
PoolPage = PoolEnd;
continue;
}
else if (StartPosition >= BusyStart)
{
OutCtl.OutWarn("Found end of allocation at %lu within expansion pages starting at %lu.\n",
EndPosition, BusyStart);
}
}
}
}
if (Flags & SEARCH_POOL_LARGE_ONLY)
{
OutCtl.OutErr(" 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;
PoolHeadReader.DiscardOutput();
if ((hr = OutState.OutputType(FALSE, Pool, NTModuleBase, PoolHeadTypeID, 0)) != S_OK ||
PoolHeadReader.Get(&HdrPoolTag, "PoolTag", DEBUG_VALUE_INT32) != S_OK)
{
if (hr != S_OK)
{
PSTR psz;
OutCtl.OutErr("Type read error %s @ 0x%p.\n", pszHRESULT(hr), Pool);
OutCtl.OutWarn("Failed to read an allocated page @ 0x%p.\n", StartPage);
if (PoolHeadReader.GetOutputCopy(&psz) == S_OK)
{
if (strchr(psz, '?') != NULL)
{
hr = S_OK;
}
else
{
OutCtl.OutVerb("PoolHeadReader contains:\n"
"%s\n", psz);
OutCtl.Output("\n...terminating - searched pool to 0x%p\n",
Pool);
hr = E_ABORT;
}
PoolHeadReader.FreeOutputCopy(psz);
}
if (hr == E_ABORT)
{
break;
}
}
if (!PageReadFailed)
{
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
OutCtl.OutWarn(" Couldn't read pool from 0x%p", Pool);
}
PagesRead += (StartPage - LastPageRead) / PageSize;
LastPageRead = StartPage;
PageReadFailed = TRUE;
}
if ((hr = GetNextResidentAddress(Client,
Session,
StartPage + PageSize,
PoolEnd,
&PoolPage,
NULL)) != S_OK)
{
if (hr != E_ABORT)
{
hr = S_OK;
}
if (Flags & SEARCH_POOL_PRINT_UNREAD)
{
OutCtl.OutWarn(" to 0x%p.\n", PoolEnd);
OutCtl.OutWarn("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)
{
OutCtl.OutWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailures += (StartPage - LastPageRead) / PageSize;
LastPageRead = StartPage;
PageReadFailed = FALSE;
}
if (PoolHeadReader.Get(&BlockSize, "BlockSize", DEBUG_VALUE_INT32) != S_OK)
{
OutCtl.OutErr("Error reading BlockSize @ 0x%p.\n", Pool);
break;
}
if ((BlockSize.I32 << POOL_BLOCK_SHIFT) > PageSize)//POOL_PAGE_SIZE)
{
OutCtl.OutVerb("Bad allocation size @ 0x%p, too large\n", Pool);
break;
}
if (BlockSize.I32 == 0)
{
OutCtl.OutVerb("Bad allocation size @ 0x%p, zero is invalid\n", Pool);
break;
}
if (PoolHeadReader.Get(&PreviousSize, "PreviousSize", DEBUG_VALUE_INT32) != S_OK ||
PreviousSize.I32 != Previous)
{
OutCtl.OutVerb("Bad previous allocation size @ 0x%p, last size was 0x%lx\n", Pool, Previous);
break;
}
Filter(&OutCtl,
Pool,
TagName,
&PoolHeadReader,
&HdrPoolTag,
BlockSize.I32,
bQuotaWithTag,
Context
);
Previous = BlockSize.I32;
Pool += (Previous << POOL_BLOCK_SHIFT);
if ( OutCtl.GetInterrupt() == S_OK)
{
OutCtl.Output("\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)
{
OutCtl.OutWarn(" to 0x%p\n", StartPage-1);
}
PageReadFailuresAtEnd = (PoolPage - LastPageRead) / PageSize;
PageReadFailures += PageReadFailuresAtEnd;
PageReadFailed = FALSE;
}
else
{
PagesRead += (PoolPage - LastPageRead) / PageSize;
}
OutCtl.Output(" 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;
OutputControl OutCtl(Client);
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')
{
hr = Evaluate(Client, TagArg, DEBUG_VALUE_INT64,
EVALUATE_DEFAULT_RADIX, TagFilter,
NULL, NULL,
EVALUATE_COMPACT_EXPR);
}
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])))
)
)
{
OutCtl.OutErr(" 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)
{
OutCtl.OutWarn(" 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(
OutputControl *OutCtl,
PALLOCATION_STATS AllocStats,
BOOL PartialResults
)
{
return OutCtl->Output("\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
);
}
DECLARE_API( spoolfind )
{
HRESULT hr = S_OK;
BEGIN_API( spoolfind );
BOOL BadArg = FALSE;
ULONG RemainingArgIndex;
DEBUG_VALUE TagName = { 0, DEBUG_VALUE_INVALID };
FLONG Flags = 0;
DEBUG_VALUE Session = { DEFAULT_SESSION, DEBUG_VALUE_INVALID };
OutputControl OutCtl(Client);
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)
{
OutCtl.OutErr("Session argument specified multiple times.\n");
BadArg = TRUE;
}
else
{
args++;
hr = Evaluate(Client, args, DEBUG_VALUE_INT32, 10, &Session, &RemainingArgIndex);
if (hr != S_OK)
{
OutCtl.OutErr("Invalid Session.\n");
}
else
{
args += RemainingArgIndex;
}
}
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
{
OutCtl.OutErr("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
{
OutCtl.OutErr("Missing Tag.\n");
hr = E_INVALIDARG;
}
}
}
if (BadArg || hr != S_OK)
{
if (*args == '?')
{
OutCtl.Output("spoolfind is like !kdexts.poolfind, but for the SessionId specified.\n"
"\n");
}
OutCtl.Output("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(&OutCtl, &AllocStats, (hr != S_OK));
}
else
{
OutCtl.OutWarn("SearchSessionPool returned %s\n", pszHRESULT(hr));
}
}
return hr;
}
DECLARE_API( spoolsum )
{
HRESULT hr = S_OK;
BEGIN_API( spoolsum );
ULONG RemainingArgIndex;
FLONG Flags = 0;
DEBUG_VALUE Session = { DEFAULT_SESSION, DEBUG_VALUE_INVALID };
OutputControl OutCtl(Client);
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)
{
OutCtl.OutErr("Session argument specified multiple times.\n");
hr = E_INVALIDARG;
}
else
{
args++;
hr = Evaluate(Client, args, DEBUG_VALUE_INT32, 10, &Session, &RemainingArgIndex);
if (hr != S_OK)
{
OutCtl.OutErr("Invalid Session.\n");
}
else
{
args += RemainingArgIndex;
}
}
break;
default:
hr = E_INVALIDARG;
break;
}
if (hr != S_OK) break;
}
}
else
{
if (*args == '\0') break;
if (Session.Type == DEBUG_VALUE_INVALID)
{
hr = Evaluate(Client, args, DEBUG_VALUE_INT32, 10, &Session, &RemainingArgIndex);
if (hr != S_OK)
{
OutCtl.OutErr("Invalid Session.\n");
}
else
{
args += RemainingArgIndex;
}
}
else
{
OutCtl.OutErr("Unrecognized argument @ %s\n", args);
hr = E_INVALIDARG;
}
}
}
if (hr != S_OK)
{
if (*args == '?')
{
OutCtl.Output("spoolsum summarizes session pool information for SessionId specified.\n"
"\n");
hr = S_OK;
}
OutCtl.Output("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(&OutCtl, &AllocStats, (hr != S_OK));
}
else
{
OutCtl.OutWarn("SearchSessionPool returned %s\n", pszHRESULT(hr));
}
}
return hr;
}
DECLARE_API( spoolused )
{
HRESULT hr = S_OK;
BEGIN_API( spoolused );
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 };
OutputControl OutCtl(Client);
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)
{
OutCtl.OutErr("Session argument specified multiple times.\n");
BadArg = TRUE;
}
else
{
args++;
hr = Evaluate(Client, args, DEBUG_VALUE_INT32, 10, &Session, &RemainingArgIndex);
if (hr != S_OK)
{
OutCtl.OutErr("Invalid Session.\n");
}
else
{
args += RemainingArgIndex;
}
}
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
{
OutCtl.OutErr("Unrecognized argument @ %s\n", args);
BadArg = TRUE;
}
}
}
if (BadArg || hr != S_OK)
{
if (*args == '?')
{
OutCtl.Output("spoolused is like !kdexts.poolused, but for the SessionId specified.\n"
"\n");
}
OutCtl.Output("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)
{
OutCtl.OutErr("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(&OutCtl, AllocSort);
}
else
{
OutCtl.OutWarn("SearchSessionPool returned %s\n", pszHRESULT(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(&OutCtl, AllocSort);
}
else
{
OutCtl.OutWarn("SearchSessionPool returned %s\n", pszHRESULT(hr));
}
}
}
return hr;
}