|
|
/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) 1992 Microsoft Corporation
Module Name:
prflibva.c
Abstract:
Virtual address space counter evaluation routines
computes the process and image virtual address space usage for return via Perfmon API
Author:
Stolen from the "internal" PVIEW SDK program and adapted for Perfmon by:
a-robw (Bob Watson) 11/29/92
Revision History:
--*/ //
// define routine's "personality"
//
#define UNICODE 1
//
// Include files
//
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winperf.h>
#define PERF_HEAP hLibHeap
#include <perfutil.h>
#include "perfsprc.h"
#define DEFAULT_INCR (64*1024)
#ifdef _WIN64
#define STOP_AT (PVOID)(0xFFFFFFFF80000000)
#else
#define STOP_AT (PVOID)(0x80000000)
#endif
// Function Prototypes
PPROCESS_VA_INFO GetProcessVaData ( IN PSYSTEM_PROCESS_INFORMATION );
PMODINFO GetModuleVaData ( PLDR_DATA_TABLE_ENTRY, // module information structure
PPROCESS_VA_INFO // process data structure
);
BOOL FreeProcessVaData ( IN PPROCESS_VA_INFO );
BOOL FreeModuleVaData ( IN PMODINFO );
PMODINFO LocateModInfo( IN PMODINFO, IN PVOID, IN SIZE_T );
DWORD ProtectionToIndex( IN ULONG );
DWORD dwProcessCount; DWORD dwModuleCount;
PPROCESS_VA_INFO GetSystemVaData ( IN PSYSTEM_PROCESS_INFORMATION pFirstProcess ) /*++
GetSystemVaData
Obtains the Process and Image Virtual Address information for all processes running on the system. (note that the routines called by this function allocate data structures consequently the corresponding FreeSystemVaData must be called to prevent memory "leaks")
Arguments
IN PSYSTEM_PROCESS_INFORMATION pFirstProcess Pointer to first process in list of process structures returned by NtQuerySystemInformation service
Return Value
Pointer to first process in list of processes or NULL if unable to obtain data
--*/ { PSYSTEM_PROCESS_INFORMATION pThisProcess; PPROCESS_VA_INFO pReturnValue = NULL; PPROCESS_VA_INFO pLastProcess; PPROCESS_VA_INFO pNewProcess; DWORD dwStartTime; BOOL bMoreProcesses;
dwProcessCount = 0; dwModuleCount = 0;
if (pFirstProcess != NULL) {
pThisProcess = pFirstProcess; pLastProcess = NULL; bMoreProcesses = TRUE;
while ( bMoreProcesses ) { // loop exit is at bottom of loop
dwStartTime = GetTickCount (); pNewProcess = GetProcessVaData( pThisProcess); // pointer to process Info structure
if (pNewProcess) { // process data found OK
pNewProcess->LookUpTime = GetTickCount() - dwStartTime; dwProcessCount++; if (!pLastProcess) { // this is the first process returned
pReturnValue = pNewProcess; // save return value here
} else { pLastProcess->pNextProcess = pNewProcess; } pLastProcess = pNewProcess; } if ( pThisProcess->NextEntryOffset == 0 ) { bMoreProcesses = FALSE; // this is the last entry
} else { // point to the next process info structure
pThisProcess = (PSYSTEM_PROCESS_INFORMATION) ((PBYTE)pThisProcess + pThisProcess->NextEntryOffset); } } return pReturnValue; // return pointer to first list entry
} else { return NULL; } }
PPROCESS_VA_INFO GetProcessVaData ( IN PSYSTEM_PROCESS_INFORMATION pProcess ) /*++
GetProcessVaData
Gets the Virtual Memory usage details for the process passed in the argument list. Collects the data for all images in use by the process.
Note that this routine allocates data structures that must be freed (using the FreeProcessVaData routine) when finished with them.
Arguments
IN HANDLE hProcess handle to the process to collect data for
Return Value
Pointer to completed Process VA info structure or NULL if unable to collect data --*/ { NTSTATUS Status; HANDLE hProcess; PPROCESS_VA_INFO pThisProcess; PPEB pPeb; PPEB_LDR_DATA Ldr; PLIST_ENTRY LdrHead, LdrNext; LDR_DATA_TABLE_ENTRY LdrEntryData, *pLdrEntry; PMODINFO pNewModule, pLastModule; PVOID pBaseAddress; MEMORY_BASIC_INFORMATION VaBasicInfo; DWORD dwProtection; PMODINFO pMod; SIZE_T dwRegionSize; OBJECT_ATTRIBUTES obProcess; CLIENT_ID ClientId; PUNICODE_STRING pProcessNameBuffer;
// get handle to process
ClientId.UniqueThread = (HANDLE)NULL; ClientId.UniqueProcess = pProcess->UniqueProcessId;
InitializeObjectAttributes( &obProcess, NULL, 0, NULL, NULL );
Status = NtOpenProcess( &hProcess, (ACCESS_MASK)PROCESS_ALL_ACCESS, &obProcess, &ClientId);
if (! NT_SUCCESS(Status)){ // unable to open the process, but still want to
// create pThisProcess so we will not mess up
// the process sequence.
hProcess = 0; // return NULL; // unable to open process
}
// allocate structure
pThisProcess = ALLOCMEM (sizeof (PROCESS_VA_INFO));
if (pThisProcess) { // allocation successful
// initialize fields
pThisProcess->BasicInfo = ALLOCMEM (sizeof (PROCESS_BASIC_INFORMATION));
if (!pThisProcess->BasicInfo) { // Bailout if unable to allocate memory
goto PBailOut; }
// zero process counters
pThisProcess->MappedGuard = 0; pThisProcess->PrivateGuard = 0; pThisProcess->ImageReservedBytes = 0; pThisProcess->ImageFreeBytes = 0; pThisProcess->ReservedBytes = 0; pThisProcess->FreeBytes = 0;
// get process short name from Process Info Structure
// alloc a new buffer since GetProcessShortName reuses the name
// buffer
pThisProcess->pProcessName = ALLOCMEM ((sizeof(UNICODE_STRING) + MAX_PROCESS_NAME_LENGTH));
if (pThisProcess->pProcessName != NULL) { pThisProcess->pProcessName->Length = 0; pThisProcess->pProcessName->MaximumLength = MAX_PROCESS_NAME_LENGTH; pThisProcess->pProcessName->Buffer = (PWSTR)(&pThisProcess->pProcessName[1]);
pProcessNameBuffer = GetProcessShortName (pProcess); RtlCopyUnicodeString (pThisProcess->pProcessName, pProcessNameBuffer); } else { pThisProcess->pProcessName = NULL; }
pThisProcess->dwProcessId = HandleToUlong(pProcess->UniqueProcessId); pThisProcess->hProcess = hProcess;
// zero list pointers
pThisProcess->pMemBlockInfo = NULL; pThisProcess->pNextProcess = NULL;
if (hProcess) {
Status = NtQueryInformationProcess ( hProcess, ProcessBasicInformation, pThisProcess->BasicInfo, sizeof (PROCESS_BASIC_INFORMATION), NULL);
if (!NT_SUCCESS(Status)){ // if error reading data, then bail out
goto SuccessExit; }
// get pointer to the Process Environment Block
pPeb = pThisProcess->BasicInfo->PebBaseAddress;
// read address of loader information structure
Status = NtReadVirtualMemory ( hProcess, &pPeb->Ldr, &Ldr, sizeof (Ldr), NULL);
// bail out if unable to read information
if (!NT_SUCCESS(Status)){ // if error reading data, then bail out
goto SuccessExit; }
//
// get head pointer to linked list of memory modules used by
// this process
//
LdrHead = &Ldr->InMemoryOrderModuleList;
// Get address of next list entry
Status = NtReadVirtualMemory ( hProcess, &LdrHead->Flink, &LdrNext, sizeof (LdrNext), NULL);
// bail out if unable to read information
if (!NT_SUCCESS(Status)){ // if error reading data, then bail out
goto SuccessExit; }
pLastModule = NULL;
// walk down the list of modules until back at the top.
// to list all the images in use by this process
while ( LdrNext != LdrHead ) { // get record attached to list entry
pLdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
Status = NtReadVirtualMemory( hProcess, pLdrEntry, &LdrEntryData, sizeof(LdrEntryData), NULL ); // if unable to read memory, then give up rest of search
// and return what we have already.
if ( !NT_SUCCESS(Status) ) { goto SuccessExit; }
pNewModule = GetModuleVaData ( &LdrEntryData, pThisProcess); if (pNewModule) { // if structure returned...
dwModuleCount++; if (!pLastModule) { // if this is the first module...
// then set list head pointer
pThisProcess->pMemBlockInfo = pNewModule; } else { // otherwise link to list
pLastModule->pNextModule = pNewModule; } pLastModule = pNewModule; } LdrNext = LdrEntryData.InMemoryOrderLinks.Flink; } // end while not at end of list
// now that we have a list of all images, query the process'
// virtual memory for the list of memory blocks in use by this
// process and assign them to the appropriate category of memory
pBaseAddress = NULL; // start at 0 and go to end of User VA space
while (pBaseAddress < STOP_AT) { // truncate to 32-bit if necessary
Status = NtQueryVirtualMemory ( hProcess, pBaseAddress, MemoryBasicInformation, &VaBasicInfo, sizeof(VaBasicInfo), NULL);
if (!NT_SUCCESS(Status)) { goto SuccessExit; } else { // get protection type for index into counter array
dwRegionSize = VaBasicInfo.RegionSize; switch (VaBasicInfo.State) { case MEM_COMMIT: // if the memory is for an IMAGE, then search the image list
// for the corresponding image to update
dwProtection = ProtectionToIndex(VaBasicInfo.Protect); if (VaBasicInfo.Type == MEM_IMAGE) { // update process total
pThisProcess->MemTotals.CommitVector[dwProtection] += dwRegionSize; pMod = LocateModInfo (pThisProcess->pMemBlockInfo, pBaseAddress, dwRegionSize); if (pMod) { // if matching image found, then update
pMod->CommitVector[dwProtection] += dwRegionSize; pMod->TotalCommit += dwRegionSize; } else { // otherwise update orphan total
pThisProcess->OrphanTotals.CommitVector[dwProtection] += dwRegionSize; } } else { // if not assigned to an image, then update the process
// counters
if (VaBasicInfo.Type == MEM_MAPPED) { pThisProcess->MappedCommit[dwProtection] += dwRegionSize; } else { pThisProcess->PrivateCommit[dwProtection] += dwRegionSize; } } break;
case MEM_RESERVE: if (VaBasicInfo.Type == MEM_IMAGE) { pThisProcess->ImageReservedBytes += dwRegionSize; } else { pThisProcess->ReservedBytes += dwRegionSize; } break;
case MEM_FREE: if (VaBasicInfo.Type == MEM_IMAGE) { pThisProcess->ImageFreeBytes += dwRegionSize; } else { pThisProcess->FreeBytes += dwRegionSize; } break;
default: break; } // end switch (VaBasicInfo.State)
} // endif QueryVM ok
// go to next memory block
pBaseAddress = (PVOID)((ULONG_PTR)pBaseAddress + dwRegionSize);
} // end whil not at the end of memory
} // endif hProcess not NULL
} // endif pThisProcess not NULL
SuccessExit:
if (hProcess) CloseHandle(hProcess);
return pThisProcess;
//
// error recovery section, called when the routine is unable to
// complete successfully to clean up before leaving
//
PBailOut: if (pThisProcess->BasicInfo) { FREEMEM (pThisProcess->BasicInfo); } FREEMEM (pThisProcess); if (hProcess) CloseHandle(hProcess); return NULL; }
PMODINFO GetModuleVaData ( PLDR_DATA_TABLE_ENTRY ModuleListEntry, // module information structure
PPROCESS_VA_INFO pProcess // process data structure
) /*++
GetModuleVaData
Gets the Virtual Memory usage details for the module pointed to by the Process Memory Module List Entry argument in the argument list
Note that this routine allocates data structures that must be freed (using the FreeModuleVaData routine) when finished with them.
Arguments
IN HANDLE ModuleListEntry
Return Value
Pointer to completed Module VA info structure or NULL if unable to collect data
--*/ { PMODINFO pThisModule = NULL; // module structure that is returned
PUNICODE_STRING pusInstanceName = NULL; // process->image
PUNICODE_STRING pusLongInstanceName = NULL; // process->fullimagepath
UNICODE_STRING usImageFileName = {0,0, NULL}; // image file name
UNICODE_STRING usExeFileName = {0,0, NULL}; // short name
UNICODE_STRING usNtFileName = {0,0, NULL}; // full Nt File Name
PWCHAR p,p1; NTSTATUS Status; HANDLE hFile; HANDLE hMappedFile; WORD wStringSize;
PVOID MappedAddress; PVOID MapBase; SIZE_T dwMappedSize;
PIMAGE_DOS_HEADER DosHeader; PIMAGE_NT_HEADERS FileHeader;
LARGE_INTEGER liSectionSize; PLARGE_INTEGER pliSectionSize; LARGE_INTEGER liSectionOffset; OBJECT_ATTRIBUTES obFile; IO_STATUS_BLOCK IoStatusBlock; BOOL bRetCode; USHORT wBufOffset; USHORT wDiffSize;
// allocate this item's memory
pThisModule = ALLOCMEM (sizeof (MODINFO));
if (!pThisModule) { return NULL; }
// allocate this items Instance Name Buffer
wStringSize = (WORD)(ModuleListEntry->BaseDllName.MaximumLength + sizeof (UNICODE_NULL));
pusInstanceName = ALLOCMEM (wStringSize + sizeof(UNICODE_STRING)) ;
if (!pusInstanceName) { goto MBailOut; }
pusInstanceName->Length = 0; pusInstanceName->MaximumLength = wStringSize; pusInstanceName->Buffer = (PWCHAR)&pusInstanceName[1];
// save instance name using full file path
wStringSize = (WORD)(ModuleListEntry->FullDllName.MaximumLength + sizeof (UNICODE_NULL));
pusLongInstanceName = ALLOCMEM (wStringSize + sizeof (UNICODE_STRING));
if (!pusLongInstanceName) { goto MBailOut; }
pusLongInstanceName->Length = 0; pusLongInstanceName->MaximumLength = wStringSize; pusLongInstanceName->Buffer = (PWCHAR)&pusLongInstanceName[1];
// allocate temporary buffer for image name
usImageFileName.Length = ModuleListEntry->FullDllName.Length; usImageFileName.MaximumLength = ModuleListEntry->FullDllName.MaximumLength; usImageFileName.Buffer = ALLOCMEM(usImageFileName.MaximumLength); if ( !usImageFileName.Buffer ) { goto MBailOut; }
// allocate temporary buffer for exe name
usExeFileName.Length = ModuleListEntry->BaseDllName.Length; usExeFileName.MaximumLength = ModuleListEntry->BaseDllName.MaximumLength; usExeFileName.Buffer = ALLOCMEM(usExeFileName.MaximumLength); if ( !usExeFileName.Buffer ) { goto MBailOut; } usExeFileName.Buffer[0] = UNICODE_NULL;
// read base .exe/.dll name of image
Status = NtReadVirtualMemory( pProcess->hProcess, ModuleListEntry->BaseDllName.Buffer, usExeFileName.Buffer, usExeFileName.MaximumLength, NULL ); if ( !NT_SUCCESS(Status) ) { goto MBailOut; }
usImageFileName.Buffer[0] = UNICODE_NULL; // read full name of image
Status = NtReadVirtualMemory( pProcess->hProcess, ModuleListEntry->FullDllName.Buffer, usImageFileName.Buffer, usImageFileName.MaximumLength, NULL );
if ( !NT_SUCCESS(Status) ) { goto MBailOut; }
// make a DOS filename to convert to NT again
wDiffSize = wBufOffset = 0; p = p1 = usImageFileName.Buffer; while (*p != (WCHAR)0){ if (*p == L':'){ p1 = p; wDiffSize = wBufOffset; } wBufOffset += sizeof(WCHAR); p++; } if (p1 != usImageFileName.Buffer) { // move pointer
usImageFileName.Buffer = --p1; // adjust length fields
wDiffSize -= (USHORT)(sizeof(WCHAR)); usImageFileName.Length = usImageFileName.Length - wDiffSize; usImageFileName.MaximumLength = usImageFileName.MaximumLength - wDiffSize; }
// Create/copy a NT filename for Nt file operation
bRetCode = RtlDosPathNameToNtPathName_U ( usImageFileName.Buffer, &usNtFileName, NULL, NULL);
if ( !bRetCode ) { goto MBailOut; }
// get handle to file
InitializeObjectAttributes( &obFile, &usNtFileName, FILE_ATTRIBUTE_NORMAL | OBJ_CASE_INSENSITIVE, NULL, NULL );
Status = NtCreateFile ( &hFile, (ACCESS_MASK)GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES, &obFile, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL & FILE_ATTRIBUTE_VALID_FLAGS, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0);
if (!NT_SUCCESS(Status)) { goto MBailOut; }
pliSectionSize = &liSectionSize; liSectionSize.HighPart = 0; liSectionSize.LowPart = 0;
InitializeObjectAttributes ( &obFile, NULL, 0, NULL, NULL);
Status = NtCreateSection ( &hMappedFile, SECTION_QUERY | SECTION_MAP_READ, &obFile, pliSectionSize, PAGE_READONLY, SEC_COMMIT, hFile);
if ( ! NT_SUCCESS(Status)) { CloseHandle(hFile); goto MBailOut; }
// get pointer to mapped memory
MappedAddress = MapBase = NULL; dwMappedSize = 0;
liSectionOffset.LowPart = 0; liSectionOffset.HighPart = 0;
Status = NtMapViewOfSection ( hMappedFile, NtCurrentProcess(), &MapBase, 0L, 0L, &liSectionOffset, &dwMappedSize, ViewShare, 0L, PAGE_READONLY);
CloseHandle(hMappedFile);
if (NT_SUCCESS(Status)) { MappedAddress = MapBase; } else { CloseHandle(hFile); goto MBailOut; }
// check for dos image signature (if a dos file)
DosHeader = (PIMAGE_DOS_HEADER)MappedAddress;
if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE ) { UnmapViewOfFile(MappedAddress); CloseHandle(hFile); goto MBailOut; }
FileHeader = (PIMAGE_NT_HEADERS)((UINT_PTR)DosHeader + DosHeader->e_lfanew);
if ( FileHeader->Signature != IMAGE_NT_SIGNATURE ) { UnmapViewOfFile(MappedAddress); CloseHandle(hFile); goto MBailOut; }
// get base address for this module and save in local data structure
pThisModule->BaseAddress = ModuleListEntry->DllBase;
// get image name
RtlCopyUnicodeString ( pusInstanceName, &usExeFileName);
RtlCopyUnicodeString ( pusLongInstanceName, &usImageFileName);
pThisModule->InstanceName = pusInstanceName; pThisModule->LongInstanceName = pusLongInstanceName; pThisModule->pNextModule = NULL; pThisModule->TotalCommit = 0;
memset ( &pThisModule->CommitVector[0], 0, sizeof (pThisModule->CommitVector));
pThisModule->VirtualSize = FileHeader->OptionalHeader.SizeOfImage;
// close file handles
UnmapViewOfFile(MappedAddress); CloseHandle(hFile);
// free local memory
// this is allocated by an RTL function RtlDosPathNameToNtPathName_U.
RtlFreeHeap(RtlProcessHeap(), 0, usNtFileName.Buffer);
// FREEMEM (
// RelativeName.RelativeName.Buffer);
FREEMEM (usExeFileName.Buffer);
return (pThisModule); // return pointer to completed module structure
//
// Module bail out point, called when the routine is unable to continue
// for some reason. This cleans up any allocated memory, etc.
//
MBailOut:
if (pThisModule) { FREEMEM (pThisModule); }
if (usNtFileName.Buffer) { // this is allocated by an RTL function RtlDosPathNameToNtPathName_U.
RtlFreeHeap(RtlProcessHeap(), 0, usNtFileName.Buffer); }
// if (RelativeName.RelativeName.Buffer) {
// FREEMEM (
// RelativeName.RelativeName.Buffer);
// }
if (pusInstanceName) { FREEMEM (pusInstanceName);
}
if (pusLongInstanceName) { FREEMEM (pusLongInstanceName);
}
if (usExeFileName.Buffer){ FREEMEM (usExeFileName.Buffer); }
if (usImageFileName.Buffer) { FREEMEM(usImageFileName.Buffer); }
return NULL; }
PMODINFO LocateModInfo( IN PMODINFO pFirstMod, IN PVOID pAddress, IN SIZE_T dwExtent ) /*++
LocateModInfo
Locates the images associated with the address passed in the argument list
Arguments
IN PMODINFO pFirstMod, first module entry in process list
IN PVOID Address Address to search for in list
Return Value
Pointer to matching image or NULL if no match found
--*/ { PMODINFO pThisMod;
pThisMod = pFirstMod;
while (pThisMod) { // go to end of list or match is found
// match criteria are:
// address >= Module BaseAddress and
// address+extent between base and base+image_extent
if (pAddress >= pThisMod->BaseAddress) { if ((PVOID)((PDWORD)pAddress + dwExtent) <= (PVOID)((ULONG_PTR)pThisMod->BaseAddress+pThisMod->VirtualSize)) { return (pThisMod); } }
pThisMod = pThisMod->pNextModule;
}
return NULL; }
DWORD ProtectionToIndex( IN ULONG Protection ) /*++
ProtectionToIndex
Determine the memory access protection type and return local code
Arguments
IN ULONG Protection
Process memory protection mask
Return Value
Local value of protection type
--*/ { Protection &= (PAGE_NOACCESS | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
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 0xFFFFFFFF; } }
BOOL FreeSystemVaData ( IN PPROCESS_VA_INFO pFirstProcess ) { PPROCESS_VA_INFO pThisProcess, pNextProcess;
pThisProcess = pFirstProcess; while (pThisProcess) { pNextProcess = pThisProcess->pNextProcess; // save pointer to next
FreeProcessVaData (pThisProcess); pThisProcess = pNextProcess; // do next until NULL pointer
} return (FALSE); }
BOOL FreeProcessVaData ( IN PPROCESS_VA_INFO pProcess ) { PMODINFO pThisModule, pNextModule;
if (pProcess) { if (pProcess->pProcessName) { FREEMEM (pProcess->pProcessName); pProcess->pProcessName = NULL; } if (pProcess->BasicInfo) { FREEMEM (pProcess->BasicInfo); pProcess->BasicInfo = NULL; }
pThisModule = pProcess->pMemBlockInfo; while (pThisModule) { pNextModule = pThisModule->pNextModule; FreeModuleVaData (pThisModule); pThisModule = pNextModule; } //
// and finally throw ourselves away
//
FREEMEM (pProcess); } return FALSE; }
BOOL FreeModuleVaData ( IN PMODINFO pModule ) { if (pModule) { if (pModule->InstanceName) { FREEMEM(pModule->InstanceName); pModule->InstanceName = NULL; } if (pModule->LongInstanceName) { FREEMEM(pModule->LongInstanceName); pModule->LongInstanceName = NULL; } FREEMEM (pModule); }
return FALSE; }
|