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