/*++ 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; }