-#ifdef PERF_METHOD1
-/*************************************************************
- METHOD 1
-
- Obtaining statistics in Windows is done at a low level by
- calling registry functions, in particular the key
- HKEY_PERFORMANCE_DATA on NT and successors.
- Something equivalent exists on Win95, see Microsoft article
- HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
- but it is not implemented here.
-
- The list of objects to be polled is specified in the string
- passed to RegQueryValueEx in ReadStat() below.
- On NT, all objects are polled even if info about only one is
- required. This is fixed in Windows 2000. See articles
- INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
- INFO: Performance Data Changes Between Windows NT 4.0 and Windows
- 2000 (KB 296523)
-
- It is unclear to me how the counters are primarily identified.
- Whether it's by name strings or by the offset of their strings
- as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
- reported by the registry functions in GetNameStrings( ) below].
- Microsoft documentation seems to say that both methods should
- work.
-
- In the interest of speed and language independence, the main
- code below relies on offsets. However if debug is enabled, the
- code verifies that the names of the corresponding strings are
- as expected.
-
-*****************************************************************/
-
-/* Object and counter indices and names */
-#define PROCESSOR_OBJECT_INDEX 238
-#define PROCESSOR_OBJECT_STRING "238"
-#define PROCESSOR_OBJECT_NAME "Processor"
-#define PROCESSOR_TIME_COUNTER 6
-#define PROCESSOR_TIME_NAME "% Processor Time"
-
-#define BYTEINCREMENT 800 /* Block to add to PerfData */
-
-/*****************************************************************
- *
- Macros to navigate through the performance data.
-
- *****************************************************************/
-#define FirstObject(PerfData)\
- ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
-#define NextObject(PerfObj)\
- ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
-#define ObjectCounterBlock(PerfObj)\
- ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
-#define FirstInstance(PerfObj )\
- ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
-#define InstanceCounterBlock(PerfInst)\
- ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
-#define NextInstance(PerfInst )\
- ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
- InstanceCounterBlock(PerfInst)->ByteLength) )
-#define FirstCounter(PerfObj)\
- ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
-#define NextCounter(PerfCntr)\
- ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
-
-/*****************************************************************
- *
- Load the counter and object names from the registry
- to cygwin_load.perf->NameStrings
- and index them in cygwin_load.perf->NamesArray
-
- NameStrings seems to be taken from the file
- X:\Winnt\system32\perfc009.dat
-
- This is used only for name verification during initialization,
- if DEBUG(D_load) is TRUE.
-
-*****************************************************************/
-static BOOL GetNameStrings( )
-{
- HKEY hKeyPerflib; // handle to registry key
- DWORD dwArraySize; // size for array
- DWORD dwNamesSize; // size for strings
- LPSTR lpCurrentString; // pointer for enumerating data strings
- DWORD dwCounter; // current counter index
- LONG res;
-
- /* Get the number of Counter items into dwArraySize. */
- if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
- 0,
- KEY_QUERY_VALUE, /* KEY_READ, */
- &hKeyPerflib))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
- return FALSE;
- }
- dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
- if ((res = RegQueryValueEx( hKeyPerflib,
- "Last Counter",
- NULL,
- NULL,
- (LPBYTE) &dwArraySize,
- &dwNamesSize ))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
- return FALSE;
- }
- RegCloseKey( hKeyPerflib );
- /* Open the key containing the counter and object names. */
- if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
- 0,
- KEY_READ,
- &hKeyPerflib))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
- return FALSE;
- }
- /* Get the size of the Counter value in the key
- and then read the value in the tail of NamesArray */
- dwNamesSize = 0;
- lpCurrentString = NULL;
- while (1) {
- res = RegQueryValueEx( hKeyPerflib,
- "Counter",
- NULL,
- NULL,
- (unsigned char *) lpCurrentString,
- &dwNamesSize);
- if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
- (cygwin_load.perf->NamesArray != NULL)) break;
- if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
- (res == ERROR_MORE_DATA)) {
- /* Allocate memory BOTH for the names array and for the counter and object names */
- if ((cygwin_load.perf->NamesArray =
- (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
- != NULL) {
- /* Point to area for the counter and object names */
- lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
- continue;
- }
- DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
- }
- else { /* Serious error */
- DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
- }
- return FALSE;
- }
- RegCloseKey( hKeyPerflib );
- /* Index the names into an array. */
- while (*lpCurrentString) {
- dwCounter = atol( lpCurrentString );
- lpCurrentString += (lstrlen(lpCurrentString)+1);
- cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
- lpCurrentString += (strlen(lpCurrentString)+1);
- }
- return TRUE;
-}
-
-/*****************************************************************
- *
- Find the value of the Processor Time counter
-
-*****************************************************************/
-static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
- PPERF_COUNTER_DEFINITION CurCntr,
- PPERF_COUNTER_BLOCK PtrToCntr,
- unsigned long long * TimePtr){
- int j;
- /* Scan all counters. */
- for( j = 0; j < PerfObj->NumCounters; j++ ) {
- if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
- /* Verify it is really the proc time counter */
- if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
- ((cygwin_load.perf->NamesArray != NULL) && /* Verify name */
- (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex],
- PROCESSOR_TIME_NAME)))) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Incorrect Perf counter type or name %x %s",
- (unsigned) CurCntr->CounterType,
- cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
- return FALSE;
- }
- *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
- return TRUE; /* return TRUE as soon as we found the counter */
- }
- /* Get the next counter. */
- CurCntr = NextCounter( CurCntr );
- }
- return FALSE;
-}
-
-/*****************************************************************
- *
- ReadStat()
- Measures current Time100ns and IdleCount
- Return TRUE if success.
-
- *****************************************************************/
-static BOOL ReadStat(unsigned long long int *Time100nsPtr,
- unsigned long long int * IdleCountPtr)
-{
- PPERF_OBJECT_TYPE PerfObj;
- PPERF_INSTANCE_DEFINITION PerfInst;
- PPERF_COUNTER_DEFINITION PerfCntr;
- PPERF_COUNTER_BLOCK PtrToCntr;
- DWORD i, k, res;
-
- /* Get the performance data for the Processor object
- There is no need to open a key.
- We may need to blindly increase the buffer size.
- BufferSize does not return info but may be changed */
- while (1) {
- DWORD BufferSize = cygwin_load.perf->BufferSize;
- res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
- PROCESSOR_OBJECT_STRING,
- NULL,
- NULL,
- (LPBYTE) cygwin_load.perf->PerfData,
- &BufferSize );
- if (res == ERROR_SUCCESS) break;
- if (res == ERROR_MORE_DATA ) {
- /* Increment if necessary to get a buffer that is big enough. */
- cygwin_load.perf->BufferSize += BYTEINCREMENT;
- if ((cygwin_load.perf->PerfData =
- (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
- != NULL) continue;
- DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
- }
- else { /* Serious error */
- DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
- }
- return FALSE;
- }
- /* Initialize the counters */
- *Time100nsPtr = 0;
- *IdleCountPtr = 0;
- /* We should only have one object, but write general code just in case. */
- PerfObj = FirstObject( cygwin_load.perf->PerfData );
- for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
- /* We are only interested in the processor object */
- if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
- /* Possibly verify it is really the Processor object. */
- if ((cygwin_load.perf->NamesArray != NULL) &&
- (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
- PROCESSOR_OBJECT_NAME))) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Incorrect Perf object name %s",
- cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
- return FALSE;
- }
- /* Get the first counter */
- PerfCntr = FirstCounter( PerfObj );
- /* See if the object has instances.
- It should, but write general code. */
- if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
- PerfInst = FirstInstance( PerfObj );
- for( k = 0; k < PerfObj->NumInstances; k++ ) {
- /* There can be several processors.
- Accumulate both the Time100ns and the idle counter.
- Starting with Win2000 there is an instance named "_Total".
- Do not use it. We only use instances with a single
- character in the name.
- If we examine the object names, we also look at the instance
- names and their lengths and issue reports */
- if ( cygwin_load.perf->NamesArray != NULL) {
- CHAR ascii[30]; /* The name is in unicode */
- wsprintf(ascii,"%.29lS",
- (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
- log_write(0, LOG_MAIN,
- "Perf: Found processor instance \"%s\", length %d",
- ascii, PerfInst->NameLength);
- if ((PerfInst->NameLength != 4) &&
- (strcmp(ascii, "_Total") != 0)) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Perf: WARNING: Unexpected processor instance name");
- return FALSE;
- }
- }
- if (PerfInst->NameLength == 4) {
- *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
- PtrToCntr = InstanceCounterBlock(PerfInst);
- if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
- return FALSE;
- }
- }
- PerfInst = NextInstance( PerfInst );
- }
- return (*Time100nsPtr != 0); /* Something was read */
- }
- else { /* No instance, just the counter data */
- *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
- PtrToCntr = ObjectCounterBlock(PerfObj);
- return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
- }
- }
- PerfObj = NextObject( PerfObj );
- }
- return FALSE; /* Did not find the Processor object */
-}
-
-#elif defined(PERF_METHOD2)
-
-/*************************************************************
- METHOD 2
-
- Uses NtQuerySystemInformation.
- This requires definitions that are not part of
- standard include files.
-*************************************************************/