diff options
Diffstat (limited to 'src/netcpu_ntperf.c')
-rw-r--r-- | src/netcpu_ntperf.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/src/netcpu_ntperf.c b/src/netcpu_ntperf.c new file mode 100644 index 0000000..88637fb --- /dev/null +++ b/src/netcpu_ntperf.c @@ -0,0 +1,479 @@ +char netcpu_ntperf_id[]="\ +@(#)netcpu_ntperf.c (c) Copyright 2005-2012, Hewlett-Packard Company, Version 2.6.0"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.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: %lX\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: %lX\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 %lu\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 %lu\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; + double duration; // in milliseconds
+ + 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 100 usec (1/10th milliseconds) timebase.
+ ActualDuration.QuadPart = (ActualDuration.QuadPart*10000)/TickHz.QuadPart; + duration = (double)ActualDuration.QuadPart/10.0; // duration in ms
+ + if (verbosity > 1) + { + fprintf(where,"ActualDuration (ms): %d\n", (int)duration);
+ } + + 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*1000.0/duration);
+ + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.1f", + (double)DeltaInfo[i].InterruptCount*1000.0/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; + + memset(&lib_local_cpu_stats, 0, sizeof(lib_local_cpu_stats)); + + /* 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_stats.cpu_util = (float)ReportPerfCntrs(PerfCntrs); + lib_local_cpu_stats.cpu_util *= correction_factor; + return lib_local_cpu_stats.cpu_util; + +} |