/*++ Copyright (c) 1996 Microsoft Corporation Module Name: perfos.c Abstract: Author: Bob Watson (a-robw) Aug 95 Revision History: --*/ #include #include #include #include #include #include #include #define PERF_HEAP hLibHeap #include #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 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])) BOOL bInitOk = FALSE; BOOL bReportedNotOpen = FALSE; #ifdef DBG LONG64 clock0, clock1, freq, diff; #endif BOOL DllProcessAttach ( IN HANDLE DllHandle ) /*++ Description: perform any initialization function that apply to all object modules --*/ { BOOL bReturn = TRUE; NTSTATUS status; WCHAR wszTempBuffer[MAX_PATH]; 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); if (hEventLog != NULL) { ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, 0, PERFOS_UNABLE_QUERY_BASIC_INFO, NULL, 0, sizeof(DWORD), NULL, (LPVOID)&status); } bReturn = FALSE; } } wszTempBuffer[0] = UNICODE_NULL; wszTempBuffer[MAX_PATH-1] = UNICODE_NULL; lStatus = GetPerflibKeyValue ( szTotalValue, REG_SZ, sizeof(wszTempBuffer) - sizeof(WCHAR), (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 (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; } 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 (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 Open and Objects did not // close the open objects CloseProcessorObject(); } } else { // nothing opened } if (status == ERROR_SUCCESS) { status = OpenSystemObject(lpDeviceNames); if (status != ERROR_SUCCESS) { CloseProcessorObject(); CloseObjectsObject(); CloseSystemObject(); } } if (status == ERROR_SUCCESS) { bInitOk = TRUE; } else if (hEventLog != NULL) { ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, 0, PERFOS_UNABLE_OPEN, NULL, 0, sizeof(DWORD), NULL, (LPVOID)&status); } #ifdef DBG DbgPrint("PERFOS: OpenOsObject() status = %d\n", status); #endif 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) { #ifdef DBG STARTTIMING; #endif Status = NtQuerySystemInformation( SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), &dwReturnedBufferSize ); if (!NT_SUCCESS(Status)) { if (hEventLog != NULL) { ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, 0, PERFOS_UNABLE_QUERY_SYS_PERF_INFO, NULL, 0, sizeof(DWORD), NULL, (LPVOID)&Status); } memset (&SysPerfInfo, 0, sizeof(SysPerfInfo)); } #ifdef DBG ENDTIMING(("PERFOS: %d takes %I64d ms\n", __LINE__, diff)); #endif } 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); #ifdef DBG STARTTIMING; #endif lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) ( lppData, &dwByteSize, &dwNumObjectsFromFunction); if (lReturn == ERROR_SUCCESS) { *lpNumObjectTypes += dwNumObjectsFromFunction; *lpcbTotalBytes += dwByteSize; dwOrigBuffSize -= dwByteSize; dwByteSize = dwOrigBuffSize; } else { break; } #ifdef DBG ENDTIMING(("PERFOS: %d POS %d takes %I64d ms\n", __LINE__, FunctionIndex, diff)); #endif } // *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) { if (hEventLog != NULL)) { 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; #ifdef DBG STARTTIMING; #endif if (!bInitOk) { if (!bReportedNotOpen) { if (hEventLog != NULL) { 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: #ifdef DBG ENDTIMING (("PERFOS: %d Collect takes %I64d ms\n", __LINE__, diff)); #endif 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; // close System object status = CloseSystemObject(); if (status != ERROR_SUCCESS) dwReturn = status; return dwReturn; }