/*++ Copyright (c) 1989 Microsoft Corporation Module Name: vadump.c Abstract: This module contains the routines to dump the virtual address space of a process. Author: Lou Perazzoli (loup) 22-May-1989 Landy Wang (landyw) 02-June-1997 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #include "psapi.h" #define SYM_HANDLE INVALID_HANDLE_VALUE #define DEFAULT_INCR (64*1024) #define P2KB(x) (((x) * SystemInfo.dwPageSize) / 1024) #define MAX_SYMNAME_SIZE 1024 CHAR symBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE]; PIMAGEHLP_SYMBOL ThisSymbol; ULONG_PTR SystemRangeStart; LIST_ENTRY VaList; ULONG_PTR ProcessId; PCHAR ExeName; ULONG_PTR IsSystemWithShareCount = 0; ULONG_PTR PageSize; ULONG_PTR PtesPerPage; ULONG_PTR PteWidth; PVOID PteBase; PVOID UserPteMax; ULONG_PTR VaMappedByPageTable; #define IS_USER_PAGE_TABLE_PAGE(Va) (((PVOID)(Va) >= PteBase) && ((PVOID)(Va) < UserPteMax)) SYSTEM_INFO SystemInfo; typedef struct _VAINFO { LIST_ENTRY Links; LIST_ENTRY AllocationBaseHead; MEMORY_BASIC_INFORMATION BasicInfo; } VAINFO, *PVAINFO; PVAINFO LastAllocationBase; SIZE_T ReservedBytes; SIZE_T FreeBytes; SIZE_T ImageReservedBytes; SIZE_T ImageFreeBytes; SIZE_T Displacement; #define OPTIONS_CODE_TOO 0x1 #define OPTIONS_RAW_SYMBOLS 0x2 #define OPTIONS_VERBOSE 0x4 #define OPTIONS_WORKING_SET 0x8 #define OPTIONS_WORKING_SET_OLD 0x10 #define OPTIONS_PAGE_TABLES 0x20 ULONG Options; BOOLEAN fSummary = FALSE; BOOLEAN fFast = FALSE; BOOLEAN fRunning = FALSE; #define NOACCESS 0 #define READONLY 1 #define READWRITE 2 #define WRITECOPY 3 #define EXECUTE 4 #define EXECUTEREAD 5 #define EXECUTEREADWRITE 6 #define EXECUTEWRITECOPY 7 #define MAXPROTECT 8 ULONG_PTR ImageCommit[MAXPROTECT]; ULONG_PTR MappedCommit[MAXPROTECT]; ULONG_PTR PrivateCommit[MAXPROTECT]; CHAR LogFileName[256]; FILE *LogFile; BOOL InCtrlc = FALSE; typedef struct _WSINFOCOUNTS { ULONG_PTR FaultingPc; ULONG Faults; } WSINFOCOUNTS, *PWSINFOCOUNTS; typedef struct _MODINFO { PVOID BaseAddress; ULONG VirtualSize; LPSTR Name; ULONG_PTR CommitVector[MAXPROTECT]; ULONG WsHits; ULONG WsSharedHits; ULONG WsPrivateHits; BOOL SymbolsLoaded; } MODINFO, *PMODINFO; #define MODINFO_SIZE 100 ULONG ModInfoMax; MODINFO ModInfo[MODINFO_SIZE]; BOOLEAN bHitModuleMax = FALSE; typedef struct _SYSTEM_PAGE { ULONG_PTR Va; PVOID BaseAddress; ULONG ResidentPages; } SYSTEM_PAGE, *PSYSTEM_PAGE; // // room for 4 million pagefaults // #define MAX_RUNNING_WORKING_SET_BUFFER (4*1024*1024) ULONG_PTR RunningWorkingSetBuffer[MAX_RUNNING_WORKING_SET_BUFFER]; LONG CurrentWsIndex; #define INITIAL_WORKING_SET_BLOCK_ENTRYS 4000 PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo; #define WORKING_SET_BUFFER_ENTRYS 64*1024 PROCESS_WS_WATCH_INFORMATION NewWorkingSetBuffer[WORKING_SET_BUFFER_ENTRYS]; const PCHAR ProtectTable[] = { "NoAccess", "ReadOnly", "Execute", "ExecuteRead", "ReadWrite", "WriteCopy", "ExecuteReadWrite", "ExecuteWriteCopy", "NoAccess", "ReadOnly Nocache", "Execute Nocache", "ExecuteRead Nocache", "ReadWrite Nocache", "WriteCopy Nocache", "ExecuteReadWrite Nocache", "ExecuteWriteCopy Nocache", "NoAccess", "ReadOnly Guard", "Execute Guard", "ExecuteRead Guard", "ReadWrite Guard", "WriteCopy Guard", "ExecuteReadWrite Guard", "ExecuteWriteCopy Guard", "NoAccess", "ReadOnly Nocache Guard", "Execute Nocache Guard", "ExecuteRead Nocache Guard", "ReadWrite Nocache Guard", "WriteCopy Nocache Guard", "ExecuteReadWrite Nocache Guard", "ExecuteWriteCopy Nocache Guard" }; const PCHAR SharedTable[] = { " ", "Shared" }; LIST_ENTRY LoadedHeapList; int UnknownHeapCount = 0; typedef struct _LOADED_HEAP_SEGMENT { PVOID BaseVa; ULONG Length; ULONG HitsFromThisSegment; } LOADED_HEAP_SEGMENT, *PLOADED_HEAP_SEGMENT; typedef struct _LOADED_HEAP { LIST_ENTRY HeapsList; LPSTR HeapName; ULONG HitsFromThisHeap; PVOID HeapAddress; ULONG HeapClass; LOADED_HEAP_SEGMENT Segments[ HEAP_MAXIMUM_SEGMENTS ]; } LOADED_HEAP, *PLOADED_HEAP; typedef struct _LOADED_THREAD { HANDLE ThreadID; PBYTE ThreadTEB; PBYTE StackBase; PBYTE StackEnd; ULONG HitsFromThisStack; } LOADED_THREAD, *PLOADED_THREAD; ULONG NumberOfThreads = 0; PLOADED_THREAD TheThreads; LOGICAL SetCurrentPrivilege( IN LPCTSTR Privilege, // Privilege to enable/disable IN OUT BOOL *bEnablePrivilege // to enable or disable privilege ); VOID Usage( VOID ); void ConvertAppToOem ( IN unsigned argc, IN char* argv[] ) /*++ Routine Description: Converts the command line from ANSI to OEM, and force the app to use OEM APIs. Arguments: argc - Standard C argument count. argv - Standard C argument strings. Return Value: None. --*/ { ULONG i; LPSTR pSrc; LPSTR pDst; WCHAR Wide; for (i = 0; i < argc; i += 1) { pSrc = argv[i]; pDst = argv[i]; do { // // Convert Ansi to Unicode and then to OEM. // MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, pSrc++, 1, &Wide, 1); WideCharToMultiByte (CP_OEMCP, 0, &Wide, 1, pDst++, 1, "_", NULL); } while (*pSrc); } SetFileApisToOEM (); } BOOLEAN FindAndIncHeapContainingThisVa ( IN PVOID Va, IN ULONG ShareCount ) { PLIST_ENTRY Next; PLOADED_HEAP pHeap; PLOADED_HEAP_SEGMENT Segment; PLOADED_HEAP_SEGMENT LastSegment; Next = LoadedHeapList.Flink; while (Next != &LoadedHeapList) { pHeap = CONTAINING_RECORD(Next, LOADED_HEAP, HeapsList); Segment = pHeap->Segments; LastSegment = Segment + HEAP_MAXIMUM_SEGMENTS; Next = Next->Flink; while (Segment < LastSegment) { if (Segment->BaseVa == NULL) { break; } if ((Va > Segment->BaseVa) && (Va < (PVOID)((ULONG_PTR)Segment->BaseVa + Segment->Length))) { pHeap->HitsFromThisHeap += 1; Segment->HitsFromThisSegment += 1; if (ShareCount > 1) { fprintf(stderr, "Error: Heap ShareCount > 1, 0x%p\n", Va); } if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("%s\n", pHeap->HeapName); } return TRUE; } Segment += 1; } } return FALSE; } VOID DumpLoadedHeap ( IN PLOADED_HEAP LoadedHeap ) { PLOADED_HEAP_SEGMENT Segment; PLOADED_HEAP_SEGMENT LastSegment; printf ("%4d pages from %s (class 0x%08x)\n", LoadedHeap->HitsFromThisHeap, LoadedHeap->HeapName, LoadedHeap->HeapClass); Segment = LoadedHeap->Segments; LastSegment = Segment + HEAP_MAXIMUM_SEGMENTS; while (Segment < LastSegment) { if (Segment->BaseVa == NULL) { break; } printf("\t0x%p - 0x%p %d pages\n", Segment->BaseVa, (ULONG_PTR)Segment->BaseVa + Segment->Length, Segment->HitsFromThisSegment); Segment += 1; } } VOID LoadTheHeaps ( IN HANDLE Process ) { HEAP TheHeap; PLOADED_HEAP LoadedHeap; PHEAP *ProcessHeaps; HEAP_SEGMENT TheSegment; BOOL b; ULONG cb, i, j; NTSTATUS Status; PROCESS_BASIC_INFORMATION ProcessInformation; PEB ThePeb; InitializeListHead (&LoadedHeapList); Status = NtQueryInformationProcess (Process, ProcessBasicInformation, &ProcessInformation, sizeof( ProcessInformation ), NULL); if (!NT_SUCCESS (Status)) { fprintf(stderr, "NtQueryInformationProcess for ProcessBasicInformation" " failed %lx\n", GetLastError()); return; } // // Read the process's PEB. // b = ReadProcessMemory (Process, ProcessInformation.PebBaseAddress, &ThePeb,sizeof(ThePeb), NULL); if (!b) { return; } // // Allocate space for and read the array of process heap pointers. // cb = ThePeb.NumberOfHeaps * sizeof( PHEAP ); ProcessHeaps = LocalAlloc(LMEM_ZEROINIT,cb); if (ProcessHeaps == NULL) { return; } b = ReadProcessMemory (Process, ThePeb.ProcessHeaps, ProcessHeaps, cb, NULL); if (b) { for (i = 0; i < ThePeb.NumberOfHeaps; i += 1) { // // Read the heap. // b = ReadProcessMemory (Process, ProcessHeaps[i], &TheHeap, sizeof(TheHeap), NULL); if (!b) { break; } // // We got the heap, now initialize our heap structure // LoadedHeap = LocalAlloc (LMEM_ZEROINIT, sizeof(*LoadedHeap)); if (!LoadedHeap) { break; } LoadedHeap->HeapAddress = ProcessHeaps[i]; LoadedHeap->HeapClass = TheHeap.Flags & HEAP_CLASS_MASK; switch ( LoadedHeap->HeapClass ) { case HEAP_CLASS_0: LoadedHeap->HeapName = "Process Heap"; break; case HEAP_CLASS_1: LoadedHeap->HeapName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 16); if (LoadedHeap->HeapName) { sprintf(LoadedHeap->HeapName, "Private Heap %d", UnknownHeapCount++); } else { LoadedHeap->HeapName = "Private Heap"; } break; case HEAP_CLASS_2: LoadedHeap->HeapName = "Kernel Heap"; break; case HEAP_CLASS_3: LoadedHeap->HeapName = "GDI Heap"; break; case HEAP_CLASS_4: LoadedHeap->HeapName = "User Heap"; break; case HEAP_CLASS_5: LoadedHeap->HeapName = "Console Heap"; break; case HEAP_CLASS_6: LoadedHeap->HeapName = "User Desktop Heap"; break; case HEAP_CLASS_7: LoadedHeap->HeapName = "Csrss Shared Heap"; break; default: LoadedHeap->HeapName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 16); if (LoadedHeap->HeapName) { sprintf(LoadedHeap->HeapName, "UNKNOWN Heap %d", UnknownHeapCount++); } else { LoadedHeap->HeapName = "UNKNOWN Heap"; } break; } // // Now go through the heap segments to compute the // area covered by the heap. // for (j = 0; j < HEAP_MAXIMUM_SEGMENTS; j += 1) { if (!TheHeap.Segments[j]) { break; } b = ReadProcessMemory (Process, TheHeap.Segments[j], &TheSegment, sizeof(TheSegment), NULL); if (!b) { break; } LoadedHeap->Segments[j].BaseVa = TheSegment.BaseAddress; LoadedHeap->Segments[j].Length = TheSegment.NumberOfPages * SystemInfo.dwPageSize; } InsertTailList (&LoadedHeapList,&LoadedHeap->HeapsList); } } LocalFree (ProcessHeaps); return; } BOOLEAN FindAndIncStackContainingThisVa ( IN PBYTE Va, IN ULONG ShareCount ) { ULONG i; for (i = 0 ; i < NumberOfThreads ; i++) { if ((Va > TheThreads[i].StackBase) && (Va < TheThreads[i].StackEnd)) { TheThreads[i].HitsFromThisStack++; if (ShareCount > 1) { fprintf(stderr, "Error: Stack ShareCount > 1, 0x%p\n", Va); } if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("Stack for ThreadID %p\n", TheThreads[i].ThreadID); } return TRUE; } } return FALSE; } VOID DumpLoadedStacks ( VOID ) { ULONG i; for (i = 0 ; i < NumberOfThreads ; i++) { printf("%4d pages from stack for thread %p\n", TheThreads[i].HitsFromThisStack, TheThreads[i].ThreadID); } return; } VOID LoadTheThreads ( IN HANDLE Process, IN ULONG_PTR ProcessID ) { BOOL b; ULONG i; NTSTATUS Status; PSYSTEM_PROCESS_INFORMATION ProcessInfo; PSYSTEM_THREAD_INFORMATION ThreadInfo; THREAD_BASIC_INFORMATION ThreadBasicInfo; TEB TheTeb; HANDLE Thread; OBJECT_ATTRIBUTES Obja; // // To get the thread IDs of the process, load the system process info // and look for the matching process. For each thread in it, open the // thread to get the Teb address, then, read the stack information from // the Teb in the processes memory. // Status = NtQuerySystemInformation( SystemProcessInformation, &RunningWorkingSetBuffer, // not in use yet for WS 512*1024, // don't give the whole thing // or it will be probed NULL ); if (!NT_SUCCESS(Status)) { fprintf(stderr, "NtQuerySystemInformation for SystemProcessInformation" " failed %lx\n", GetLastError()); return; } ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &RunningWorkingSetBuffer; while (ProcessInfo) { if (ProcessInfo->UniqueProcessId == (HANDLE)ProcessID) { break; } if (ProcessInfo->NextEntryOffset == 0) { ProcessInfo = NULL; break; } ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) ((PBYTE)ProcessInfo + ProcessInfo->NextEntryOffset); } if (ProcessInfo == NULL) { fprintf(stderr, "Error: Failed to find process for Stack lookup\n"); return; } ThreadInfo = (PSYSTEM_THREAD_INFORMATION) (ProcessInfo + 1); NumberOfThreads = ProcessInfo->NumberOfThreads; TheThreads = (PLOADED_THREAD) LocalAlloc(LMEM_ZEROINIT, sizeof(LOADED_THREAD) * NumberOfThreads); if (TheThreads == NULL) { printf("FAILURE: Couldn't allocate memory for thread database\n"); ExitProcess(0); } InitializeObjectAttributes(&Obja, NULL, 0, NULL, NULL); for (i = 0; i < NumberOfThreads; i += 1, ThreadInfo += 1) { Status = NtOpenThread (&Thread, MAXIMUM_ALLOWED, &Obja, &ThreadInfo->ClientId); if (!NT_SUCCESS( Status )) { fprintf(stderr, "NtOpenThread %p failed %lx\n", ThreadInfo->ClientId.UniqueThread, GetLastError()); return; } Status = NtQueryInformationThread (Thread, ThreadBasicInformation, &ThreadBasicInfo, sizeof( ThreadBasicInfo ), NULL); if (!NT_SUCCESS( Status )) { fprintf(stderr, "NtQueryInformationThread for" " ThreadBasicInformation failed %lx\n", GetLastError()); CloseHandle (Thread); return; } // // Read the threads's TEB. // b = ReadProcessMemory (Process, ThreadBasicInfo.TebBaseAddress, &TheTeb, sizeof(TheTeb), NULL); if (!b) { fprintf(stderr, "ReadProcessMemory for" " TEB %d failed %lx\n", i, GetLastError()); CloseHandle (Thread); return; } TheThreads[i].ThreadID = TheTeb.ClientId.UniqueThread; TheThreads[i].ThreadTEB = (PBYTE)ThreadBasicInfo.TebBaseAddress; TheThreads[i].StackBase = TheTeb.DeallocationStack; TheThreads[i].StackEnd = TheTeb.NtTib.StackBase; CloseHandle(Thread); } } PMODINFO LocateModInfo( PVOID Address ) { ULONG i; for (i=0;i= ModInfo[i].BaseAddress && Address <= (PVOID)((ULONG_PTR)ModInfo[i].BaseAddress+ModInfo[i].VirtualSize) ) { return &ModInfo[i]; } } return NULL; } VOID CaptureWorkingSet( HANDLE Process ) { ULONG_PTR NumEntries = INITIAL_WORKING_SET_BLOCK_ENTRYS; BOOLEAN Done; SIZE_T Size; DWORD Error; Done = FALSE; while (!Done) { Size = FIELD_OFFSET(MEMORY_WORKING_SET_INFORMATION, WorkingSetInfo) + NumEntries * sizeof(MEMORY_WORKING_SET_BLOCK); WorkingSetInfo = HeapAlloc(GetProcessHeap(), 0, Size); if (WorkingSetInfo == NULL) { printf("FAILURE Couldn't allocate working set info buffer\n"); exit(0); } if (!QueryWorkingSet(Process, WorkingSetInfo, (DWORD) Size)) { Error = GetLastError(); if (Error != ERROR_BAD_LENGTH) { printf("FAILURE query working set %lu\n", Error); exit(0); } } if (WorkingSetInfo->NumberOfEntries > NumEntries) { // // Not big enough so increase the number of entries and // free the old one. // NumEntries = WorkingSetInfo->NumberOfEntries + 100; // Add in some fudge for growth HeapFree(GetProcessHeap(), 0, WorkingSetInfo); } else { Done = TRUE; } } } int __cdecl ulcomp( const void *e1, const void *e2 ) { PULONG p1; PULONG p2; p1 = (PULONG)e1; p2 = (PULONG)e2; if (*p1 > *p2) { return 1; } if (*p1 < *p2) { return -1; } return 0; } int __cdecl WSBlockComp( const void *e1, const void *e2 ) { PMEMORY_WORKING_SET_BLOCK p1; PMEMORY_WORKING_SET_BLOCK p2; p1 = (PMEMORY_WORKING_SET_BLOCK)e1; p2 = (PMEMORY_WORKING_SET_BLOCK)e2; if (p1->VirtualPage > p2->VirtualPage) { return 1; } if (p1->VirtualPage < p2->VirtualPage) { return -1; } return 0; } int __cdecl wsinfocomp( const void *e1, const void *e2 ) { PWSINFOCOUNTS p1; PWSINFOCOUNTS p2; p1 = (PWSINFOCOUNTS)e1; p2 = (PWSINFOCOUNTS)e2; return (p1->Faults - p2->Faults); } BOOL CtrlcH ( IN DWORD dwCtrlType ) { PWSINFOCOUNTS WsInfoCount; LONG RunIndex; LONG CountIndex; IMAGEHLP_MODULE Mi; ULONG_PTR Offset; CHAR Line[256]; if ( dwCtrlType != CTRL_C_EVENT ) { return FALSE; } if ((Options & (OPTIONS_WORKING_SET | OPTIONS_WORKING_SET_OLD)) == OPTIONS_WORKING_SET) { ; } else { return FALSE; } Mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE); InCtrlc = TRUE; // // Sort the running working set buffer // qsort((void *)RunningWorkingSetBuffer,(size_t)CurrentWsIndex,(size_t)sizeof(ULONG),ulcomp); WsInfoCount = LocalAlloc(LMEM_ZEROINIT,CurrentWsIndex*sizeof(*WsInfoCount)); if ( !WsInfoCount ) { ExitProcess(0); } // // Sum unique PC values // CountIndex = 0; RunIndex = 0; WsInfoCount[CountIndex].FaultingPc = RunningWorkingSetBuffer[RunIndex]; WsInfoCount[CountIndex].Faults++; for(RunIndex = 1; RunIndex < CurrentWsIndex; RunIndex++){ if ( WsInfoCount[CountIndex].FaultingPc == RunningWorkingSetBuffer[RunIndex] ) { WsInfoCount[CountIndex].Faults++; } else { CountIndex++; WsInfoCount[CountIndex].FaultingPc = RunningWorkingSetBuffer[RunIndex]; WsInfoCount[CountIndex].Faults++; } } // // Now sort the counted pc/fault count pairs // qsort(WsInfoCount,CountIndex,sizeof(*WsInfoCount),wsinfocomp); // // Now print the sorted pc/fault count pairs // for ( RunIndex = CountIndex-1; RunIndex >= 0 ; RunIndex-- ) { if (!SymGetModuleInfo((HANDLE)ProcessId, WsInfoCount[RunIndex].FaultingPc, &Mi )) { printf("%8d, 0x%p\n",WsInfoCount[RunIndex].Faults,WsInfoCount[RunIndex].FaultingPc); if ( LogFile ) { fprintf(LogFile,"%8d, 0x%p\n",WsInfoCount[RunIndex].Faults,WsInfoCount[RunIndex].FaultingPc); } } else { if (SymGetSymFromAddr((HANDLE)ProcessId, WsInfoCount[RunIndex].FaultingPc, &Displacement, ThisSymbol )) { Offset = (ULONG_PTR)WsInfoCount[RunIndex].FaultingPc - ThisSymbol->Address; if ( Offset ) { sprintf(Line,"%8d, %s+%x\n",WsInfoCount[RunIndex].Faults,ThisSymbol->Name,Offset); } else { sprintf(Line,"%8d, %s\n",WsInfoCount[RunIndex].Faults, ThisSymbol->Name); } printf("%s",Line); if ( LogFile ) { fprintf(LogFile,"%s",Line); } } else { printf("%8d, 0x%p\n",WsInfoCount[RunIndex].Faults,WsInfoCount[RunIndex].FaultingPc); if ( LogFile ) { fprintf(LogFile,"%8d, 0x%p\n",WsInfoCount[RunIndex].Faults,WsInfoCount[RunIndex].FaultingPc); } } } } exit(1); return FALSE; } VOID DumpWorkingSetSnapshot ( IN HANDLE Process ) { LOGICAL NewLine; PSYSTEM_PAGE SystemPageBase; ULONG i; ULONG_PTR BaseVa = 0; ULONG_PTR Va = 0; ULONG_PTR PteIndex; ULONG_PTR BaseAddress; ULONG SystemPages = 0; ULONG HeapPages = 0; ULONG StackPages = 0; ULONG MappedPages = 0; ULONG SharedMappedPages = 0; ULONG PrivateMappedPages = 0; ULONG DataPages = 0; ULONG SharedDataPages = 0; ULONG PrivateDataPages = 0; ULONG ErrorPages = 0; ULONG QuickPages = 0; ULONG LpcPages = 0; ULONG CsrSharedPages = 0; ULONG SharedCsrSharedPages = 0; ULONG TebPages = 0; ULONG TotalStaticCodeData = 0; ULONG TotalStaticCodeDataShared = 0; ULONG TotalStaticCodeDataPrivate = 0; ULONG TotalDynamicData = 0; ULONG TotalDynamicDataShared = 0; ULONG TotalDynamicDataPrivate = 0; ULONG TotalSystem = 0; ULONG Total, Shareable, Private, Shared; PMODINFO Mi; PLOADED_HEAP pHeap; PLIST_ENTRY Next; MEMORY_BASIC_INFORMATION BasicInfo; BOOL b; ULONG Mstack[7]; WCHAR FileName[MAX_PATH+1]; PWCHAR pwch; ULONG ShareCount; BOOLEAN IsShareable; PMEMORY_WORKING_SET_BLOCK WorkingSetBlock; PMEMORY_WORKING_SET_BLOCK LastWorkingSetBlock; ULONG PageTablePageCount; ULONG PageTablePageMax; ULONG_PTR SPBase; ULONG_PTR MIBase; ULONG_PTR MIEnd; ULONG_PTR HSBase; ULONG_PTR HSEnd; ULONG_PTR SSBase; ULONG_PTR SSEnd; NewLine = FALSE; if (Options & OPTIONS_RAW_SYMBOLS) { fSummary = FALSE; } qsort (&WorkingSetInfo->WorkingSetInfo[0], WorkingSetInfo->NumberOfEntries, sizeof(MEMORY_WORKING_SET_BLOCK), WSBlockComp); // // Count the number of user page table page references that faulted. // PageTablePageCount = 0; WorkingSetBlock = &WorkingSetInfo->WorkingSetInfo[0]; LastWorkingSetBlock = WorkingSetBlock + WorkingSetInfo->NumberOfEntries; while (WorkingSetBlock < LastWorkingSetBlock) { Va = WorkingSetBlock->VirtualPage << 12; if (IS_USER_PAGE_TABLE_PAGE(Va)) { PageTablePageCount += 1; } WorkingSetBlock += 1; } // // Allocate memory to hold the user page table page references. // SystemPageBase = NULL; PageTablePageMax = PageTablePageCount; if (PageTablePageMax != 0) { SystemPageBase = LocalAlloc (LMEM_ZEROINIT, PageTablePageMax * sizeof(SYSTEM_PAGE)); if (SystemPageBase == NULL) { return; } } PageTablePageCount = 0; WorkingSetBlock = &WorkingSetInfo->WorkingSetInfo[0]; while (WorkingSetBlock < LastWorkingSetBlock) { Va = WorkingSetBlock->VirtualPage << 12; IsSystemWithShareCount |= (ULONG_PTR)WorkingSetBlock->ShareCount; if (IS_USER_PAGE_TABLE_PAGE(Va)) { SystemPageBase[PageTablePageCount].Va = Va; PteIndex = (Va - (ULONG_PTR)PteBase) / PteWidth; BaseAddress = (PteIndex / PtesPerPage) * VaMappedByPageTable; SystemPageBase[PageTablePageCount].BaseAddress = (PVOID)BaseAddress; PageTablePageCount += 1; } WorkingSetBlock += 1; } // // Attribute each user space page into the system page that backs it. // WorkingSetBlock = &WorkingSetInfo->WorkingSetInfo[0]; LastWorkingSetBlock = WorkingSetBlock + WorkingSetInfo->NumberOfEntries; while (WorkingSetBlock < LastWorkingSetBlock) { Va = WorkingSetBlock->VirtualPage << 12; if (Va < SystemRangeStart) { for (i = 0; i < PageTablePageCount; i += 1) { if ((Va >= (ULONG_PTR)SystemPageBase[i].BaseAddress) && (Va < ((ULONG_PTR)SystemPageBase[i].BaseAddress + VaMappedByPageTable))) { SystemPageBase[i].ResidentPages += 1; break; } } } WorkingSetBlock += 1; } WorkingSetBlock = &WorkingSetInfo->WorkingSetInfo[0]; for ( ; WorkingSetBlock < LastWorkingSetBlock; WorkingSetBlock += 1) { Va = WorkingSetBlock->VirtualPage << 12; IsShareable = (BOOLEAN) (WorkingSetBlock->Shared == 1); ShareCount = (ULONG) WorkingSetBlock->ShareCount; if (Va >= SystemRangeStart) { if ((!fSummary || (Options & OPTIONS_PAGE_TABLES)) && (IS_USER_PAGE_TABLE_PAGE(Va))) { // // For each system page, dump the range spanned, number of // resident pages, and the modules and heaps covered. // for (i = 0; Va != SystemPageBase[i].Va; i += 1) { ; } SPBase = (ULONG_PTR) SystemPageBase[i].BaseAddress; if (NewLine) { printf("\n"); NewLine = FALSE; } printf("0x%p -> (0x%p : 0x%p) %4d " "Resident Pages\n", Va, SPBase, SPBase + VaMappedByPageTable - 1, SystemPageBase[i].ResidentPages); // // Figure out which modules are covered by this // page table page. If the base of the module is // within the page, or the base+size of the // module is covered, then it is in the page // for (i = 0 ; i < ModInfoMax ; i += 1) { MIBase = (ULONG_PTR) ModInfo[i].BaseAddress; MIEnd = MIBase + (ULONG_PTR)ModInfo[i].VirtualSize; if ((MIEnd >= SPBase) && (MIBase < SPBase + VaMappedByPageTable)) { printf(" (0x%p : 0x%p) " "-> %s\n", MIBase, MIEnd, ModInfo[i].Name ); NewLine = TRUE; } } // // Figure out which heaps are covered by this // page table page. // Next = LoadedHeapList.Flink; while (Next != &LoadedHeapList) { pHeap = CONTAINING_RECORD (Next, LOADED_HEAP, HeapsList); Next = Next->Flink; for (i = 0 ; i < HEAP_MAXIMUM_SEGMENTS ; i += 1) { if (pHeap->Segments[i].BaseVa == NULL) { break; } HSBase = (ULONG_PTR) pHeap->Segments[i].BaseVa; HSEnd = HSBase + (ULONG_PTR)pHeap->Segments[i].Length; if ((HSEnd >= SPBase) && (HSBase < (SPBase + VaMappedByPageTable))) { printf(" (0x%p : 0x%p) " "-> %s segment %d\n", HSBase, HSEnd, pHeap->HeapName, i); NewLine = TRUE; } } } // // Figure out which stacks are covered by this // page table page. // for (i = 0 ; i < NumberOfThreads ; i += 1) { SSBase = (ULONG_PTR)TheThreads[i].StackBase; SSEnd = (ULONG_PTR)TheThreads[i].StackEnd; if ((SSEnd >= SPBase) && (SSBase < (SPBase + VaMappedByPageTable))) { printf(" (0x%p : 0x%p) " "-> Stack for thread %d\n", SSBase, SSEnd, i); NewLine = TRUE; } } } SystemPages += 1; TotalSystem += 1; continue; } Mi = LocateModInfo ((PVOID)Va); if (Mi == NULL) { if (FindAndIncHeapContainingThisVa ((PVOID)Va, ShareCount)) { HeapPages += 1; TotalDynamicData += 1; TotalDynamicDataPrivate += 1; continue; } if (FindAndIncStackContainingThisVa ((PVOID)Va, ShareCount)) { StackPages += 1; TotalDynamicData += 1; TotalDynamicDataPrivate += 1; continue; } if (VirtualQueryEx (Process, (LPVOID) Va, &BasicInfo, sizeof(BasicInfo)) ) { if (BasicInfo.Type == MEM_MAPPED) { if (ProcessId == 0xffffffff) { // // Look to see if this is a quick thread message // stack window // b = ReadProcessMemory( Process, BasicInfo.AllocationBase, &Mstack, sizeof(Mstack), NULL); if (!b) { goto unknownmapped; } if ((Mstack[0] >= Mstack[1]) && (Mstack[2] == 0x10000)) { if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("CSRQUICK Base 0x%p\n", BasicInfo.AllocationBase); } QuickPages += 1; TotalDynamicData += 1; TotalDynamicDataPrivate += 1; if (ShareCount > 1) { fprintf(stderr, "Error: QuickPage ShareCount > 1, " " 0x%x\n", Va); } continue; } if ((BasicInfo.AllocationBase == NtCurrentPeb()->ReadOnlySharedMemoryBase) || (Va == (ULONG_PTR)NtCurrentPeb()->ReadOnlySharedMemoryBase)) { if (!fSummary) { printf("0x%p", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("CSRSHARED Base 0x%p", BasicInfo.AllocationBase); } TotalDynamicData++; CsrSharedPages++; if (IsShareable) { if (ShareCount > 1) { TotalDynamicDataShared++; SharedCsrSharedPages++; } } else { fprintf(stderr, "Error: CsrShared not " " sharable, 0x%x\n", Va); } continue; } // Fall Through if not found } // // It's mapped but wasn't CSRSS special page. // unknownmapped: if ( !fSummary ) { DWORD cch; // // See if we can figure out the name associated with // this mapped region // cch = GetMappedFileNameW(Process, (LPVOID) Va, FileName, sizeof(FileName)); if (cch != 0) { // // Now go back through the string to // find the seperator // pwch = FileName + cch; while ( *pwch != (WCHAR)'\\' ) { pwch--; } pwch++; printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("DATAFILE_MAPPED Base 0x%p %ws\n", BasicInfo.AllocationBase, pwch ); } else { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("UNKNOWN_MAPPED Base 0x%p\n", BasicInfo.AllocationBase); } } TotalDynamicData++; MappedPages++; if (IsShareable) { if (ShareCount > 1) { TotalDynamicDataShared++; SharedMappedPages++; } } else { TotalDynamicDataPrivate++; PrivateMappedPages++; } continue; } // // Not Mapped section // for (i = 0 ; i < NumberOfThreads; i += 1) { if ((ULONG_PTR) TheThreads[i].ThreadTEB == Va) { if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("TEB Base 0x%p\n", BasicInfo.AllocationBase); } TotalDynamicData++; TebPages++; if (ShareCount > 1) { fprintf(stderr, "Error: TEB ShareCount > 1, " " 0x%x\n", Va); } TotalDynamicDataPrivate++; continue; } } // // Wasn't a TEB either it must have been VirtualAlloc'd. // if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("PRIVATE Base 0x%p\n", BasicInfo.AllocationBase ); } TotalDynamicData += 1; DataPages += 1; if (Va != MM_SHARED_USER_DATA_VA) { if (ShareCount > 1) { fprintf(stderr, "Error: Private ShareCount > 1, " " 0x%x %x\n", Va, ShareCount); } TotalDynamicDataPrivate += 1; PrivateDataPages += 1; } continue; } // // Hmm, couldn't find out about the page. Say it's data. // if (!fSummary) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("UNKOWN\n"); } TotalDynamicData++; DataPages++; if (IsShareable) { if (ShareCount > 1) { TotalDynamicDataShared++; SharedDataPages++; } } else { TotalDynamicDataPrivate++; PrivateDataPages++; } continue; } // // It's from a module. // Mi->WsHits += 1; TotalStaticCodeData += 1; if (IsShareable) { if (ShareCount > 1) { TotalStaticCodeDataShared += 1; Mi->WsSharedHits += 1; } } else { Mi->WsPrivateHits += 1; TotalStaticCodeDataPrivate += 1; } if ( !fSummary ) { printf("0x%p ", Va); if (IsSystemWithShareCount) { printf("(%d) ", ShareCount); } printf("%s\n",Mi->Name); if (Options & OPTIONS_RAW_SYMBOLS) { if (SymGetSymFromAddr((HANDLE)ProcessId, Va, &Displacement, ThisSymbol )) { BaseVa = Va; if (ThisSymbol->Size) { printf("\t(%4x) %s\n", ThisSymbol->Size, ThisSymbol->Name ); Va += ThisSymbol->Size; while ((Va < BaseVa + 4096) && ThisSymbol->Size) { if (SymGetSymFromAddr((HANDLE)ProcessId, Va, &Displacement, ThisSymbol)) { printf("\t(%4x) %s\n", ThisSymbol->Size, ThisSymbol->Name ); Va += ThisSymbol->Size; } else { break; } } } } else { ErrorPages++; } } } } if (!fSummary || (Options & OPTIONS_PAGE_TABLES)) { printf("\n"); } if (IsSystemWithShareCount) { printf("Category Total Private Shareable Shared\n"); printf(" Pages KBytes KBytes KBytes KBytes\n"); } else { printf("Category Total Private Shareable\n"); printf(" Pages KBytes KBytes KBytes\n"); } Total = PageTablePageCount; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Page Table Pages %5d %9d %9d %9d %9d\n" : " Page Table Pages %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = SystemPages - PageTablePageCount; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Other System %5d %9d %9d %9d %9d\n" : " Other System %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = TotalStaticCodeData; Private = TotalStaticCodeDataPrivate; Shared = TotalStaticCodeDataShared; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Code/StaticData %5d %9d %9d %9d %9d\n" : " Code/StaticData %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = HeapPages; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Heap %5d %9d %9d %9d %9d\n" : " Heap %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = StackPages; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Stack %5d %9d %9d %9d %9d\n" : " Stack %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); if ( ProcessId == 0xffffffff ) { Total = QuickPages; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Quick Thread Stack %5d %9d %9d %9d %9d\n" : " Quick Thread Stack %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = LpcPages; Private = 0; Shareable = 0; Shared = 0; printf(IsSystemWithShareCount ? " Lpc Message Windows %5d %9d %9d %9d %9d\n" : " Lpc Message Windows %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = CsrSharedPages; Private = 0; Shared = SharedCsrSharedPages; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Csr Shared Memory %5d %9d %9d %9d %9d\n" : " Csr Shared Memory %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); } Total = TebPages; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Teb %5d %9d %9d %9d %9d\n" : " Teb %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = MappedPages; Private = PrivateMappedPages; Shared = SharedMappedPages; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Mapped Data %5d %9d %9d %9d %9d\n" : " Mapped Data %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = DataPages; Private = PrivateDataPages; Shared = SharedDataPages; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Other Data %5d %9d %9d %9d %9d\n" : " Other Data %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); printf("\n"); Total = TotalStaticCodeData; Private = TotalStaticCodeDataPrivate; Shared = TotalStaticCodeDataShared; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Total Modules %5d %9d %9d %9d %9d\n" : " Total Modules %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = TotalDynamicData; Private = TotalDynamicDataPrivate; Shared = TotalDynamicDataShared; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? " Total Dynamic Data %5d %9d %9d %9d %9d\n" : " Total Dynamic Data %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = TotalSystem; Private = Total; Shared = 0; Shareable = 0; printf(IsSystemWithShareCount ? " Total System %5d %9d %9d %9d %9d\n" : " Total System %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); Total = TotalSystem + TotalDynamicData + TotalStaticCodeData; Private = TotalSystem + TotalDynamicDataPrivate + TotalStaticCodeDataPrivate; Shared = TotalDynamicDataShared + TotalStaticCodeDataShared; Shareable = Total - Shared - Private; printf(IsSystemWithShareCount ? "Grand Total Working Set %5d %9d %9d %9d %9d\n" : "Grand Total Working Set %5d %9d %9d %9d\n", Total, P2KB(Total), P2KB(Private), P2KB(Shareable), P2KB(Shared) ); printf("\nModule Working Set Contributions in pages\n"); printf(IsSystemWithShareCount ? " Total Private Shareable Shared Module\n" : " Total Private Shareable Module\n" ); for (i=0 ; i < ModInfoMax ; i++){ if ( ModInfo[i].WsHits ) { if (IsSystemWithShareCount) { printf("%9d %9d %9d %9d %s\n", ModInfo[i].WsHits, ModInfo[i].WsPrivateHits, ModInfo[i].WsHits - ModInfo[i].WsSharedHits - ModInfo[i].WsPrivateHits, ModInfo[i].WsSharedHits, ModInfo[i].Name ); } else { printf("%9d %9d %9d %s\n", ModInfo[i].WsHits, ModInfo[i].WsPrivateHits, ModInfo[i].WsHits - ModInfo[i].WsSharedHits - ModInfo[i].WsPrivateHits, ModInfo[i].Name ); } } } printf("\nHeap Working Set Contributions\n"); Next = LoadedHeapList.Flink; while ( Next != &LoadedHeapList ) { pHeap = CONTAINING_RECORD(Next, LOADED_HEAP, HeapsList); Next = Next->Flink; DumpLoadedHeap(pHeap); } printf("\nStack Working Set Contributions\n"); DumpLoadedStacks(); #if 0 if ( Options & OPTIONS_VERBOSE ) { printf("Raw Working Set Blocks\n\n"); for (i = 0; i < WorkingSetInfo->NumberOfEntries ; i++) { printf("%d %p\n", i, (ULONG_PTR) WorkingSetInfo->WorkingSetInfo[i]); i++; } } #endif if (SystemPageBase != NULL) { LocalFree (SystemPageBase); } } VOID DumpWorkingSet ( IN HANDLE Process ) { ULONG i; PMODINFO Mi,Mi2; NTSTATUS Status; ULONG_PTR Offset; CHAR Line[256]; BOOLEAN didone; HANDLE ScreenHandle; INPUT_RECORD InputRecord; DWORD NumRead; ScreenHandle = GetStdHandle (STD_INPUT_HANDLE); if (ScreenHandle == NULL) { printf("Error obtaining screen handle, error was: 0x%lx\n", GetLastError()); ExitProcess(1); } Status = NtSetInformationProcess (Process, ProcessWorkingSetWatch, NULL, 0); if (!NT_SUCCESS(Status) && !(Status == STATUS_PORT_ALREADY_SET) && !(Status == STATUS_ACCESS_DENIED)) { return; } SetConsoleCtrlHandler(CtrlcH,TRUE); EmptyWorkingSet(Process); while (TRUE) { Status = NtQueryInformationProcess (Process, ProcessWorkingSetWatch, (PVOID *)&NewWorkingSetBuffer, sizeof (NewWorkingSetBuffer), NULL); if (fFast) { fFast = FALSE; Status = STATUS_NO_MORE_ENTRIES; } if ( NT_SUCCESS(Status) ) { // // For each PC/VA pair, print the pc and referenced VA // symbolically // didone = FALSE; i = 0; while (NewWorkingSetBuffer[i].FaultingPc) { if ( NewWorkingSetBuffer[i].FaultingVa ) { if ( InCtrlc ) { ExitThread(0); } Mi2 = LocateModInfo((PVOID)NewWorkingSetBuffer[i].FaultingVa); if ( !Mi2 || (Mi2 && (Options & OPTIONS_CODE_TOO))) { // // Add the pc to the running working set // watch buffer // RunningWorkingSetBuffer[CurrentWsIndex++] = (ULONG_PTR)NewWorkingSetBuffer[i].FaultingPc; if ( CurrentWsIndex >= MAX_RUNNING_WORKING_SET_BUFFER ) { CtrlcH(CTRL_C_EVENT); } if ( fRunning ) { // // Print the PC symbolically. // didone = TRUE; Mi = LocateModInfo((PVOID)NewWorkingSetBuffer[i].FaultingPc); if ( !Mi ) { printf("0x%p",NewWorkingSetBuffer[i].FaultingPc); if ( LogFile ) { fprintf(LogFile,"0x%p",NewWorkingSetBuffer[i].FaultingPc); } } else { if (SymGetSymFromAddr((HANDLE)ProcessId, (DWORD_PTR)NewWorkingSetBuffer[i].FaultingPc, &Displacement, ThisSymbol )) { Offset = (ULONG_PTR)NewWorkingSetBuffer[i].FaultingPc - ThisSymbol->Address; if ( Offset ) { sprintf(Line,"%s+%x",ThisSymbol->Name,Offset); } else { sprintf(Line,"%s",ThisSymbol->Name); } printf("%s",Line); if ( LogFile ) { fprintf(LogFile,"%s",Line); } } else { printf("0x%p",NewWorkingSetBuffer[i].FaultingPc); if ( LogFile ) { fprintf(LogFile,"0x%p",NewWorkingSetBuffer[i].FaultingPc); } } } // // Print the VA Symbolically // Mi = LocateModInfo((PVOID)NewWorkingSetBuffer[i].FaultingVa); if ( !Mi ) { printf(" : 0x%p",NewWorkingSetBuffer[i].FaultingVa); if ( LogFile ) { fprintf(LogFile," : 0x%p",NewWorkingSetBuffer[i].FaultingVa); } } else { if (SymGetSymFromAddr((HANDLE)ProcessId, (DWORD_PTR)NewWorkingSetBuffer[i].FaultingVa, &Displacement, ThisSymbol )) { Offset = (ULONG_PTR)NewWorkingSetBuffer[i].FaultingVa - ThisSymbol->Address; if ( Offset ) { sprintf(Line," : %s+%x",ThisSymbol->Name,Offset); } else { sprintf(Line," : %s",ThisSymbol->Name); } printf("%s",Line); if ( LogFile ) { fprintf(LogFile,"%s",Line); } } else { printf(" : 0x%p",NewWorkingSetBuffer[i].FaultingVa); if ( LogFile ) { fprintf(LogFile," : 0x%p",NewWorkingSetBuffer[i].FaultingVa); } } } printf("\n"); if ( LogFile ) { fprintf(LogFile,"\n"); } } } } i++; } if ( didone ) { printf("\n"); if ( LogFile ) { fprintf(LogFile,"\n"); } } } Sleep(1000); while (PeekConsoleInput (ScreenHandle, &InputRecord, 1, &NumRead) && NumRead != 0) { if (!ReadConsoleInput (ScreenHandle, &InputRecord, 1, &NumRead)) { break; } if (InputRecord.EventType == KEY_EVENT) { // // Ignore control characters. // if (InputRecord.Event.KeyEvent.uChar.AsciiChar >= ' ') { switch (InputRecord.Event.KeyEvent.uChar.AsciiChar) { case 'F': case 'f': EmptyWorkingSet(Process); printf("\n*** Working Set Flushed ***\n\n"); if ( LogFile ) { fprintf(LogFile,"\n*** Working Set Flushed ***\n\n"); } break; default: break; } } } } } } VOID ComputeModInfo( HANDLE Process, DWORD_PTR ProcessId ) { HMODULE rghModule[MODINFO_SIZE]; DWORD cbNeeded; ULONG ModInfoNext; PVOID BaseAddress; IMAGEHLP_MODULE ModuleInfo; MODULEINFO PsapiModuleInfo; ULONG i; ModuleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE); SymInitialize((HANDLE)ProcessId, NULL, FALSE ); SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); for (i=0 ; i < ModInfoMax ; i++){ if ( ModInfo[i].BaseAddress && ModInfo[i].BaseAddress != (PVOID)-1 && ModInfo[i].Name ) { LocalFree(ModInfo[i].Name); } } RtlZeroMemory(ModInfo, sizeof(ModInfo)); if (!EnumProcessModules(Process, rghModule, sizeof(rghModule), &cbNeeded)) { return; } if (cbNeeded > sizeof(rghModule)) { cbNeeded = sizeof(rghModule); } ModInfoMax = cbNeeded / sizeof(HMODULE); for (ModInfoNext = 0; ModInfoNext < ModInfoMax; ModInfoNext++) { HMODULE hModule; DWORD cch; CHAR DllName[MAX_PATH]; hModule = rghModule[ModInfoNext]; ModInfo[ModInfoNext].BaseAddress = (PVOID) hModule; // // Get the base name of the module // cch = GetModuleBaseName(Process, hModule, DllName, sizeof(DllName)); if (cch == 0) { return; } ModInfo[ModInfoNext].Name = LocalAlloc(LMEM_ZEROINIT, cch+1); if ( !ModInfo[ModInfoNext].Name) { return; } memcpy(ModInfo[ModInfoNext].Name, DllName, cch); // // Get the full path to the module. // cch = GetModuleFileNameEx (Process, hModule, DllName, sizeof(DllName)); if (cch == 0) { return; } GetModuleInformation (Process, hModule, &PsapiModuleInfo, sizeof(MODULEINFO)); ModInfo[ModInfoNext].VirtualSize = PsapiModuleInfo.SizeOfImage; BaseAddress = (PVOID)SymLoadModule ((HANDLE)ProcessId, NULL, DllName, NULL, (DWORD_PTR)hModule, PsapiModuleInfo.SizeOfImage); if ((ModInfo[ModInfoNext].BaseAddress) && (ModInfo[ModInfoNext].BaseAddress == BaseAddress)) { SymGetModuleInfo( (HANDLE)ProcessId, (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress, &ModuleInfo ); if (ModuleInfo.SymType == SymNone) { ModInfo[ModInfoNext].SymbolsLoaded = FALSE; if (Options & OPTIONS_VERBOSE) { fprintf(stderr, "Could not load symbols: %p : %p %s\n", (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress, (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress + ModInfo[ModInfoNext].VirtualSize, ModInfo[ModInfoNext].Name ); } } else { ModInfo[ModInfoNext].SymbolsLoaded = TRUE; if (Options & OPTIONS_VERBOSE) { fprintf(stderr, "Symbols loaded: %p : %p %s\n", (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress, (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress + ModInfo[ModInfoNext].VirtualSize, ModInfo[ModInfoNext].Name ); } } } else { ModInfo[ModInfoNext].SymbolsLoaded = FALSE; if (Options & OPTIONS_VERBOSE) { fprintf(stderr, "Symbols not loaded and conflicting Base: %p (%p) : %p %s\n", (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress, BaseAddress, (DWORD_PTR)ModInfo[ModInfoNext].BaseAddress + ModInfo[ModInfoNext].VirtualSize, ModInfo[ModInfoNext].Name ); } } } if (bHitModuleMax) { fprintf(stderr, "\nERROR: The number of modules in the process more than the buffer size\n"); } } ProtectionToIndex( ULONG Protection ) { Protection &= ~PAGE_GUARD; switch ( Protection ) { case PAGE_NOACCESS: return NOACCESS; case PAGE_READONLY: return READONLY; case PAGE_READWRITE: return READWRITE; case PAGE_WRITECOPY: return WRITECOPY; case PAGE_EXECUTE: return EXECUTE; case PAGE_EXECUTE_READ: return EXECUTEREAD; case PAGE_EXECUTE_READWRITE: return EXECUTEREADWRITE; case PAGE_EXECUTE_WRITECOPY: return EXECUTEWRITECOPY; default: return 0; } } VOID DumpCommit ( PSZ Header, ULONG_PTR *CommitVector ) { ULONG_PTR TotalCommitCount; ULONG i; TotalCommitCount = 0; for ( i=0;iBasicInfo, sizeof(VaInfo->BasicInfo)) ) { LocalFree (VaInfo); return; } switch (VaInfo->BasicInfo.State ) { case MEM_COMMIT : if ( VaInfo->BasicInfo.Type == MEM_IMAGE ) { ImageCommit[ProtectionToIndex(VaInfo->BasicInfo.Protect)] += VaInfo->BasicInfo.RegionSize; Mod = LocateModInfo(BaseAddress); if ( Mod ) { Mod->CommitVector[ProtectionToIndex(VaInfo->BasicInfo.Protect)] += VaInfo->BasicInfo.RegionSize; } } else { if ( VaInfo->BasicInfo.Type == MEM_MAPPED ) { MappedCommit[ProtectionToIndex(VaInfo->BasicInfo.Protect)] += VaInfo->BasicInfo.RegionSize; } else { PrivateCommit[ProtectionToIndex(VaInfo->BasicInfo.Protect)] += VaInfo->BasicInfo.RegionSize; } } break; case MEM_RESERVE : if ( VaInfo->BasicInfo.Type == MEM_IMAGE ) { ImageReservedBytes += VaInfo->BasicInfo.RegionSize; } else { ReservedBytes += VaInfo->BasicInfo.RegionSize; } break; case MEM_FREE : if ( VaInfo->BasicInfo.Type == MEM_IMAGE ) { ImageFreeBytes += VaInfo->BasicInfo.RegionSize; } else { FreeBytes += VaInfo->BasicInfo.RegionSize; } break; } if ( LastAllocationBase ) { // // Normal case // // // See if last one is 0, or if this one doesn't match the // last one. // if ( LastAllocationBase->BasicInfo.AllocationBase == NULL || LastAllocationBase->BasicInfo.AllocationBase != VaInfo->BasicInfo.AllocationBase ) { LastAllocationBase = VaInfo; InsertTailList(&VaList,&VaInfo->Links); InitializeListHead(&VaInfo->AllocationBaseHead); } else { // // Current Entry Matches // InsertTailList(&LastAllocationBase->AllocationBaseHead,&VaInfo->Links); } } else { LastAllocationBase = VaInfo; InsertTailList(&VaList,&VaInfo->Links); InitializeListHead(&VaInfo->AllocationBaseHead); } BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + VaInfo->BasicInfo.RegionSize); } } PSZ MemProtect( IN ULONG Protection ) { switch ( Protection ) { case PAGE_NOACCESS: return "No Access"; case PAGE_READONLY: return "Read Only"; case PAGE_READWRITE: return "Read/Write"; case PAGE_WRITECOPY: return "Write Copy"; case PAGE_EXECUTE: return "Execute"; case PAGE_EXECUTE_READ: return "Execute Read"; case PAGE_EXECUTE_READWRITE: return "Execute Read/Write"; case PAGE_EXECUTE_WRITECOPY: return "Execute Write Copy"; default : if ( Protection & PAGE_GUARD ) { switch ( Protection & 0xff ) { case PAGE_NOACCESS: return "-- GUARD -- No Access"; case PAGE_READONLY: return "-- GUARD -- Read Only"; case PAGE_READWRITE: return "-- GUARD -- Read/Write"; case PAGE_WRITECOPY: return "-- GUARD -- Write Copy"; case PAGE_EXECUTE: return "-- GUARD -- Execute"; case PAGE_EXECUTE_READ: return "-- GUARD -- Execute Read"; case PAGE_EXECUTE_READWRITE: return "-- GUARD -- Execute Read/Write"; case PAGE_EXECUTE_WRITECOPY: return "-- GUARD -- Execute Write Copy"; default: return "-- GUARD -- Unknown"; } } return "Unknown"; } } PSZ MemState( IN ULONG State ) { switch ( State ) { case MEM_COMMIT : return "Committed"; case MEM_RESERVE : return "Reserved"; case MEM_FREE : return "Free"; default: return "Unknown State"; } } PSZ MemType( IN ULONG Type ) { switch ( Type ) { case MEM_PRIVATE : return "Private"; case MEM_MAPPED : return "Mapped"; case MEM_IMAGE : return "Image"; default: return "Unknown Type"; } } VOID DumpVaSpace( VOID ) { PLIST_ENTRY Next; PVAINFO VaInfo; ULONG_PTR VirtualSize; Next = VaList.Flink; while ( Next != &VaList) { VaInfo = (PVAINFO)(CONTAINING_RECORD(Next,VAINFO,Links)); printf("\n"); if ( !IsListEmpty(&VaInfo->AllocationBaseHead) ) { PLIST_ENTRY xNext; PVAINFO xVaInfo; VirtualSize = VaInfo->BasicInfo.RegionSize; xNext = VaInfo->AllocationBaseHead.Flink; while ( xNext != &VaInfo->AllocationBaseHead) { xVaInfo = (PVAINFO)(CONTAINING_RECORD(xNext,VAINFO,Links)); VirtualSize += xVaInfo->BasicInfo.RegionSize; xNext = xNext->Flink; } } else { VirtualSize = 0; } printf("Address: %p Size: %p", VaInfo->BasicInfo.BaseAddress, VaInfo->BasicInfo.RegionSize); if ( VirtualSize ) { printf(" RegionSize: %lx\n",VirtualSize); } else { printf("\n"); } printf(" State %s\n",MemState(VaInfo->BasicInfo.State)); if ( VaInfo->BasicInfo.State == MEM_COMMIT ) { printf(" Protect %s\n",MemProtect(VaInfo->BasicInfo.Protect)); } if ( VaInfo->BasicInfo.State == MEM_COMMIT || VaInfo->BasicInfo.State == MEM_RESERVE ) { printf(" Type %s\n",MemType(VaInfo->BasicInfo.Type)); } if ( Options & OPTIONS_VERBOSE ) { if ( !IsListEmpty(&VaInfo->AllocationBaseHead) ) { PLIST_ENTRY xNext; PVAINFO xVaInfo; xNext = VaInfo->AllocationBaseHead.Flink; while ( xNext != &VaInfo->AllocationBaseHead) { xVaInfo = (PVAINFO)(CONTAINING_RECORD(xNext,VAINFO,Links)); printf("\n"); printf(" Address: %p Size: %p\n", xVaInfo->BasicInfo.BaseAddress, xVaInfo->BasicInfo.RegionSize ); printf(" RegionSize %p\n",xVaInfo->BasicInfo.RegionSize); printf(" State %s\n",MemState(xVaInfo->BasicInfo.State)); if ( xVaInfo->BasicInfo.State == MEM_COMMIT ) { printf(" Protect %s\n",MemProtect(xVaInfo->BasicInfo.Protect)); } if ( xVaInfo->BasicInfo.State == MEM_COMMIT || xVaInfo->BasicInfo.State == MEM_RESERVE ) { printf(" Type %s\n",MemType(xVaInfo->BasicInfo.Type)); } xNext = xNext->Flink; } } } Next = Next->Flink; } } int __cdecl main( int argc, char *argv[], char *envp[] ) { HANDLE Process; OBJECT_ATTRIBUTES Obja; UNICODE_STRING Unicode; NTSTATUS Status; LPSTR lpstrCmd; CHAR ch; ULONG_PTR Temp; VM_COUNTERS VmCounters; LPSTR p; SYSTEM_BASIC_INFORMATION SystemInformation; BOOL bEnabledDebugPriv; UNREFERENCED_PARAMETER (envp); ExeName = argv[0]; if (argc == 1) { Usage(); } if (!NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &SystemInformation, sizeof(SystemInformation), NULL))) { fprintf(stderr, "Failed to get system basic information\n"); return 1; } PageSize = SystemInformation.PageSize; if (!NT_SUCCESS(NtQuerySystemInformation(SystemRangeStartInformation, &SystemRangeStart, sizeof(SystemRangeStart), NULL))) { // assume usermode is the low half of the address space SystemRangeStart = (ULONG_PTR)MAXLONG_PTR; } #if defined (_X86_) PteWidth = 4; PteBase = (PVOID)0xC0000000; #else PteWidth = 8; PteBase = (PVOID)0x1FFFFF0000000000; #endif if ((USER_SHARED_DATA) && (USER_SHARED_DATA->ProcessorFeatures[PF_PAE_ENABLED])) { PteWidth = 8; } PtesPerPage = PageSize / PteWidth; VaMappedByPageTable = PtesPerPage * PageSize; UserPteMax = (PVOID)((ULONG_PTR)PteBase + (SystemRangeStart / PageSize) * PteWidth); ThisSymbol = (PIMAGEHLP_SYMBOL) symBuffer; ThisSymbol->MaxNameLength = MAX_SYMNAME_SIZE; ProcessId = 0; GetSystemInfo(&SystemInfo); ConvertAppToOem( argc, argv ); lpstrCmd = GetCommandLine(); if( lpstrCmd != NULL ) { CharToOem( lpstrCmd, lpstrCmd ); } do { ch = *lpstrCmd++; } while (ch != ' ' && ch != '\t' && ch != '\0'); while (ch == ' ' || ch == '\t') { ch = *lpstrCmd++; } while (ch == '-') { ch = *lpstrCmd++; // process multiple switch characters as needed do { switch (ch) { case '?': Usage(); case 'C': case 'c': Options |= OPTIONS_CODE_TOO; ch = *lpstrCmd++; break; case 'F': case 'f': fFast = TRUE; ch = *lpstrCmd++; break; case 'L': case 'l': // // l takes log-file-name as argument. // do ch = *lpstrCmd++; while (ch == ' ' || ch == '\t'); p = LogFileName; while (ch && (ch != ' ' && ch != '\t')) { *p++ = ch; ch = *lpstrCmd++; } LogFile = fopen(LogFileName,"wt"); break; case 'M': case 'm': Options |= OPTIONS_RAW_SYMBOLS; ch = *lpstrCmd++; break; case 'P': case 'p': // // pid takes a decimal argument. // do { ch = *lpstrCmd++; } while (ch == ' ' || ch == '\t'); if (ch == '-') { ch = *lpstrCmd++; if (ch == '1') { ProcessId = 0xffffffff; ch = *lpstrCmd++; } } else { while (ch >= '0' && ch <= '9') { Temp = ProcessId * 10 + ch - '0'; if (Temp < ProcessId) { fprintf(stderr, "pid number overflow\n"); ExitProcess(1); } ProcessId = Temp; ch = *lpstrCmd++; } } if (!ProcessId) { fprintf(stderr, "bad pid '%ld'\n", ProcessId); ExitProcess(1); } break; case 'R': case 'r': fRunning = TRUE; ch = *lpstrCmd++; break; case 'S': case 's': fSummary = TRUE; ch = *lpstrCmd++; break; case 'T': case 't': Options |= OPTIONS_PAGE_TABLES; ch = *lpstrCmd++; break; case 'O': case 'o': Options |= OPTIONS_WORKING_SET_OLD; // // Fall through ... // case 'W': case 'w': Options |= OPTIONS_WORKING_SET; ch = *lpstrCmd++; break; case 'V': case 'v': Options |= OPTIONS_VERBOSE; ch = *lpstrCmd++; break; default: Usage(); } } while (ch != ' ' && ch != '\t' && ch != '\0'); // skip over any following white space while (ch == ' ' || ch == '\t') { ch = *lpstrCmd++; } } // // try to enable SeDebugPrivilege to allow opening any process // bEnabledDebugPriv = TRUE; if (!SetCurrentPrivilege(SE_DEBUG_NAME, &bEnabledDebugPriv)) { fprintf(stderr, "Failed to set debug privilege\n"); return 1; } if ( ProcessId == 0 || ProcessId == 0xffffffff ) { ProcessId = 0xffffffff; RtlInitUnicodeString(&Unicode,L"\\WindowsSS"); InitializeObjectAttributes( &Obja, &Unicode, 0, NULL, NULL ); Status = NtOpenProcess( &Process, MAXIMUM_ALLOWED, //PROCESS_VM_READ | PROCESS_VM_OPERATION | PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION, &Obja, NULL ); if ( !NT_SUCCESS(Status) ) { fprintf(stderr, "OpenProcess Failed %lx\n",Status); return 1; } } else { Process = OpenProcess (PROCESS_ALL_ACCESS,FALSE, (ULONG)ProcessId); if ( !Process ) { fprintf(stderr, "OpenProcess %ld failed %lx\n",ProcessId,GetLastError()); return 1; } } if (Options & OPTIONS_WORKING_SET_OLD) { CaptureWorkingSet (Process); LoadTheHeaps (Process); LoadTheThreads (Process, ProcessId); } ComputeModInfo (Process, ProcessId); CaptureVaSpace (Process); // // disable the SeDebugPrivilege if we enabled it above // if(bEnabledDebugPriv) { bEnabledDebugPriv = FALSE; SetCurrentPrivilege (SE_DEBUG_NAME, &bEnabledDebugPriv); } if (Options & OPTIONS_WORKING_SET) { if (Options & OPTIONS_WORKING_SET_OLD) { DumpWorkingSetSnapshot (Process); } else { DumpWorkingSet (Process); } return 1; } if ( !fSummary ) { DumpVaSpace(); } DumpCommit(" Image",ImageCommit); DumpModInfo(); DumpCommit("Mapped",MappedCommit); DumpCommit(" Priv",PrivateCommit); printf("\n"); printf("Dynamic Reserved Memory %ld\n", ReservedBytes ); Status = NtQueryInformationProcess( Process, ProcessVmCounters, (PVOID)&VmCounters, sizeof(VmCounters), NULL ); if ( !NT_SUCCESS(Status) ) { return 1; } printf("\n"); printf("PageFaults: %9ld\n",VmCounters.PageFaultCount); printf("PeakWorkingSetSize %9ld\n",VmCounters.PeakWorkingSetSize); printf("WorkingSetSize %9ld\n",VmCounters.WorkingSetSize); printf("PeakPagedPoolUsage %9ld\n",VmCounters.QuotaPeakPagedPoolUsage); printf("PagedPoolUsage %9ld\n",VmCounters.QuotaPagedPoolUsage); printf("PeakNonPagedPoolUsage %9ld\n",VmCounters.QuotaPeakNonPagedPoolUsage); printf("NonPagedPoolUsage %9ld\n",VmCounters.QuotaNonPagedPoolUsage); printf("PagefileUsage %9ld\n",VmCounters.PagefileUsage); printf("PeakPagefileUsage %9ld\n",VmCounters.PeakPagefileUsage); return 0; } VOID Usage ( VOID ) { fprintf(stderr, "Usage:\n" " Dump the address space:\n" " %s [-sv] -p decimal_process_id\n" "\n" " Dump the current workingset:\n" " %s -o [-mpsv] [-l logfile] -p decimal_process_id\n" "\n" " Dump new additions to the workingset (Stop with ^C):\n" " %s -w [-crv] [-l logfile] -p decimal_process_id\n" "\n" " -c Include code faults faulting PC summary\n" " -m Show all code symbols on page\n" " -o Workingset snapshot w/ summary\n" " -r Print info on individual faults\n" " -s Summary info only\n" " -t Include pagetable info in summary\n" " -w Track new working set additions\n" " -v Verbose\n", ExeName, ExeName, ExeName); ExitProcess(1); } LOGICAL SetCurrentPrivilege ( IN LPCTSTR Privilege, // Privilege to enable/disable IN OUT BOOL *bEnablePrivilege // to enable or disable privilege ) /* If successful, *bEnablePrivlege is set to the new state. If NOT successful, bEnablePrivlege is invalid Returns: TRUE - success FALSE - failure */ { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); LOGICAL bSuccess; BOOL bEnableIt; bEnableIt = *bEnablePrivilege; if (!LookupPrivilegeValue(NULL, Privilege, &luid)) { return FALSE; } if(!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken )) { return FALSE; } // // first pass. get current privilege setting // tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = 0; AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious ); bSuccess = FALSE; if(GetLastError() == ERROR_SUCCESS) { // // second pass. set privilege based on previous setting // tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; *bEnablePrivilege = tpPrevious.Privileges[0].Attributes | (SE_PRIVILEGE_ENABLED); if(bEnableIt) { tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); } else { tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes); } AdjustTokenPrivileges( hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL ); if (GetLastError() == ERROR_SUCCESS) { bSuccess=TRUE; } } CloseHandle(hToken); return bSuccess; }