Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

735 lines
20 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
perfos.c
Abstract:
Author:
Bob Watson (a-robw) Aug 95
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <assert.h>
#include <perfutil.h>
#include "perfos.h"
#include "perfosmc.h"
// bit field definitions for collect function flags
#define POS_GET_SYS_PERF_INFO ((DWORD)0x00010000)
#define POS_COLLECT_CACHE_DATA ((DWORD)0x00010001)
#define POS_COLLECT_CPU_DATA ((DWORD)0x00000002)
#define POS_COLLECT_MEMORY_DATA ((DWORD)0x00010004)
#define POS_COLLECT_OBJECTS_DATA ((DWORD)0x00000008)
#define POS_COLLECT_PAGEFILE_DATA ((DWORD)0x00000010)
#define POS_COLLECT_SYSTEM_DATA ((DWORD)0x00010020)
#define POS_COLLECT_FUNCTION_MASK ((DWORD)0x0000003F)
#define POS_COLLECT_GLOBAL_DATA ((DWORD)0x0001003F)
#define POS_COLLECT_FOREIGN_DATA ((DWORD)0)
#define POS_COLLECT_COSTLY_DATA ((DWORD)0)
// global variables to this DLL
HANDLE ThisDLLHandle = NULL;
HANDLE hEventLog = NULL;
HANDLE hLibHeap = NULL;
SYSTEM_BASIC_INFORMATION BasicInfo;
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
PM_OPEN_PROC OpenOSObject;
PM_COLLECT_PROC CollectOSObjectData;
PM_CLOSE_PROC CloseOSObject;
LPWSTR wszTotal = NULL;
// variables local to this module
static POS_FUNCTION_INFO posDataFuncInfo[] = {
{CACHE_OBJECT_TITLE_INDEX, POS_COLLECT_CACHE_DATA, 0, CollectCacheObjectData},
{PROCESSOR_OBJECT_TITLE_INDEX, POS_COLLECT_CPU_DATA, 0, CollectProcessorObjectData},
{MEMORY_OBJECT_TITLE_INDEX, POS_COLLECT_MEMORY_DATA, 0, CollectMemoryObjectData},
{OBJECT_OBJECT_TITLE_INDEX, POS_COLLECT_OBJECTS_DATA, 0, CollectObjectsObjectData},
{PAGEFILE_OBJECT_TITLE_INDEX, POS_COLLECT_PAGEFILE_DATA, 0, CollectPageFileObjectData},
{SYSTEM_OBJECT_TITLE_INDEX, POS_COLLECT_SYSTEM_DATA, 0, CollectSystemObjectData}
};
#define POS_NUM_FUNCS (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))
static bInitOk = FALSE;
static bReportedNotOpen = FALSE;
static
BOOL
DllProcessAttach (
IN HANDLE DllHandle
)
/*++
Description:
perform any initialization function that apply to all object
modules
--*/
{
BOOL bReturn = TRUE;
LONG status;
WCHAR wszTempBuffer[512];
LONG lStatus;
DWORD dwBufferSize;
UNREFERENCED_PARAMETER (DllHandle);
if (hLibHeap == NULL) {
hLibHeap = HeapCreate (0, 1, 0);
}
assert (hLibHeap != NULL);
if (hLibHeap == NULL) {
return FALSE;
}
// open handle to the event log
if (hEventLog == NULL) {
hEventLog = MonOpenEventLog((LPWSTR)L"PerfOS");
//
// collect basic and static processor data
//
status = NtQuerySystemInformation(
SystemBasicInformation,
&BasicInfo,
sizeof(SYSTEM_BASIC_INFORMATION),
NULL
);
if (!NT_SUCCESS(status)) {
BasicInfo.PageSize = 0;
status = (LONG)RtlNtStatusToDosError(status);
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFOS_UNABLE_QUERY_BASIC_INFO,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&status);
bReturn = FALSE;
}
}
lStatus = GetPerflibKeyValue (
szTotalValue,
REG_SZ,
sizeof(wszTempBuffer),
(LPVOID)&wszTempBuffer[0],
DEFAULT_TOTAL_STRING_LEN,
(LPVOID)&szDefaultTotalString[0]);
if (lStatus == ERROR_SUCCESS) {
// then a string was returned in the temp buffer
dwBufferSize = lstrlenW (wszTempBuffer) + 1;
dwBufferSize *= sizeof (WCHAR);
wszTotal = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
if (wszTotal == NULL) {
// unable to allocate buffer so use static buffer
wszTotal = (LPWSTR)&szDefaultTotalString[0];
} else {
memcpy (wszTotal, wszTempBuffer, dwBufferSize);
}
} else {
// unable to get string from registry so just use static buffer
wszTotal = (LPWSTR)&szDefaultTotalString[0];
}
return bReturn;
}
static
BOOL
DllProcessDetach (
IN HANDLE DllHandle
)
{
UNREFERENCED_PARAMETER (DllHandle);
if ((dwCpuOpenCount + dwPageOpenCount + dwObjOpenCount) != 0) {
// close the objects now sinc this is the last chance
// as the DLL is in the process of being unloaded
// if any of the open counters are > 1, then set them to 1
// to insure the object is closed on this call
if (dwCpuOpenCount > 1) dwCpuOpenCount = 1;
if (dwPageOpenCount > 1) dwPageOpenCount = 1;
if (dwObjOpenCount > 1) dwObjOpenCount = 1;
CloseOSObject();
}
assert ((dwCpuOpenCount + dwPageOpenCount + dwObjOpenCount) == 0);
if ((wszTotal != NULL) && (wszTotal != &szDefaultTotalString[0])) {
FREEMEM (hLibHeap, 0, wszTotal);
wszTotal = NULL;
}
if (HeapDestroy (hLibHeap)) hLibHeap = NULL;
if (hEventLog != NULL) {
MonCloseEventLog ();
hEventLog = NULL;
}
return TRUE;
}
BOOL
__stdcall
DllInit(
IN HANDLE DLLHandle,
IN DWORD Reason,
IN LPVOID ReservedAndUnused
)
{
ReservedAndUnused;
// this will prevent the DLL from getting
// the DLL_THREAD_* messages
DisableThreadLibraryCalls (DLLHandle);
switch(Reason) {
case DLL_PROCESS_ATTACH:
return DllProcessAttach (DLLHandle);
case DLL_PROCESS_DETACH:
return DllProcessDetach (DLLHandle);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
default:
return TRUE;
}
}
DWORD APIENTRY
OpenOSObject (
LPWSTR lpDeviceNames
)
/*++
Routine Description:
This routine will initialize the data structures used to pass
data back to the registry
Arguments:
Pointer to object ID of each device to be opened (PerfGen)
Return Value:
None.
--*/
{
DWORD status;
// cache object does not need to be opened
// open Processor Object
status = OpenProcessorObject (lpDeviceNames);
// memory object does not need to be opened
// open Objects object
if (status == ERROR_SUCCESS) {
status = OpenObjectsObject (lpDeviceNames);
// open Pagefile object
if (status == ERROR_SUCCESS) {
status = OpenPageFileObject (lpDeviceNames);
if (status != ERROR_SUCCESS) {
// processor & Objects opened & page file did not
// close the open objects
CloseProcessorObject ();
CloseObjectsObject();
}
} else {
// processor Opend and Objects did not
// close the open objects
CloseProcessorObject();
}
} else {
// nothing opened
}
// System Object does not need to be opened
if (status == ERROR_SUCCESS) {
bInitOk = TRUE;
} else {
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFOS_UNABLE_OPEN,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&status);
}
return status;
}
DWORD APIENTRY
ReadOSObjectData (
IN DWORD FunctionCallMask,
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the OS object
Arguments:
IN DWORD FunctionCallMask
bit mask of functions to call
IN OUT LPVOID *lppData
IN: pointer to the address of the buffer to receive the completed
data structure. In the case of an item list, Global or Costly
query, this will be a collection of one or more perf data objects.
In the case of a PERF_QUERY_OBJECTS request, this will be an array
of DWORDs listing the object ID's of the perf data objects
supported by this DLL.
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 number of objects listed in the array of DWORDs referenced
by the pObjList argument
OUT: the number of objects returned by this routine is writted to the
DWORD pointed to by this argument
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
NTSTATUS Status;
DWORD lReturn = ERROR_SUCCESS;
DWORD FunctionIndex;
DWORD dwNumObjectsFromFunction;
DWORD dwOrigBuffSize;
DWORD dwByteSize;
DWORD dwReturnedBufferSize;
// collect data
if (FunctionCallMask & POS_GET_SYS_PERF_INFO) {
Status = NtQuerySystemInformation(
SystemPerformanceInformation,
&SysPerfInfo,
sizeof(SysPerfInfo),
&dwReturnedBufferSize
);
if (!NT_SUCCESS(Status)) {
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFOS_UNABLE_QUERY_SYS_PERF_INFO,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&Status);
memset (&SysPerfInfo, 0, sizeof(SysPerfInfo));
}
} else {
memset (&SysPerfInfo, 0, sizeof(SysPerfInfo));
}
*lpNumObjectTypes = 0;
dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
*lpcbTotalBytes = 0;
// remove query bits
FunctionCallMask &= POS_COLLECT_FUNCTION_MASK;
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
if (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit &
FunctionCallMask) {
dwNumObjectsFromFunction = 0;
// check for QUADWORD alignment of data buffer
assert (((ULONG_PTR)(*lppData) & 0x00000007) == 0);
lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
lppData,
&dwByteSize,
&dwNumObjectsFromFunction);
if (lReturn == ERROR_SUCCESS) {
*lpNumObjectTypes += dwNumObjectsFromFunction;
*lpcbTotalBytes += dwByteSize;
dwOrigBuffSize -= dwByteSize;
dwByteSize = dwOrigBuffSize;
} else {
break;
}
}
// *lppData is updated by each function
// *lpcbTotalBytes is updated after each successful function
// *lpNumObjects is updated after each successful function
}
return lReturn;
}
DWORD APIENTRY
QueryOSObjectData (
IN LPDWORD pObjList,
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 LPDWORD *pObjList
pointer to an array of Performance Objects that are
to be returned to the caller. Each object is referenced by its
DWORD value. If the first element in the array is one of the
following then only the first item is read and the following
data is returned:
PERF_QUERY_OBJECTS an array of object id's supported
by this function is returned in the data
PERF_QUERY_GLOBAL all perf objects supported by this
function are returned (Except COSTLY objects)
PERF_QUERY_COSTLY all COSTLY perf objects supported
by this function are returned
Foreign objects are not supported by this API
IN OUT LPVOID *lppData
IN: pointer to the address of the buffer to receive the completed
data structure. In the case of an item list, Global or Costly
query, this will be a collection of one or more perf data objects.
In the case of a PERF_QUERY_OBJECTS request, this will be an array
of DWORDs listing the object ID's of the perf data objects
supported by this DLL.
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 number of objects listed in the array of DWORDs referenced
by the pObjList argument
OUT: the number of objects returned by this routine is writted to the
DWORD pointed to by this argument
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
LONG lReturn = ERROR_SUCCESS;
DWORD FunctionCallMask = 0;
DWORD FunctionIndex;
LPDWORD pdwRetBuffer;
DWORD ObjectIndex;
if (!bInitOk) {
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFOS_NOT_OPEN,
NULL,
0,
0,
NULL,
NULL);
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
lReturn = ERROR_SUCCESS;
goto QUERY_BAIL_OUT;
}
// evaluate the object list
if (*lpNumObjectTypes == 1) {
// then see if it's a predefined request value
if (pObjList[0] == PERF_QUERY_GLOBAL) {
FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
} else if (pObjList[0] == PERF_QUERY_COSTLY) {
FunctionCallMask = POS_COLLECT_COSTLY_DATA;
} else if (pObjList[0] == PERF_QUERY_OBJECTS) {
if (*lpcbTotalBytes < (POS_NUM_FUNCS * sizeof(DWORD))) {
lReturn = ERROR_MORE_DATA;
} else {
pdwRetBuffer = (LPDWORD)*lppData;
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
pdwRetBuffer[FunctionIndex] =
posDataFuncInfo[FunctionIndex].dwObjectId;
}
*lppData = &pdwRetBuffer[FunctionIndex];
*lpcbTotalBytes = (POS_NUM_FUNCS * sizeof(DWORD));
*lpNumObjectTypes = FunctionIndex;
lReturn = ERROR_SUCCESS;
goto QUERY_BAIL_OUT;
}
}
}
if (FunctionCallMask == 0) {
// it's not a predfined value so run through the list
// read the object list and build the call mask
ObjectIndex = 0;
while (ObjectIndex < *lpNumObjectTypes) {
// search for this object in the list of object id's
// supported by this DLL
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
if (pObjList[ObjectIndex] ==
posDataFuncInfo[FunctionIndex].dwObjectId) {
FunctionCallMask |=
posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
break; // out of inner loop
}
}
ObjectIndex++;
}
}
if (FunctionCallMask != 0) {
lReturn = ReadOSObjectData (FunctionCallMask,
lppData,
lpcbTotalBytes,
lpNumObjectTypes);
} else {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
lReturn = ERROR_SUCCESS;
}
QUERY_BAIL_OUT:
return lReturn;
}
DWORD APIENTRY
CollectOSObjectData (
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 processor object
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
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
LONG lReturn = ERROR_SUCCESS;
// build bit mask of functions to call
DWORD dwQueryType;
DWORD FunctionCallMask = 0;
DWORD FunctionIndex;
if (!bInitOk) {
if (!bReportedNotOpen) {
bReportedNotOpen = ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFOS_NOT_OPEN,
NULL,
0,
0,
NULL,
NULL);
}
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
lReturn = ERROR_SUCCESS;
goto COLLECT_BAIL_OUT;
}
dwQueryType = GetQueryType (lpValueName);
switch (dwQueryType) {
case QUERY_ITEMS:
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
if (IsNumberInUnicodeList (
posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
FunctionCallMask |=
posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
}
}
break;
case QUERY_GLOBAL:
FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
break;
case QUERY_FOREIGN:
FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
break;
case QUERY_COSTLY:
FunctionCallMask = POS_COLLECT_COSTLY_DATA;
break;
default:
FunctionCallMask = POS_COLLECT_COSTLY_DATA;
break;
}
if (FunctionCallMask != 0) {
lReturn = ReadOSObjectData (FunctionCallMask,
lppData,
lpcbTotalBytes,
lpNumObjectTypes);
} else {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
lReturn = ERROR_SUCCESS;
}
COLLECT_BAIL_OUT:
return lReturn;
}
DWORD APIENTRY
CloseOSObject (
)
/*++
Routine Description:
This routine closes the open handles to the Signal Gen counters.
Arguments:
None.
Return Value:
ERROR_SUCCESS
--*/
{
DWORD status;
DWORD dwReturn = ERROR_SUCCESS;
// cache object does not need to be closeed
// close Processor Object
status = CloseProcessorObject ();
assert (status == ERROR_SUCCESS);
if (status != ERROR_SUCCESS) dwReturn = status;
// memory object does not need to be closeed
// close Objects object
status = CloseObjectsObject ();
assert (status == ERROR_SUCCESS);
if (status != ERROR_SUCCESS) dwReturn = status;
// close Pagefile object
status = ClosePageFileObject ();
assert (status == ERROR_SUCCESS);
if (status != ERROR_SUCCESS) dwReturn = status;
// System Object does not need to be closeed
return dwReturn;
}