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