PERFMEM.C

/*++ 

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

perfmem.c

Abstract:

This file implements the Extensible Objects for the application
memory object

Created:

Bob Watson 24 august 1995

Revision History

None

--*/
//
// Include Files
//
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "..\inc\appmemi.h" // definitions shared with the app. dll
#include "memctrs.h" // error message definition
#include "perfmsg.h" // message utilities
#include "perfutil.h" // perf data utilities
#include "datamem.h" // perf data structure definitions

//
// References to constants which initialize the Object type definitions
//
extern APPMEM_DATA_DEFINITION AppMemDataDefinition;

static DWORD dwOpenCount = 0; // count of "Open" threads
static BOOL bInitOK = FALSE; // true = DLL initialized OK

//
// App Mem counter data structures
//

static HANDLE hAppMemSharedMemory = NULL; // Handle of counter Shared Memory
static HANDLE hAppMemMutex = NULL; // sync mutex for memory

static PAPPMEM_DATA_HEADER pDataHeader = NULL; // pointer to header of shared mem
//
// Function Prototypes
//
// these are used to insure that the data collection functions
// accessed by Perflib will have the correct calling format.
//

PM_OPEN_PROC OpenAppMemPerformanceData;
PM_COLLECT_PROC CollectAppMemPerformanceData;
PM_CLOSE_PROC CloseAppMemPerformanceData;


DWORD APIENTRY
OpenAppMemPerformanceData(
LPWSTR lpDeviceNames
)
/*++

Routine Description:

This routine will open and map the memory used by the App Mem DLL to
pass performance data in. This routine also initializes the data
structures used to pass data back to the registry

Arguments:

Pointer to object ID of each device to be opened

Return Value:

None.

--*/

{
LONG status;
HKEY hKeyDriverPerf;
DWORD size;
DWORD type;
DWORD dwFirstCounter;
DWORD dwFirstHelp;
APPMEM_COUNTERS ac;

//
// Since WINLOGON is multi-threaded and will call this routine in
// order to service remote performance queries, this library
// must keep track of how many times it has been opened (i.e.
// how many threads have accessed it). the registry routines will
// limit access to the initialization routine to only one thread
// at a time so synchronization (i.e. reentrancy) should not be
// a problem
//

if (dwOpenCount == 0) { // do this only on the first instance of the DLL
// open Eventlog interface

hEventLog = MonOpenEventLog();

// open/create shared memory used by the application to pass performance values
status = GetSharedMemoryDataHeader (
&hAppMemSharedMemory, &hAppMemMutex, &pDataHeader,
TRUE); // only read access is required

if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (status, LOG_USER, NULL, 0);
goto OpenExitPoint;
}

// get counter and help index base values from registry
// Open key to registry entry
// read First Counter and First Help values
// update static data strucutures by adding base to
// offset value in structure.

status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\AppMem\\Performance",
0L,
KEY_ALL_ACCESS,
&hKeyDriverPerf);

if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (APPMEM_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
goto OpenExitPoint;
}

size = sizeof (DWORD);
status = RegQueryValueEx(
hKeyDriverPerf,
"First Counter",
0L,
&type,
(LPBYTE)&dwFirstCounter,
&size);

if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (APPMEM_UNABLE_READ_FIRST_COUNTER, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
goto OpenExitPoint;
}

size = sizeof (DWORD);
status = RegQueryValueEx(
hKeyDriverPerf,
"First Help",
0L,
&type,
(LPBYTE)&dwFirstHelp,
&size);

if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (APPMEM_UNABLE_READ_FIRST_HELP, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
goto OpenExitPoint;
}

//
// NOTE: the initialization program could also retrieve
// LastCounter and LastHelp if they wanted to do
// bounds checking on the new number. e.g.
//
// counter->CounterNameTitleIndex += dwFirstCounter;
// if (counter->CounterNameTitleIndex > dwLastCounter) {
// LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
// }

AppMemDataDefinition.AppMemObjectType.ObjectNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemObjectType.ObjectHelpTitleIndex += dwFirstHelp;

AppMemDataDefinition.AppMemBytesAllocated.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemBytesAllocated.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemBytesAllocated.CounterOffset +=
(DWORD)((LPBYTE)&ac.dwAppMemBytesAllocated - (LPBYTE)&ac);

AppMemDataDefinition.AppMemAllocs.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemAllocs.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemAllocs.CounterOffset =
(DWORD)((LPBYTE)&ac.dwAppMemAllocs - (LPBYTE)&ac);

AppMemDataDefinition.AppMemAllocsSec.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemAllocsSec.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemAllocsSec.CounterOffset =
(DWORD)((LPBYTE)&ac.dwAppMemAllocsSec - (LPBYTE)&ac);

AppMemDataDefinition.AppMemReAllocs.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemReAllocs.CounterHelpTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemReAllocs.CounterOffset=
(DWORD)((LPBYTE)&ac.dwAppMemReAllocs - (LPBYTE)&ac);

AppMemDataDefinition.AppMemReAllocsSec.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemReAllocsSec.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemReAllocsSec.CounterOffset =
(DWORD)((LPBYTE)&ac.dwAppMemReAllocsSec - (LPBYTE)&ac);

AppMemDataDefinition.AppMemFrees.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemFrees.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemFrees.CounterOffset =
(DWORD)((LPBYTE)&ac.dwAppMemFrees - (LPBYTE)&ac);

AppMemDataDefinition.AppMemFreesSec.CounterNameTitleIndex += dwFirstCounter;
AppMemDataDefinition.AppMemFreesSec.CounterHelpTitleIndex += dwFirstHelp;
AppMemDataDefinition.AppMemFreesSec.CounterOffset =
(DWORD)((LPBYTE)&ac.dwAppMemFreesSec - (LPBYTE)&ac);

RegCloseKey (hKeyDriverPerf); // close key to registry

bInitOK = TRUE; // ok to use this function
}

dwOpenCount++; // increment OPEN counter

status = ERROR_SUCCESS; // for successful exit

OpenExitPoint:

return status;
}


DWORD APIENTRY
CollectAppMemPerformanceData(
IN LPWSTR lpValueName,
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

This routine will return the data for the Application memory counters

Arguments:

IN LPWSTR lpValueName
pointer to a wide character string passed by registry.

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

Return Value:

ERROR_MORE_DATA if buffer passed is too small to hold data
any error conditions encountered are reported to the event log if
event logging is enabled.

ERROR_SUCCESS if success or any other error. Errors, however are
also reported to the event log.

--*/
{
// Variables for reformating the data

ULONG SpaceNeeded;
PERF_INSTANCE_DEFINITION *pPerfInstanceDef;
APPMEM_DATA_DEFINITION *pAppMemDataDefinition;
DWORD dwQueryType;
DWORD dwInstanceIndex;
PAPPMEM_INSTANCE pThisAppInstanceData = NULL;
BOOL bFreeMutex;
PAPPMEM_COUNTERS pAC;

//
// before doing anything else, see if Open went OK
//
if ((!bInitOK) || (pDataHeader == NULL)) {
// unable to continue because open failed.
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS; // yes, this is a successful exit
}

// see if this is a foreign (i.e. non-NT) computer data request
//
dwQueryType = GetQueryType (lpValueName);

if (dwQueryType == QUERY_FOREIGN) {
// this routine does not service requests for data from
// Non-NT computers
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}

if (dwQueryType == QUERY_ITEMS){
if ( !(IsNumberInUnicodeList (AppMemDataDefinition.AppMemObjectType.ObjectNameTitleIndex, lpValueName))) {
// request received for data object not provided by this routine
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
}

// if here, then this must be one for us so load data structures

pAppMemDataDefinition = (APPMEM_DATA_DEFINITION *) *lppData;

if (hAppMemMutex != NULL) {
if (WaitForSingleObject (hAppMemMutex, SHARED_MEMORY_MUTEX_TIMEOUT) == WAIT_FAILED) {
// unable to obtain a lock
bFreeMutex = FALSE;
} else {
bFreeMutex = TRUE;
}
} else {
bFreeMutex = FALSE;
}

// always return an "instance sized" buffer after the definition blocks
// to prevent perfmon from reading bogus data. This is strictly a hack
// to accomodate how PERFMON handles the "0" instance case.
// By doing this, perfmon won't choke when there are no instances
// and the counter object & counters will be displayed in the
// list boxes, even though no instances will be listed.

SpaceNeeded = sizeof(APPMEM_DATA_DEFINITION) +
((pDataHeader->dwInstanceCount > 0 ? pDataHeader->dwInstanceCount : 1) * (
sizeof(PERF_INSTANCE_DEFINITION) +
MAX_SIZEOF_INSTANCE_NAME +
sizeof(APPMEM_COUNTERS)));

if ( *lpcbTotalBytes < SpaceNeeded ) {
// not enough room so return nothing.
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}

// copy the object & counter definition information

memmove(pAppMemDataDefinition,
&AppMemDataDefinition,
sizeof(APPMEM_DATA_DEFINITION));

// prepare to read the instance data
pPerfInstanceDef = (PERF_INSTANCE_DEFINITION *) &pAppMemDataDefinition[1];

// point to the first instance structure in the shared buffer
pThisAppInstanceData = FIRST_INUSE(pDataHeader);

// process each of the instances in the shared memory buffer
dwInstanceIndex = 0;
while ((dwInstanceIndex < pDataHeader->dwInstanceCount) &&
(pThisAppInstanceData != NULL)){
// initialize this instance
MonBuildInstanceDefinition (
pPerfInstanceDef,
&pAC, // pointer to first byte after instance def
0, // no parent
0, // object to reference
(DWORD)PERF_NO_UNIQUE_ID,
pThisAppInstanceData->wcszInstanceName);
//
// collect and format app memory perf data from shared memory
//

pAC->CounterBlock.ByteLength = sizeof(APPMEM_COUNTERS);

// set pointer to first counter data field

pAC->dwAppMemBytesAllocated = pThisAppInstanceData->dwApplicationBytes;
pAC->dwAppMemAllocs = pThisAppInstanceData->dwAllocCalls;
pAC->dwAppMemAllocsSec = pThisAppInstanceData->dwAllocCalls;
pAC->dwAppMemReAllocs = pThisAppInstanceData->dwReAllocCalls;
pAC->dwAppMemReAllocsSec = pThisAppInstanceData->dwReAllocCalls;
pAC->dwAppMemFrees = pThisAppInstanceData->dwFreeCalls;
pAC->dwAppMemFreesSec = pThisAppInstanceData->dwFreeCalls;

// setup for the next instance
dwInstanceIndex++;
pThisAppInstanceData =
APPMEM_INST(pDataHeader, pThisAppInstanceData->dwOffsetOfNext);
pPerfInstanceDef =
(PERF_INSTANCE_DEFINITION *)((LPBYTE)pAC + sizeof(APPMEM_COUNTERS));
}

if (dwInstanceIndex == 0) {
// zero fill one instance sized block of data if there are no
// data instances

memset (pPerfInstanceDef, 0,
(sizeof(PERF_INSTANCE_DEFINITION) +
MAX_SIZEOF_INSTANCE_NAME +
sizeof(APPMEM_COUNTERS)));

// adjust pointer to point to end of zeroed block
(BYTE *)pPerfInstanceDef += (sizeof(PERF_INSTANCE_DEFINITION) +
MAX_SIZEOF_INSTANCE_NAME +
sizeof(APPMEM_COUNTERS));

}

// done with the shared memory so free the mutex if one was
// acquired

if (bFreeMutex) {
ReleaseMutex (hAppMemMutex);
}

*lppData = (PVOID)pPerfInstanceDef;

// update arguments for return

*lpNumObjectTypes = 1;
pAppMemDataDefinition->AppMemObjectType.NumInstances = dwInstanceIndex;

pAppMemDataDefinition->AppMemObjectType.TotalByteLength =
*lpcbTotalBytes = (DWORD)((PBYTE)pPerfInstanceDef -
(PBYTE) pAppMemDataDefinition);

return ERROR_SUCCESS;
}

DWORD APIENTRY
CloseAppMemPerformanceData(
)

/*++

Routine Description:

This routine closes the open handles to Application Memory
usage performance counters

Arguments:

None.


Return Value:

ERROR_SUCCESS

--*/

{
if ((--dwOpenCount) == 0) { // when this is the last thread...

if (hAppMemSharedMemory != NULL) CloseHandle(hAppMemSharedMemory);
if (hAppMemMutex != NULL) CloseHandle(hAppMemMutex);

pDataHeader = NULL;

MonCloseEventLog();
}

return ERROR_SUCCESS;

}