| char netcpu_ntperf_id[]="\ |
| @(#)netcpu_ntperf.c (c) Copyright 2005-2007, Hewlett-Packard Company, Version 2.4.3"; |
| |
| #if HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> |
| #else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| #endif |
| |
| #if 0 |
| #include <limits.h> |
| #include <sys/types.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <string.h> |
| #endif |
| |
| #include <assert.h> |
| |
| #include <process.h> |
| #include <time.h> |
| |
| #include <windows.h> |
| #include <assert.h> |
| |
| #include <winsock2.h> |
| // If you are trying to compile on Windows 2000 or NT 4.0 you may |
| // need to define DONT_IPV6 in the "sources" files. |
| #ifndef DONT_IPV6 |
| #include <ws2tcpip.h> |
| #endif |
| |
| #include "netsh.h" |
| #include "netlib.h" |
| |
| // |
| // System CPU time information class. |
| // Used to get CPU time information. |
| // |
| // SDK\inc\ntexapi.h |
| // Function x8: SystemProcessorPerformanceInformation |
| // DataStructure: SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION |
| // |
| |
| #define SystemProcessorPerformanceInformation 0x08 |
| |
| typedef struct |
| { |
| LARGE_INTEGER IdleTime; |
| LARGE_INTEGER KernelTime; |
| LARGE_INTEGER UserTime; |
| LARGE_INTEGER DpcTime; |
| LARGE_INTEGER InterruptTime; |
| LONG InterruptCount; |
| } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; |
| |
| // |
| // Calls to get the information |
| // |
| typedef ULONG (__stdcall *NT_QUERY_SYSTEM_INFORMATION)( |
| ULONG SystemInformationClass, |
| PVOID SystemInformation, |
| ULONG SystemInformationLength, |
| PULONG ReturnLength |
| ); |
| |
| NT_QUERY_SYSTEM_INFORMATION NtQuerySystemInformation = NULL; |
| |
| |
| static LARGE_INTEGER TickHz = {0,0}; |
| |
| _inline LARGE_INTEGER ReadPerformanceCounter(VOID) |
| { |
| LARGE_INTEGER Counter; |
| QueryPerformanceCounter(&Counter); |
| |
| return(Counter); |
| } // ReadperformanceCounter |
| |
| |
| /* The NT performance data is accessed through the NtQuerySystemInformation |
| call. References to the PDH.DLL have been deleted. This structure |
| is the root for these data structures. */ |
| |
| typedef struct sPerfObj |
| { |
| LARGE_INTEGER StartTime; |
| LARGE_INTEGER EndTime; |
| SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StartInfo[MAXCPUS +1]; |
| SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION EndInfo[MAXCPUS +1]; |
| } PerfObj, *PPerfObj; |
| |
| static PerfObj *PerfCntrs; |
| |
| // Forward declarations |
| |
| PerfObj *InitPerfCntrs(); |
| void RestartPerfCntrs(PerfObj *PerfCntrs); |
| double ReportPerfCntrs(PerfObj *PerfCntrs); /* returns CPU utilization */ |
| void ClosePerfCntrs(PerfObj *PerfCntrs); |
| |
| |
| void |
| cpu_util_init(void) |
| { |
| if (NtQuerySystemInformation == NULL) { |
| // Open the performance counter interface |
| PerfCntrs = InitPerfCntrs(); |
| } |
| return; |
| } |
| |
| void |
| cpu_util_terminate(void) |
| { |
| return; |
| } |
| |
| int |
| get_cpu_method(void) |
| { |
| return NT_METHOD; |
| } |
| |
| typedef unsigned __int64 uint64_t; |
| |
| void |
| get_cpu_idle(uint64_t *res) |
| { |
| RestartPerfCntrs(PerfCntrs); |
| return; |
| } |
| |
| float |
| calibrate_idle_rate(int iterations, int interval) |
| { |
| return (float)0.0; |
| } |
| |
| |
| /* |
| InitPerfCntrs() - |
| |
| Changed to no longer access the NT performance registry interfaces. |
| A direct call to NtQuerySystemInformation (an undocumented NT API) |
| is made instead. Parameters determined by decompilation of ntkrnlmp |
| and ntdll. |
| */ |
| |
| |
| PerfObj *InitPerfCntrs() |
| { |
| PerfObj *NewPerfCntrs; |
| DWORD NTVersion; |
| DWORD status; |
| SYSTEM_INFO SystemInfo; |
| |
| GetSystemInfo(&SystemInfo); |
| |
| NewPerfCntrs = (PerfObj *)GlobalAlloc(GPTR, sizeof(PerfObj)); |
| assert(NewPerfCntrs != NULL); |
| |
| ZeroMemory((PCHAR)NewPerfCntrs, sizeof(PerfObj)); |
| |
| // get NT version |
| NTVersion = GetVersion(); |
| if (NTVersion >= 0x80000000) |
| { |
| fprintf(stderr, "Not running on Windows NT\n"); |
| exit(1); |
| } |
| |
| // locate the calls we need in NTDLL |
| //Lint |
| NtQuerySystemInformation = |
| (NT_QUERY_SYSTEM_INFORMATION)GetProcAddress( GetModuleHandle("ntdll.dll"), |
| "NtQuerySystemInformation" ); |
| |
| if ( !(NtQuerySystemInformation) ) |
| { |
| //Lint |
| status = GetLastError(); |
| fprintf(stderr, "GetProcAddressFailed, status: %X\n", status); |
| exit(1); |
| } |
| |
| // setup to measure timestamps with the high resolution timers. |
| if (QueryPerformanceFrequency(&TickHz) == FALSE) |
| { |
| fprintf(stderr,"MAIN - QueryPerformanceFrequency Failed!\n"); |
| exit(2); |
| } |
| |
| RestartPerfCntrs(NewPerfCntrs); |
| |
| return(NewPerfCntrs); |
| } /* InitPerfCntrs */ |
| |
| /* |
| RestartPerfCntrs() - |
| |
| The Performance counters must be read twice to produce rate and |
| percentage results. This routine is called before the start of a |
| benchmark to establish the initial counters. It must be called a |
| second time after the benchmark completes to collect the final state |
| of the performance counters. ReportPerfCntrs is called to print the |
| results after the benchmark completes. |
| */ |
| |
| void RestartPerfCntrs(PerfObj *PerfCntrs) |
| { |
| DWORD returnLength = 0; //Lint |
| DWORD returnNumCPUs; //Lint |
| DWORD i; |
| |
| DWORD status; |
| SYSTEM_INFO SystemInfo; |
| |
| GetSystemInfo(&SystemInfo); |
| |
| // Move previous data from EndInfo to StartInfo. |
| CopyMemory((PCHAR)&PerfCntrs->StartInfo[0], |
| (PCHAR)&PerfCntrs->EndInfo[0], |
| sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*(MAXCPUS +1)); |
| |
| PerfCntrs->StartTime = PerfCntrs->EndTime; |
| |
| // get the current CPUTIME information |
| if ( (status = NtQuerySystemInformation( SystemProcessorPerformanceInformation, |
| (PCHAR)&PerfCntrs->EndInfo[0], sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*MAXCPUS, |
| &returnLength )) != 0) |
| { |
| fprintf(stderr, "NtQuery failed, status: %X\n", status); |
| exit(1); |
| } |
| |
| PerfCntrs->EndTime = ReadPerformanceCounter(); |
| |
| // Validate that NtQuery returned a reasonable amount of data |
| if ((returnLength % sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) != 0) |
| { |
| fprintf(stderr, "NtQuery didn't return expected amount of data\n"); |
| fprintf(stderr, "Expected a multiple of %i, returned %i\n", |
| sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), returnLength); |
| exit(1); |
| } |
| returnNumCPUs = returnLength / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); |
| |
| if (returnNumCPUs != (int)SystemInfo.dwNumberOfProcessors) |
| { |
| fprintf(stderr, "NtQuery didn't return expected amount of data\n"); |
| fprintf(stderr, "Expected data for %i CPUs, returned %i\n", |
| (int)SystemInfo.dwNumberOfProcessors, returnNumCPUs); |
| exit(1); |
| } |
| |
| // Zero entries not returned by NtQuery |
| ZeroMemory((PCHAR)&PerfCntrs->EndInfo[returnNumCPUs], |
| sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)* |
| (MAXCPUS +1 - returnNumCPUs)); |
| |
| // Total all of the CPUs |
| // KernelTime needs to be fixed-up; it includes both idle & |
| // true kernel time |
| // Note that kernel time also includes DpcTime & InterruptTime, but |
| // I like this. |
| for (i=0; i < returnNumCPUs; i++) |
| { |
| PerfCntrs->EndInfo[i].KernelTime.QuadPart -= PerfCntrs->EndInfo[i].IdleTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].IdleTime.QuadPart += PerfCntrs->EndInfo[i].IdleTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].KernelTime.QuadPart += PerfCntrs->EndInfo[i].KernelTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].UserTime.QuadPart += PerfCntrs->EndInfo[i].UserTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].DpcTime.QuadPart += PerfCntrs->EndInfo[i].DpcTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].InterruptTime.QuadPart += PerfCntrs->EndInfo[i].InterruptTime.QuadPart; |
| PerfCntrs->EndInfo[MAXCPUS].InterruptCount += PerfCntrs->EndInfo[i].InterruptCount; |
| } |
| |
| } /* RestartPerfCntrs */ |
| |
| /* |
| ReportPerfCntrs() - |
| This routine reports the results of the various performance |
| counters. |
| */ |
| |
| double ReportPerfCntrs(PerfObj *PerfCntrs) |
| { |
| double tot_CPU_Util; |
| int i; |
| int duration; // in 100 usecs |
| |
| LARGE_INTEGER ActualDuration; |
| |
| SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION DeltaInfo[MAXCPUS +1]; |
| |
| LARGE_INTEGER TotalCPUTime[MAXCPUS +1]; |
| |
| SYSTEM_INFO SystemInfo; |
| |
| GetSystemInfo(&SystemInfo); |
| |
| for (i=0; i <= MAXCPUS; i++) |
| { |
| DeltaInfo[i].IdleTime.QuadPart = PerfCntrs->EndInfo[i].IdleTime.QuadPart - |
| PerfCntrs->StartInfo[i].IdleTime.QuadPart; |
| DeltaInfo[i].KernelTime.QuadPart = PerfCntrs->EndInfo[i].KernelTime.QuadPart - |
| PerfCntrs->StartInfo[i].KernelTime.QuadPart; |
| DeltaInfo[i].UserTime.QuadPart = PerfCntrs->EndInfo[i].UserTime.QuadPart - |
| PerfCntrs->StartInfo[i].UserTime.QuadPart; |
| DeltaInfo[i].DpcTime.QuadPart = PerfCntrs->EndInfo[i].DpcTime.QuadPart - |
| PerfCntrs->StartInfo[i].DpcTime.QuadPart; |
| DeltaInfo[i].InterruptTime.QuadPart = PerfCntrs->EndInfo[i].InterruptTime.QuadPart - |
| PerfCntrs->StartInfo[i].InterruptTime.QuadPart; |
| DeltaInfo[i].InterruptCount = PerfCntrs->EndInfo[i].InterruptCount - |
| PerfCntrs->StartInfo[i].InterruptCount; |
| |
| TotalCPUTime[i].QuadPart = |
| DeltaInfo[i].IdleTime.QuadPart + |
| DeltaInfo[i].KernelTime.QuadPart + |
| DeltaInfo[i].UserTime.QuadPart; |
| // KernelTime already includes DpcTime & InterruptTime! |
| // + DeltaInfo[i].DpcTime.QuadPart + |
| // DeltaInfo[i].InterruptTime.QuadPart; |
| } |
| |
| tot_CPU_Util = 100.0*(1.0 - (double)DeltaInfo[MAXCPUS].IdleTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint |
| |
| // Re-calculate duration, since we may have stoped early due to cntr-C. |
| ActualDuration.QuadPart = PerfCntrs->EndTime.QuadPart - |
| PerfCntrs->StartTime.QuadPart; |
| |
| // convert to 1/10 milliseconds (100 usec) |
| ActualDuration.QuadPart = (ActualDuration.QuadPart*10000)/TickHz.QuadPart; |
| duration = ActualDuration.LowPart; |
| |
| if (verbosity > 1) |
| { |
| fprintf(where,"ActualDuration (ms): %d\n", duration/10); |
| } |
| |
| if (verbosity > 1) |
| { |
| fprintf(where, "%% CPU _Total"); |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t CPU %i", i); |
| } |
| } |
| fprintf(where, "\n"); |
| |
| fprintf(where, "Busy %5.2f", tot_CPU_Util); |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.2f", |
| 100.0*(1.0 - (double)DeltaInfo[i].IdleTime.QuadPart/(double)TotalCPUTime[i].QuadPart)); //Lint |
| } |
| } |
| fprintf(where, "\n"); |
| |
| fprintf(where, "Kernel %5.2f", |
| 100.0*(double)DeltaInfo[MAXCPUS].KernelTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint |
| |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.2f", |
| 100.0*(double)DeltaInfo[i].KernelTime.QuadPart/(double)TotalCPUTime[i].QuadPart); //Lint |
| } |
| } |
| fprintf(where, "\n"); |
| |
| fprintf(where, "User %5.2f", |
| 100.0*(double)DeltaInfo[MAXCPUS].UserTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); |
| |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.2f", |
| 100.0*(double)DeltaInfo[i].UserTime.QuadPart/TotalCPUTime[i].QuadPart); //Lint |
| } |
| } |
| fprintf(where, "\n"); |
| |
| fprintf(where, "Dpc %5.2f", |
| 100.0*(double)DeltaInfo[MAXCPUS].DpcTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint |
| |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.2f", |
| 100.0*(double)DeltaInfo[i].DpcTime.QuadPart/(double)TotalCPUTime[i].QuadPart); //Lint |
| } |
| } |
| fprintf(where, "\n"); |
| |
| fprintf(where, "Interrupt %5.2f", |
| 100.0*(double)DeltaInfo[MAXCPUS].InterruptTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint |
| |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.2f", |
| 100.0*(double)DeltaInfo[i].InterruptTime.QuadPart/TotalCPUTime[i].QuadPart); //Lint |
| } |
| } |
| fprintf(where, "\n\n"); |
| |
| fprintf(where, "Interrupt/Sec. %5.1f", |
| (double)DeltaInfo[MAXCPUS].InterruptCount*10000.0/(double)duration); |
| |
| if ((int)SystemInfo.dwNumberOfProcessors > 1) |
| { |
| for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) |
| { |
| fprintf(where, "\t %5.1f", |
| (double)DeltaInfo[i].InterruptCount*10000.0/(double)duration); |
| } |
| } |
| fprintf(where, "\n\n"); |
| fflush(where); |
| } |
| |
| return (tot_CPU_Util); |
| |
| } /* ReportPerfCntrs */ |
| |
| /* |
| ClosePerfCntrs() - |
| |
| This routine cleans up the performance counter APIs. |
| */ |
| |
| void ClosePerfCntrs(PerfObj *PerfCntrs) |
| { |
| GlobalFree(PerfCntrs); |
| |
| NtQuerySystemInformation = NULL; |
| } /* ClosePerfCntrs */ |
| |
| void |
| cpu_start_internal(void) |
| { |
| RestartPerfCntrs(PerfCntrs);
|
| } |
| |
| void |
| cpu_stop_internal(void) |
| { |
| RestartPerfCntrs(PerfCntrs);
|
| } |
| |
| float |
| calc_cpu_util_internal(float elapsed_time) |
| { |
| float correction_factor; |
| lib_local_cpu_util = (float)0.0; |
| /* It is possible that the library measured a time other than */ |
| /* the one that the user want for the cpu utilization */ |
| /* calculations - for example, tests that were ended by */ |
| /* watchdog timers such as the udp stream test. We let these */ |
| /* tests tell up what the elapsed time should be. */ |
| |
| if (elapsed_time != 0.0) { |
| correction_factor = (float) 1.0 + |
| ((lib_elapsed - elapsed_time) / elapsed_time); |
| } |
| else { |
| correction_factor = (float) 1.0; |
| } |
| |
| if (debug) { |
| fprintf(where, "correction factor: %f\n", correction_factor); |
| } |
| |
| lib_local_cpu_util = (float)ReportPerfCntrs(PerfCntrs); |
| lib_local_cpu_util *= correction_factor; |
| return lib_local_cpu_util; |
| |
| } |