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 
ReadPerformanceCounter(VOID)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
cpu_util_init(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
cpu_util_terminate(void)124 cpu_util_terminate(void)
125 {
126   return;
127 }
128 
129 int
get_cpu_method(void)130 get_cpu_method(void)
131 {
132   return NT_METHOD;
133 }
134 
135 typedef unsigned __int64    uint64_t;
136 
137 void
get_cpu_idle(uint64_t * res)138 get_cpu_idle(uint64_t *res)
139 {
140   RestartPerfCntrs(PerfCntrs);
141   return;
142 }
143 
144 float
calibrate_idle_rate(int iterations,int interval)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 
InitPerfCntrs()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 
RestartPerfCntrs(PerfObj * PerfCntrs)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 
ReportPerfCntrs(PerfObj * PerfCntrs)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 
ClosePerfCntrs(PerfObj * PerfCntrs)451 void ClosePerfCntrs(PerfObj *PerfCntrs)
452 {
453         GlobalFree(PerfCntrs);
454 
455         NtQuerySystemInformation = NULL;
456 }   /* ClosePerfCntrs */
457 
458 void
cpu_start_internal(void)459 cpu_start_internal(void)
460 {
461   RestartPerfCntrs(PerfCntrs);
462 }
463 
464 void
cpu_stop_internal(void)465 cpu_stop_internal(void)
466 {
467   RestartPerfCntrs(PerfCntrs);
468 }
469 
470 float
calc_cpu_util_internal(float elapsed_time)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