/*++ 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>
#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 (
        hLibHeap,
        HEAP_ZERO_MEMORY,
        sizeof (PROCESS_VA_INFO));

    if (pThisProcess) { // allocation successful
        // initialize fields

        pThisProcess->BasicInfo =  ALLOCMEM (
            hLibHeap,
            HEAP_ZERO_MEMORY,
            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 (hLibHeap,
            HEAP_ZERO_MEMORY, (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]);

            if (lProcessNameCollectionMethod == PNCM_MODULE_FILE) {
                pProcessNameBuffer = GetProcessSlowName (pProcess);
            } else {
               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 (
            hLibHeap,
            0,
            pThisProcess->BasicInfo);
    }
    FREEMEM (
        hLibHeap,
        0,
        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;
    PWCHAR      ImageNameBuffer = NULL;
    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 (
        hLibHeap,
        HEAP_ZERO_MEMORY,
        sizeof (MODINFO));

    if (!pThisModule) {
        return NULL;
    }

    // allocate this items Instance Name Buffer

    wStringSize = (WORD)(ModuleListEntry->BaseDllName.MaximumLength +
        sizeof (UNICODE_NULL));

    pusInstanceName = ALLOCMEM (
        hLibHeap,
        HEAP_ZERO_MEMORY,
        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 (
        hLibHeap,
        HEAP_ZERO_MEMORY,
        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;
    ImageNameBuffer = usImageFileName.Buffer = ALLOCMEM(
        hLibHeap,
        HEAP_ZERO_MEMORY,
        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(
        hLibHeap,
        HEAP_ZERO_MEMORY,
        usExeFileName.MaximumLength);
    if ( !usExeFileName.Buffer ) {
        goto MBailOut;
    }

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

    // 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
    FREEMEM (
        RtlProcessHeap(),   // this is allocated by an RTL function
        0,
        usNtFileName.Buffer);

//    FREEMEM (
//        hLibHeap,
//        0,
//        RelativeName.RelativeName.Buffer);

    FREEMEM (
        hLibHeap,
        0,
        ImageNameBuffer);

    FREEMEM (
        hLibHeap,
        0,
        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 (
            hLibHeap,
            0,
            pThisModule);
    }

    if (usNtFileName.Buffer) {
        FREEMEM (
            RtlProcessHeap(),   // this is allocated by an RTL function
            0,
            usNtFileName.Buffer);
    }

//    if (RelativeName.RelativeName.Buffer) {
//        FREEMEM (
//            hLibHeap,
//            0,
//            RelativeName.RelativeName.Buffer);
//    }

    if (pusInstanceName) {
        FREEMEM (
            hLibHeap,
            0,
            pusInstanceName);

        }

    if (pusLongInstanceName) {
        FREEMEM (
            hLibHeap,
            0,
            pusLongInstanceName);

        }

    if (ImageNameBuffer) {
        FREEMEM (
            hLibHeap,
            0,
            ImageNameBuffer);
        }

    if (usExeFileName.Buffer){
        FREEMEM (
            hLibHeap,
            0,
            usExeFileName.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 (
                hLibHeap,
                0,
                pProcess->pProcessName);
            pProcess->pProcessName = NULL;
        }
        if (pProcess->BasicInfo) {
            FREEMEM (
                hLibHeap,
                0,
                pProcess->BasicInfo);
            pProcess->BasicInfo = NULL;
        }


        pThisModule = pProcess->pMemBlockInfo;
        while (pThisModule) {
            pNextModule = pThisModule->pNextModule;
            FreeModuleVaData (pThisModule);
            pThisModule = pNextModule;
        }
        //
        //  and finally throw ourselves away
        //
        FREEMEM (
            hLibHeap,
            0,
            pProcess);
    }
    return FALSE;
}

BOOL
FreeModuleVaData (
    IN PMODINFO pModule
)
{
    if (pModule) {
        if (pModule->InstanceName) {
            FREEMEM(
                hLibHeap,
                0,
                pModule->InstanceName);
            pModule->InstanceName = NULL;
        }
        if (pModule->LongInstanceName) {
            FREEMEM(
                hLibHeap,
                0,
                pModule->LongInstanceName);
            pModule->LongInstanceName = NULL;
        }
        FREEMEM (
            hLibHeap,
            0,
            pModule);
    }

    return FALSE;
}