Home | History | Annotate | Download | only in netperf
      1 char   netcpu_ntperf_id[]="\
      2 @(#)netcpu_ntperf.c (c) Copyright 2005-2007, Hewlett-Packard Company, Version 2.4.3";
      3 
      4 #if HAVE_CONFIG_H
      5 # include <config.h>
      6 #endif
      7 
      8 #include <stdio.h>
      9 
     10 #if HAVE_INTTYPES_H
     11 # include <inttypes.h>
     12 #else
     13 # if HAVE_STDINT_H
     14 #  include <stdint.h>
     15 # endif
     16 #endif
     17 
     18 #if 0
     19 #include <limits.h>
     20 #include <sys/types.h>
     21 #include <fcntl.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <math.h>
     25 #include <string.h>
     26 #endif
     27 
     28 #include <assert.h>
     29 
     30 #include <process.h>
     31 #include <time.h>
     32 
     33 #include <windows.h>
     34 #include <assert.h>
     35 
     36 #include <winsock2.h>
     37 // If you are trying to compile on Windows 2000 or NT 4.0 you may
     38 // need to define DONT_IPV6 in the "sources" files.
     39 #ifndef DONT_IPV6
     40 #include <ws2tcpip.h>
     41 #endif
     42 
     43 #include "netsh.h"
     44 #include "netlib.h"
     45 
     46 //
     47 // System CPU time information class.
     48 // Used to get CPU time information.
     49 //
     50 //     SDK\inc\ntexapi.h
     51 // Function x8:   SystemProcessorPerformanceInformation
     52 // DataStructure: SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
     53 //
     54 
     55 #define SystemProcessorPerformanceInformation 0x08
     56 
     57 typedef struct
     58 {
     59         LARGE_INTEGER   IdleTime;
     60         LARGE_INTEGER   KernelTime;
     61         LARGE_INTEGER   UserTime;
     62         LARGE_INTEGER   DpcTime;
     63         LARGE_INTEGER   InterruptTime;
     64         LONG                    InterruptCount;
     65 } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
     66 
     67 //
     68 // Calls to get the information
     69 //
     70 typedef ULONG (__stdcall *NT_QUERY_SYSTEM_INFORMATION)(
     71                                                                                         ULONG SystemInformationClass,
     72                                                                                         PVOID SystemInformation,
     73                                                                                         ULONG SystemInformationLength,
     74                                                                                         PULONG ReturnLength
     75                                                                                         );
     76 
     77 NT_QUERY_SYSTEM_INFORMATION NtQuerySystemInformation = NULL;
     78 
     79 
     80 static LARGE_INTEGER TickHz = {0,0};
     81 
     82 _inline LARGE_INTEGER ReadPerformanceCounter(VOID)
     83 {
     84         LARGE_INTEGER Counter;
     85         QueryPerformanceCounter(&Counter);
     86 
     87         return(Counter);
     88 }       // ReadperformanceCounter
     89 
     90 
     91 /* The NT performance data is accessed through the NtQuerySystemInformation
     92    call.  References to the PDH.DLL have been deleted.  This structure
     93    is the root for these data structures. */
     94 
     95 typedef struct sPerfObj
     96 {
     97         LARGE_INTEGER   StartTime;
     98         LARGE_INTEGER   EndTime;
     99         SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StartInfo[MAXCPUS +1];
    100         SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION EndInfo[MAXCPUS +1];
    101 } PerfObj, *PPerfObj;
    102 
    103 static PerfObj *PerfCntrs;
    104 
    105 // Forward declarations
    106 
    107 PerfObj *InitPerfCntrs();
    108 void RestartPerfCntrs(PerfObj *PerfCntrs);
    109 double ReportPerfCntrs(PerfObj *PerfCntrs);  /* returns CPU utilization */
    110 void ClosePerfCntrs(PerfObj *PerfCntrs);
    111 
    112 
    113 void
    114 cpu_util_init(void)
    115 {
    116   if (NtQuerySystemInformation == NULL) {
    117     // Open the performance counter interface
    118     PerfCntrs = InitPerfCntrs();
    119   }
    120   return;
    121 }
    122 
    123 void
    124 cpu_util_terminate(void)
    125 {
    126   return;
    127 }
    128 
    129 int
    130 get_cpu_method(void)
    131 {
    132   return NT_METHOD;
    133 }
    134 
    135 typedef unsigned __int64    uint64_t;
    136 
    137 void
    138 get_cpu_idle(uint64_t *res)
    139 {
    140   RestartPerfCntrs(PerfCntrs);
    141   return;
    142 }
    143 
    144 float
    145 calibrate_idle_rate(int iterations, int interval)
    146 {
    147   return (float)0.0;
    148 }
    149 
    150 
    151 /*
    152   InitPerfCntrs() -
    153 
    154   Changed to no longer access the NT performance registry interfaces.
    155   A direct call to NtQuerySystemInformation (an undocumented NT API)
    156   is made instead.  Parameters determined by decompilation of ntkrnlmp
    157   and ntdll.
    158 */
    159 
    160 
    161 PerfObj *InitPerfCntrs()
    162 {
    163   PerfObj *NewPerfCntrs;
    164   DWORD NTVersion;
    165   DWORD status;
    166   SYSTEM_INFO SystemInfo;
    167 
    168   GetSystemInfo(&SystemInfo);
    169 
    170   NewPerfCntrs = (PerfObj *)GlobalAlloc(GPTR, sizeof(PerfObj));
    171   assert(NewPerfCntrs != NULL);
    172 
    173   ZeroMemory((PCHAR)NewPerfCntrs, sizeof(PerfObj));
    174 
    175   // get NT version
    176   NTVersion = GetVersion();
    177   if (NTVersion >= 0x80000000)
    178     {
    179       fprintf(stderr, "Not running on Windows NT\n");
    180       exit(1);
    181     }
    182 
    183   // locate the calls we need in NTDLL
    184   //Lint
    185   NtQuerySystemInformation =
    186     (NT_QUERY_SYSTEM_INFORMATION)GetProcAddress( GetModuleHandle("ntdll.dll"),
    187 						 "NtQuerySystemInformation" );
    188 
    189   if ( !(NtQuerySystemInformation) )
    190     {
    191       //Lint
    192       status = GetLastError();
    193       fprintf(stderr, "GetProcAddressFailed, status: %X\n", status);
    194       exit(1);
    195     }
    196 
    197   // setup to measure timestamps with the high resolution timers.
    198   if (QueryPerformanceFrequency(&TickHz) == FALSE)
    199     {
    200       fprintf(stderr,"MAIN - QueryPerformanceFrequency Failed!\n");
    201       exit(2);
    202     }
    203 
    204   RestartPerfCntrs(NewPerfCntrs);
    205 
    206   return(NewPerfCntrs);
    207 }  /* InitPerfCntrs */
    208 
    209 /*
    210   RestartPerfCntrs() -
    211 
    212   The Performance counters must be read twice to produce rate and
    213   percentage results.  This routine is called before the start of a
    214   benchmark to establish the initial counters.  It must be called a
    215   second time after the benchmark completes to collect the final state
    216   of the performance counters.  ReportPerfCntrs is called to print the
    217   results after the benchmark completes.
    218 */
    219 
    220 void RestartPerfCntrs(PerfObj *PerfCntrs)
    221 {
    222   DWORD returnLength = 0;  //Lint
    223   DWORD returnNumCPUs;  //Lint
    224   DWORD i;
    225 
    226   DWORD status;
    227   SYSTEM_INFO SystemInfo;
    228 
    229   GetSystemInfo(&SystemInfo);
    230 
    231   // Move previous data from EndInfo to StartInfo.
    232   CopyMemory((PCHAR)&PerfCntrs->StartInfo[0],
    233 	     (PCHAR)&PerfCntrs->EndInfo[0],
    234 	     sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*(MAXCPUS +1));
    235 
    236   PerfCntrs->StartTime = PerfCntrs->EndTime;
    237 
    238   // get the current CPUTIME information
    239   if ( (status = NtQuerySystemInformation( SystemProcessorPerformanceInformation,
    240 					   (PCHAR)&PerfCntrs->EndInfo[0], sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*MAXCPUS,
    241 					   &returnLength )) != 0)
    242     {
    243       fprintf(stderr, "NtQuery failed, status: %X\n", status);
    244       exit(1);
    245     }
    246 
    247   PerfCntrs->EndTime = ReadPerformanceCounter();
    248 
    249   // Validate that NtQuery returned a reasonable amount of data
    250   if ((returnLength % sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) != 0)
    251     {
    252       fprintf(stderr, "NtQuery didn't return expected amount of data\n");
    253       fprintf(stderr, "Expected a multiple of %i, returned %i\n",
    254 	      sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), returnLength);
    255       exit(1);
    256     }
    257   returnNumCPUs = returnLength / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
    258 
    259   if (returnNumCPUs != (int)SystemInfo.dwNumberOfProcessors)
    260     {
    261       fprintf(stderr, "NtQuery didn't return expected amount of data\n");
    262       fprintf(stderr, "Expected data for %i CPUs, returned %i\n",
    263 	      (int)SystemInfo.dwNumberOfProcessors, returnNumCPUs);
    264       exit(1);
    265     }
    266 
    267   // Zero entries not returned by NtQuery
    268   ZeroMemory((PCHAR)&PerfCntrs->EndInfo[returnNumCPUs],
    269 	     sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*
    270 	     (MAXCPUS +1 - returnNumCPUs));
    271 
    272   // Total all of the CPUs
    273   //      KernelTime needs to be fixed-up; it includes both idle &
    274   // true kernel time
    275   //  Note that kernel time also includes DpcTime & InterruptTime, but
    276   // I like this.
    277   for (i=0; i < returnNumCPUs; i++)
    278     {
    279       PerfCntrs->EndInfo[i].KernelTime.QuadPart         -= PerfCntrs->EndInfo[i].IdleTime.QuadPart;
    280       PerfCntrs->EndInfo[MAXCPUS].IdleTime.QuadPart     += PerfCntrs->EndInfo[i].IdleTime.QuadPart;
    281       PerfCntrs->EndInfo[MAXCPUS].KernelTime.QuadPart   += PerfCntrs->EndInfo[i].KernelTime.QuadPart;
    282       PerfCntrs->EndInfo[MAXCPUS].UserTime.QuadPart     += PerfCntrs->EndInfo[i].UserTime.QuadPart;
    283       PerfCntrs->EndInfo[MAXCPUS].DpcTime.QuadPart      += PerfCntrs->EndInfo[i].DpcTime.QuadPart;
    284       PerfCntrs->EndInfo[MAXCPUS].InterruptTime.QuadPart += PerfCntrs->EndInfo[i].InterruptTime.QuadPart;
    285       PerfCntrs->EndInfo[MAXCPUS].InterruptCount                += PerfCntrs->EndInfo[i].InterruptCount;
    286     }
    287 
    288 }   /* RestartPerfCntrs */
    289 
    290 /*
    291   ReportPerfCntrs() -
    292   This routine reports the results of the various performance
    293   counters.
    294 */
    295 
    296 double ReportPerfCntrs(PerfObj *PerfCntrs)
    297 {
    298   double tot_CPU_Util;
    299   int i;
    300   int duration;  // in 100 usecs
    301 
    302   LARGE_INTEGER ActualDuration;
    303 
    304   SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION        DeltaInfo[MAXCPUS +1];
    305 
    306   LARGE_INTEGER   TotalCPUTime[MAXCPUS +1];
    307 
    308   SYSTEM_INFO SystemInfo;
    309 
    310   GetSystemInfo(&SystemInfo);
    311 
    312   for (i=0; i <= MAXCPUS; i++)
    313     {
    314       DeltaInfo[i].IdleTime.QuadPart    = PerfCntrs->EndInfo[i].IdleTime.QuadPart -
    315 	PerfCntrs->StartInfo[i].IdleTime.QuadPart;
    316       DeltaInfo[i].KernelTime.QuadPart          = PerfCntrs->EndInfo[i].KernelTime.QuadPart -
    317 	PerfCntrs->StartInfo[i].KernelTime.QuadPart;
    318       DeltaInfo[i].UserTime.QuadPart    = PerfCntrs->EndInfo[i].UserTime.QuadPart -
    319 	PerfCntrs->StartInfo[i].UserTime.QuadPart;
    320       DeltaInfo[i].DpcTime.QuadPart     = PerfCntrs->EndInfo[i].DpcTime.QuadPart -
    321 	PerfCntrs->StartInfo[i].DpcTime.QuadPart;
    322       DeltaInfo[i].InterruptTime.QuadPart = PerfCntrs->EndInfo[i].InterruptTime.QuadPart -
    323 	PerfCntrs->StartInfo[i].InterruptTime.QuadPart;
    324       DeltaInfo[i].InterruptCount               = PerfCntrs->EndInfo[i].InterruptCount -
    325 	PerfCntrs->StartInfo[i].InterruptCount;
    326 
    327       TotalCPUTime[i].QuadPart =
    328 	DeltaInfo[i].IdleTime.QuadPart +
    329 	DeltaInfo[i].KernelTime.QuadPart +
    330 	DeltaInfo[i].UserTime.QuadPart;
    331       // KernelTime already includes DpcTime & InterruptTime!
    332       // + DeltaInfo[i].DpcTime.QuadPart  +
    333       //  DeltaInfo[i].InterruptTime.QuadPart;
    334     }
    335 
    336   tot_CPU_Util = 100.0*(1.0 - (double)DeltaInfo[MAXCPUS].IdleTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart);  //Lint
    337 
    338   // Re-calculate duration, since we may have stoped early due to cntr-C.
    339   ActualDuration.QuadPart = PerfCntrs->EndTime.QuadPart -
    340     PerfCntrs->StartTime.QuadPart;
    341 
    342   // convert to 1/10 milliseconds (100 usec)
    343   ActualDuration.QuadPart = (ActualDuration.QuadPart*10000)/TickHz.QuadPart;
    344   duration = ActualDuration.LowPart;
    345 
    346   if (verbosity > 1)
    347     {
    348       fprintf(where,"ActualDuration (ms): %d\n", duration/10);
    349     }
    350 
    351   if (verbosity > 1)
    352     {
    353       fprintf(where, "%% CPU    _Total");
    354       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    355 	{
    356 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    357 	    {
    358 	      fprintf(where, "\t CPU %i", i);
    359 	    }
    360 	}
    361       fprintf(where, "\n");
    362 
    363       fprintf(where, "Busy      %5.2f", tot_CPU_Util);
    364       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    365 	{
    366 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    367 	    {
    368 	      fprintf(where, "\t %5.2f",
    369 		      100.0*(1.0 - (double)DeltaInfo[i].IdleTime.QuadPart/(double)TotalCPUTime[i].QuadPart));  //Lint
    370 	    }
    371 	}
    372       fprintf(where, "\n");
    373 
    374       fprintf(where, "Kernel    %5.2f",
    375 	      100.0*(double)DeltaInfo[MAXCPUS].KernelTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart);  //Lint
    376 
    377       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    378 	{
    379 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    380 	    {
    381 	      fprintf(where, "\t %5.2f",
    382 		      100.0*(double)DeltaInfo[i].KernelTime.QuadPart/(double)TotalCPUTime[i].QuadPart);  //Lint
    383 	    }
    384 	}
    385       fprintf(where, "\n");
    386 
    387       fprintf(where, "User      %5.2f",
    388 	      100.0*(double)DeltaInfo[MAXCPUS].UserTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart);
    389 
    390       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    391 	{
    392 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    393 	    {
    394 	      fprintf(where, "\t %5.2f",
    395 		      100.0*(double)DeltaInfo[i].UserTime.QuadPart/TotalCPUTime[i].QuadPart);  //Lint
    396 	    }
    397 	}
    398       fprintf(where, "\n");
    399 
    400       fprintf(where, "Dpc       %5.2f",
    401 	      100.0*(double)DeltaInfo[MAXCPUS].DpcTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart);  //Lint
    402 
    403       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    404 	{
    405 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    406 	    {
    407 	      fprintf(where, "\t %5.2f",
    408 		      100.0*(double)DeltaInfo[i].DpcTime.QuadPart/(double)TotalCPUTime[i].QuadPart);  //Lint
    409 	    }
    410 	}
    411       fprintf(where, "\n");
    412 
    413       fprintf(where, "Interrupt %5.2f",
    414 	      100.0*(double)DeltaInfo[MAXCPUS].InterruptTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart);  //Lint
    415 
    416       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    417 	{
    418 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    419 	    {
    420 	      fprintf(where, "\t %5.2f",
    421 		      100.0*(double)DeltaInfo[i].InterruptTime.QuadPart/TotalCPUTime[i].QuadPart);  //Lint
    422 	    }
    423 	}
    424       fprintf(where, "\n\n");
    425 
    426       fprintf(where, "Interrupt/Sec. %5.1f",
    427 	      (double)DeltaInfo[MAXCPUS].InterruptCount*10000.0/(double)duration);
    428 
    429       if ((int)SystemInfo.dwNumberOfProcessors > 1)
    430 	{
    431 	  for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++)
    432 	    {
    433 	      fprintf(where, "\t %5.1f",
    434 		      (double)DeltaInfo[i].InterruptCount*10000.0/(double)duration);
    435 	    }
    436 	}
    437       fprintf(where, "\n\n");
    438       fflush(where);
    439     }
    440 
    441   return (tot_CPU_Util);
    442 
    443 }  /* ReportPerfCntrs */
    444 
    445 /*
    446   ClosePerfCntrs() -
    447 
    448   This routine cleans up the performance counter APIs.
    449 */
    450 
    451 void ClosePerfCntrs(PerfObj *PerfCntrs)
    452 {
    453         GlobalFree(PerfCntrs);
    454 
    455         NtQuerySystemInformation = NULL;
    456 }   /* ClosePerfCntrs */
    457 
    458 void
    459 cpu_start_internal(void)
    460 {
    461   RestartPerfCntrs(PerfCntrs);
    462 }
    463 
    464 void
    465 cpu_stop_internal(void)
    466 {
    467   RestartPerfCntrs(PerfCntrs);
    468 }
    469 
    470 float
    471 calc_cpu_util_internal(float elapsed_time)
    472 {
    473   float correction_factor;
    474   lib_local_cpu_util = (float)0.0;
    475   /* It is possible that the library measured a time other than */
    476   /* the one that the user want for the cpu utilization */
    477   /* calculations - for example, tests that were ended by */
    478   /* watchdog timers such as the udp stream test. We let these */
    479   /* tests tell up what the elapsed time should be. */
    480 
    481   if (elapsed_time != 0.0) {
    482     correction_factor = (float) 1.0 +
    483       ((lib_elapsed - elapsed_time) / elapsed_time);
    484   }
    485   else {
    486     correction_factor = (float) 1.0;
    487   }
    488 
    489   if (debug) {
    490     fprintf(where, "correction factor: %f\n", correction_factor);
    491   }
    492 
    493   lib_local_cpu_util = (float)ReportPerfCntrs(PerfCntrs);
    494   lib_local_cpu_util *= correction_factor;
    495   return lib_local_cpu_util;
    496 
    497 }
    498