/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    perfproc.c

Abstract:

    This file implements an Performance Object that presents
    Image details performance object data

Created:

    Bob Watson  22-Oct-1996

Revision History


--*/
//
//  Include Files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <perfutil.h>
#include "perfsprc.h"
#include "perfmsg.h"
#include "dataproc.h"

static  BOOL           bOldestProcessTime = FALSE;
static  LARGE_INTEGER  OldestProcessTime = {0,0};


DWORD APIENTRY
CollectProcessObjectData (
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

    This routine will return the data for the processor object

Arguments:

   IN OUT   LPVOID   *lppData
         IN: pointer to the address of the buffer to receive the completed
            PerfDataBlock and subordinate structures. This routine will
            append its data to the buffer starting at the point referenced
            by *lppData.
         OUT: points to the first byte after the data structure added by this
            routine. This routine updated the value at lppdata after appending
            its data.

   IN OUT   LPDWORD  lpcbTotalBytes
         IN: the address of the DWORD that tells the size in bytes of the
            buffer referenced by the lppData argument
         OUT: the number of bytes added by this routine is writted to the
            DWORD pointed to by this argument

   IN OUT   LPDWORD  NumObjectTypes
         IN: the address of the DWORD to receive the number of objects added
            by this routine
         OUT: the number of objects added by this routine is writted to the
            DWORD pointed to by this argument

   Returns:

             0 if successful, else Win 32 error code of failure

--*/
{
    DWORD   TotalLen;            //  Length of the total return block

    PSYSTEM_PROCESS_INFORMATION ProcessInfo;

    PPERF_INSTANCE_DEFINITION   pPerfInstanceDefinition;
    PPROCESS_DATA_DEFINITION    pProcessDataDefinition;
    PPROCESS_COUNTER_DATA       pPCD;
    PROCESS_COUNTER_DATA        pcdTotal;

    ULONG   NumProcessInstances;
    BOOLEAN NullProcess;

    PUNICODE_STRING pProcessName;
    ULONG ProcessBufferOffset;

    pProcessDataDefinition = (PROCESS_DATA_DEFINITION *) *lppData;

    //
    //  Check for sufficient space for Process object type definition
    //

    TotalLen = sizeof(PROCESS_DATA_DEFINITION) +
               sizeof (PERF_INSTANCE_DEFINITION) +
               MAX_VALUE_NAME_LENGTH +
               sizeof(PROCESS_COUNTER_DATA);

    if ( *lpcbTotalBytes < TotalLen ) {
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_MORE_DATA;
    }

    //
    //  Define Process data block
    //

    memcpy(pProcessDataDefinition,
           &ProcessDataDefinition,
           sizeof(PROCESS_DATA_DEFINITION));

    pProcessDataDefinition->ProcessObjectType.PerfTime = PerfTime;

    ProcessBufferOffset = 0;

    // Now collect data for each process

    NumProcessInstances = 0;
    ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;

    pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
                                  &pProcessDataDefinition[1];

    // adjust TotalLen to be the size of the buffer already in use
    TotalLen = sizeof (PROCESS_DATA_DEFINITION);

    // zero the total instance buffer
    memset (&pcdTotal, 0, sizeof (pcdTotal));

    while ( ProcessInfo != NULL ) {

        // see if this instance will fit
        TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
                   ((MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD)) * sizeof(WCHAR)) +
                   sizeof (PROCESS_COUNTER_DATA);

        if ( *lpcbTotalBytes < TotalLen ) {
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_MORE_DATA;
        }

        // check for Live processes
        //  (i.e. name or threads)

        pProcessName = NULL;

        if ((ProcessInfo->ImageName.Buffer != NULL) ||
            (ProcessInfo->NumberOfThreads > 0)){
                // thread is not Dead
            // get process name
            if (lProcessNameCollectionMethod == PNCM_MODULE_FILE) {
                pProcessName = GetProcessSlowName (ProcessInfo);
            } else {
               pProcessName = GetProcessShortName (ProcessInfo);
            }
            NullProcess = FALSE;
        } else {
            // thread is dead
            NullProcess = TRUE;
        }

        if ( !NullProcess ) {

            // get the old process creation time the first time we are in
            // this routine
            if (!bOldestProcessTime) {
                if (OldestProcessTime.QuadPart <= 0) {
                    OldestProcessTime = ProcessInfo->CreateTime;
                } else if (ProcessInfo->CreateTime.QuadPart > 0) {
                    // both time values are not zero, see which one is smaller
                    if (OldestProcessTime.QuadPart >
                        ProcessInfo->CreateTime.QuadPart) {
                        OldestProcessTime = ProcessInfo->CreateTime;
                    }
                }
            }

            // get Pool usage for this process

            NumProcessInstances++;

            MonBuildInstanceDefinition(pPerfInstanceDefinition,
                (PVOID *) &pPCD,
                0,
                0,
                (DWORD)-1,
                (pProcessName ? pProcessName->Buffer : NULL)
                );

            // test structure for Quadword Alignment
            assert (((DWORD)(pPCD) & 0x00000007) == 0);

            //
            //  Format and collect Process data
            //

            pPCD->CounterBlock.ByteLength = sizeof (PROCESS_COUNTER_DATA);
            //
            //  Convert User time from 100 nsec units to counter frequency.
            //
            pcdTotal.ProcessorTime +=
                pPCD->ProcessorTime = ProcessInfo->KernelTime.QuadPart +
                                    ProcessInfo->UserTime.QuadPart;
            pcdTotal.UserTime +=
                pPCD->UserTime = ProcessInfo->UserTime.QuadPart;
            pcdTotal.KernelTime +=
                pPCD->KernelTime = ProcessInfo->KernelTime.QuadPart;

            pcdTotal.PeakVirtualSize +=
                pPCD->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
            pcdTotal.VirtualSize +=
                pPCD->VirtualSize = ProcessInfo->VirtualSize;

            pcdTotal.PageFaults +=
                pPCD->PageFaults = ProcessInfo->PageFaultCount;
            pcdTotal.PeakWorkingSet +=
                pPCD->PeakWorkingSet = ProcessInfo->PeakWorkingSetSize;
            pcdTotal.TotalWorkingSet +=
                pPCD->TotalWorkingSet = ProcessInfo->WorkingSetSize;

#ifdef _DATAPROC_PRIVATE_WS_
            pcdTotal.PrivateWorkingSet +=
                pPCD->PrivateWorkingSet = ProcessInfo->PrivateWorkingSetSize;
            pcdTotal.SharedWorkingSet +=
                pPCD->SharedWorkingSet =
					ProcessInfo->WorkingSetSize -
					ProcessInfo->PrivateWorkingSetSize;
#endif //_DATAPROC_PRIVATE_WS_

            pcdTotal.PeakPageFile +=
                pPCD->PeakPageFile = ProcessInfo->PeakPagefileUsage;
            pcdTotal.PageFile +=
                pPCD->PageFile = ProcessInfo->PagefileUsage;

            pcdTotal.PrivatePages +=
                pPCD->PrivatePages = ProcessInfo->PrivatePageCount;

            pcdTotal.ThreadCount +=
                pPCD->ThreadCount = ProcessInfo->NumberOfThreads;

            // base priority is not totaled
            pPCD->BasePriority = ProcessInfo->BasePriority;

            // elpased time is not totaled
            if (bOldestProcessTime &&
                (ProcessInfo->CreateTime.QuadPart <= 0)) {
                pPCD->ElapsedTime = OldestProcessTime.QuadPart;
            } else {
                pPCD->ElapsedTime = ProcessInfo->CreateTime.QuadPart;
            }

            pPCD->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
            pPCD->CreatorProcessId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);

            pcdTotal.PagedPool +=
                pPCD->PagedPool = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
            pcdTotal.NonPagedPool +=
                pPCD->NonPagedPool = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
            pcdTotal.HandleCount +=
                pPCD->HandleCount = (DWORD)ProcessInfo->HandleCount;

            
            // update I/O counters
            pcdTotal.ReadOperationCount +=
                pPCD->ReadOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
            pcdTotal.DataOperationCount += 
                pPCD->DataOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
            pcdTotal.WriteOperationCount +=
                pPCD->WriteOperationCount = ProcessInfo->WriteOperationCount.QuadPart;
            pcdTotal.DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
                pPCD->DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
            pcdTotal.OtherOperationCount +=
                pPCD->OtherOperationCount = ProcessInfo->OtherOperationCount.QuadPart;

            pcdTotal.ReadTransferCount +=
                pPCD->ReadTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
            pcdTotal.DataTransferCount +=
                pPCD->DataTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
            pcdTotal.WriteTransferCount +=
                pPCD->WriteTransferCount = ProcessInfo->WriteTransferCount.QuadPart;
            pcdTotal.DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
                pPCD->DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
            pcdTotal.OtherTransferCount +=
                pPCD->OtherTransferCount = ProcessInfo->OtherTransferCount.QuadPart;
                        
            // set perfdata pointer to next byte
            pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
        }
        // exit if this was the last process in list
        if (ProcessInfo->NextEntryOffset == 0) {
            break;
        }

        // point to next buffer in list
        ProcessBufferOffset += ProcessInfo->NextEntryOffset;
        ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
                          &pProcessBuffer[ProcessBufferOffset];

    }

    if (NumProcessInstances > 0) {

        // see if the total instance will fit
        TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
                    (MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD))*
                        sizeof(WCHAR) +
                   sizeof (PROCESS_COUNTER_DATA);

        if ( *lpcbTotalBytes < TotalLen ) {
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_MORE_DATA;
        }

        // it looks like it will fit so create "total" instance

        NumProcessInstances++;

        // set the Total Elapsed Time to be the current time so that it will
        // show up as 0 when displayed.
        pcdTotal.ElapsedTime = pProcessDataDefinition->ProcessObjectType.PerfTime.QuadPart;

        MonBuildInstanceDefinition(pPerfInstanceDefinition,
            (PVOID *) &pPCD,
            0,
            0,
            (DWORD)-1,
            wszTotal);

        // test structure for Quadword Alignment
        assert (((DWORD)(pPCD) & 0x00000007) == 0);

        //
        //  Format and collect Process data
        //
        memcpy (pPCD, &pcdTotal, sizeof (pcdTotal));
        pPCD->CounterBlock.ByteLength = sizeof (PROCESS_COUNTER_DATA);
        pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];

    }

    // flag so we don't have to get the oldest Process Creation time again.
    bOldestProcessTime = TRUE;

    // Note number of process instances

    pProcessDataDefinition->ProcessObjectType.NumInstances =
        NumProcessInstances;

    //
    //  Now we know how large an area we used for the
    //  Process definition, so we can update the offset
    //  to the next object definition
    //

    *lpcbTotalBytes =
        pProcessDataDefinition->ProcessObjectType.TotalByteLength =
        (DWORD)((PCHAR) pPerfInstanceDefinition -
        (PCHAR) pProcessDataDefinition);

#if DBG
    if (*lpcbTotalBytes > TotalLen ) {
        DbgPrint ("\nPERFPROC: Process Perf Ctr. Instance Size Underestimated:");
        DbgPrint ("\nPERFPROC:   Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
    }
#endif

    *lppData = (LPVOID) pPerfInstanceDefinition;

    *lpNumObjectTypes = 1;

    return ERROR_SUCCESS;
}
