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 
ReadPerformanceCounter(VOID)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
cpu_util_init(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
cpu_util_terminate(void)104 cpu_util_terminate(void)
105 {
106   return;
107 }
108 
109 int
get_cpu_method(void)110 get_cpu_method(void)
111 {
112   return NT_METHOD;
113 }
114 
115 typedef unsigned __int64    uint64_t;
116 
117 void
get_cpu_idle(uint64_t * res)118 get_cpu_idle(uint64_t *res)
119 {
120   RestartPerfCntrs(PerfCntrs);
121   return;
122 }
123 
124 float
calibrate_idle_rate(int iterations,int interval)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 
InitPerfCntrs()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 
RestartPerfCntrs(PerfObj * PerfCntrs)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 
ReportPerfCntrs(PerfObj * PerfCntrs)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 
ClosePerfCntrs(PerfObj * PerfCntrs)431 void ClosePerfCntrs(PerfObj *PerfCntrs)
432 {
433         GlobalFree(PerfCntrs);
434 
435         NtQuerySystemInformation = NULL;
436 }   /* ClosePerfCntrs */
437 
438 void
cpu_start_internal(void)439 cpu_start_internal(void)
440 {
441   RestartPerfCntrs(PerfCntrs);
442 }
443 
444 void
cpu_stop_internal(void)445 cpu_stop_internal(void)
446 {
447   RestartPerfCntrs(PerfCntrs);
448 }
449 
450 float
calc_cpu_util_internal(float elapsed_time)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