/*++ Copyright (c) 1985 - 1999, Microsoft Corporation Module Name: perfuser.c Abstract: This file implements the Extensible Objects for the User object type Revision History July 97 MCostea created --*/ #include #include #include #include #include #include #include #include #include #include "userctrs.h" // error message definition #include "perfmsg.h" #include "perfutil.h" #include "userdata.h" #define ALL_PROCESSES_STRING L"_All Processes" /* * Declared in winuserp.h */ #define QUC_PID_TOTAL 0xffffffff #define QUERYUSER_TYPE_USER 0x1 #define QUERYUSER_TYPE_CS 0x2 /* * The counters in CSSTATISTICS refer to the USER critical section: * gCSExclusive counts how many times the CS was aquired exclusive * gCSShared counts how many times the CS was aquired shared * gCSTimeExclusive counts the time (NtQueryPerformanceCounter() units) * spent in the resource since the last query. */ typedef struct _tagCSStatistics { DWORD cExclusive; DWORD cShared; __int64 i64TimeExclusive; } CSSTATISTICS; BOOL (WINAPI *QueryUserCounters)(DWORD, LPVOID, DWORD, LPVOID, DWORD ); /* * References to constants which initialize the Object type definitions */ extern USER_DATA_DEFINITION UserDataDefinition; extern CS_DATA_DEFINITION CSDataDefinition; /* * Global to store the process instances * This array is filled when the dll is attached (OpenUserPerformanceData) */ typedef struct _tagInstance { LPWSTR pName; // pointer to Unicode string DWORD sizeName; // in bytes, including terminating NULL DWORD id; // client side ID } ProcessInstance; ProcessInstance *gaInstances; int gNumInstances; PDWORD gpPid; // globals to store the allocate blocks of memory used PDWORD gpdwResult; // as parameters in QueryUserCounters DWORD dwOpenCount; // count of "Open" threads HANDLE ghHeap; BOOL gbInitOK; // true = DLL initialized OK /* * Function Prototypes * used to insure that the data collection functions * accessed by Perflib will have the correct calling format. */ DWORD GlobalCollect( IN LPWSTR lpValueName, IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes, IN DWORD dwQueryType); BOOL FillInstances(VOID); PM_OPEN_PROC OpenUserPerformanceData; PM_COLLECT_PROC CollectUserPerformanceData; PM_CLOSE_PROC CloseUserPerformanceData; BOOL __stdcall DllInit( IN HANDLE DLLHandle, IN DWORD Reason, IN LPVOID ReservedAndUnused ) { char szUser32DllPath[MAX_PATH+15]; HMODULE hUser32Module; ReservedAndUnused; /* * this will prevent the DLL from getting the DLL_THREAD_* messages */ DisableThreadLibraryCalls (DLLHandle); switch(Reason) { case DLL_PROCESS_ATTACH: if (!GetSystemDirectory(szUser32DllPath, MAX_PATH+1)) { return FALSE; } strcat( szUser32DllPath, "\\user32.dll"); hUser32Module = GetModuleHandle(szUser32DllPath); if (!hUser32Module) { return FALSE; } QueryUserCounters = (BOOL (WINAPI *)(DWORD, LPVOID, DWORD, LPVOID, DWORD )) GetProcAddress(hUser32Module, "QueryUserCounters"); if (!QueryUserCounters) { return FALSE; } break; case DLL_PROCESS_DETACH: break; } return TRUE; } DWORD APIENTRY OpenUserPerformanceData( LPWSTR lpDeviceNames ) /*++ Routine Description: This routine opens the interface with the event log and initializes the data structures used to pass data back to the registry. It also calls FillInstances. 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; PDWORD pCounterNameTitleIndex; LPWSTR *pCounterNameTitle; PDWORD pCounterHelpTitleIndex; LPWSTR *pCounterHelpTitle; int i; /* * Since SCREG 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) { hEventLog = MonOpenEventLog(); // open Eventlog interface /* * get counter and help index base values from registry * Open key to registry entry * read First Counter and First Help values * update static data structures by adding base to * offset value in structure. */ status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\PerfUser\\Performance", 0L, KEY_ALL_ACCESS, &hKeyDriverPerf); if (status != ERROR_SUCCESS) { REPORT_ERROR_DATA (USERPERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER, &status, sizeof(status)); /* * these failures are 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 (USERPERF_UNABLE_READ_FIRST_COUNTER, LOG_USER, &status, sizeof(status)); goto OpenExitPoint; } status = RegQueryValueEx( hKeyDriverPerf, "First Help", 0L, &type, (LPBYTE)&dwFirstHelp, &size); if (status != ERROR_SUCCESS) { REPORT_ERROR_DATA (USERPERF_UNABLE_READ_FIRST_HELP, LOG_USER, &status, sizeof(status)); goto OpenExitPoint; } UserDataDefinition.UserObjectType.ObjectNameTitleIndex += dwFirstCounter; UserDataDefinition.UserObjectType.ObjectHelpTitleIndex += dwFirstHelp; pCounterNameTitleIndex = &UserDataDefinition.NumTotals.CounterNameTitleIndex; pCounterHelpTitleIndex = &UserDataDefinition.NumTotals.CounterHelpTitleIndex; for (i = 0; iUserObjectType.NumInstances = gNumInstances; pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) &pUserDataDefinition[1]; for (i = 0; i < gNumInstances; i++) { /* * Collect and format all process instances */ pPerfInstanceDefinition->ParentObjectTitleIndex = 0; pPerfInstanceDefinition->ParentObjectInstance = 0; pPerfInstanceDefinition->UniqueID = (LONG)gaInstances[i].id; pPerfInstanceDefinition->NameOffset = sizeof(PERF_INSTANCE_DEFINITION); pPerfInstanceDefinition->NameLength = gaInstances[i].sizeName; pPerfInstanceDefinition->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + pPerfInstanceDefinition->NameLength; wcscpy((wchar_t*)((PBYTE)pPerfInstanceDefinition + sizeof(PERF_INSTANCE_DEFINITION)), gaInstances[i].pName); pPerfCounterBlock = (PERF_COUNTER_BLOCK *) ((PBYTE)pPerfInstanceDefinition + pPerfInstanceDefinition->ByteLength); pPerfCounterBlock->ByteLength = SIZE_OF_USER_PERFORMANCE_DATA; pdwCounter = (PDWORD) (&pPerfCounterBlock[1]); for (counter = dwTotal = 0; counterUserObjectType.TotalByteLength = (ULONG)((PBYTE)pdwCounter - (PBYTE)pUserDataDefinition); } if (dwQueryType & (QUERY_CS | QUERY_NOCOUNTERS)) { /* * CS object is requested */ (*lpNumObjectTypes) ++; /* * Write CS_DATA_DEFINITION object, no instances * Copy the (constant, initialized) Object Type and counter definitions * to the caller's data buffer */ pCSDataDefinition = (CS_DATA_DEFINITION *)pdwCounter; memmove(pCSDataDefinition, &CSDataDefinition, sizeof(CS_DATA_DEFINITION)); pPerfCounterBlock = (PERF_COUNTER_BLOCK *) &pCSDataDefinition[1]; pPerfCounterBlock->ByteLength = SIZE_OF_CS_PERFORMANCE_DATA; pdwCounter = (DWORD *) &pPerfCounterBlock[1]; if (dwQueryType & QUERY_NOCOUNTERS) { memset(pdwCounter, 0, sizeof(DWORD)*NUM_CS_COUNTERS); } else { CSSTATISTICS CSCounters; /* * When entering for the first time, grab the hardware * cycle counter frequency (if any) */ if (!PrevCSStatistics.i64TimeExclusive) { if (!QueryPerformanceFrequency((LARGE_INTEGER*)&i64Frecv)) i64Frecv = 0; } /* * Retrieve the CS counters */ if (!QueryUserCounters(QUERYUSER_TYPE_CS, NULL, 0, &CSCounters, sizeof(CSSTATISTICS))) { REPORT_ERROR (USERPERF_CS_CANT_QUERY, LOG_USER); } /* * In case of overflow, the error will be of 1, no big deal */ *pdwCounter = CSCounters.cExclusive - PrevCSStatistics.cExclusive; *(pdwCounter + 1) = CSCounters.cShared - PrevCSStatistics.cShared; if (i64Frecv) { /* * Translate the value in counts per milisecond */ *(pdwCounter + 2) = (DWORD)((CSCounters.i64TimeExclusive-PrevCSStatistics.i64TimeExclusive)*1000/i64Frecv); } else { /* * No support for high resolution timer in the hardware */ *(pdwCounter + 2) = 0; } PrevCSStatistics.cExclusive = CSCounters.cExclusive; PrevCSStatistics.cShared = CSCounters.cShared; PrevCSStatistics.i64TimeExclusive = CSCounters.i64TimeExclusive; } pdwCounter += NUM_CS_COUNTERS; } /* * update arguments for return */ *lpcbTotalBytes = (ULONG)((PBYTE)pdwCounter - (PBYTE)*lppData); *lppData = (PVOID) pdwCounter; return ERROR_SUCCESS; } VOID CleanUpInstances(VOID) /*++ Routine Description: Clean-up previous allocated memory Helper function --*/ { int i; if (gaInstances) { for (i = 0; iImageName.Buffer ) { p = wcsrchr(ProcessInfo->ImageName.Buffer, L'\\'); if ( p ) { p++; } else { p = ProcessInfo->ImageName.Buffer; } /* * remove .exe from the name */ if (pPeriod = wcsrchr(p, L'.')) *pPeriod = L'\0'; } else { p = L"SystemProc"; } gaInstances[gNumInstances].sizeName = wcslen(p); /* * convert to bytes */ gaInstances[gNumInstances].sizeName = (gaInstances[gNumInstances].sizeName + 1) * sizeof(WCHAR); gaInstances[gNumInstances].pName = ALLOC(gaInstances[gNumInstances].sizeName); if (!gaInstances[gNumInstances].pName) { REPORT_ERROR_DATA (STATUS_NO_MEMORY, LOG_USER, &status, sizeof(status)); goto ExitNoMemory; } wcscpy(gaInstances[gNumInstances].pName, p); gaInstances[gNumInstances].id = (WORD)ProcessInfo->UniqueProcessId; gNumInstances++; if (ProcessInfo->NextEntryOffset == 0) { break; } TotalOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pLargeBuffer + TotalOffset); } FREE(pLargeBuffer); pLargeBuffer = NULL; if (oldInstances != gNumInstances) { /* * adjust the global buffers */ if (gpdwResult) FREE(gpdwResult); if (gpPid) FREE(gpPid); if (!(gpdwResult = ALLOC(sizeof(DWORD)*gNumInstances*NUM_USER_COUNTERS))) { gpdwResult = NULL; goto ExitNoMemory; } if (!(gpPid = ALLOC(sizeof(DWORD)*gNumInstances))) { gpPid = NULL; goto ExitNoMemory; } for (i = 0; i < gNumInstances; i++) { gpPid[i] = gaInstances[i].id; } } return TRUE; ExitNoMemory: CleanUpInstances(); if (pLargeBuffer) { FREE(pLargeBuffer); } gbInitOK = FALSE; return FALSE; }