1char   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
37typedef 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//
50typedef ULONG (__stdcall *NT_QUERY_SYSTEM_INFORMATION)(
51                                                                                        ULONG SystemInformationClass,
52                                                                                        PVOID SystemInformation,
53                                                                                        ULONG SystemInformationLength,
54                                                                                        PULONG ReturnLength
55                                                                                        );
56
57NT_QUERY_SYSTEM_INFORMATION NtQuerySystemInformation = NULL;
58
59
60static 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
75typedef 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
83static PerfObj *PerfCntrs;
84
85// Forward declarations
86
87PerfObj *InitPerfCntrs();
88void RestartPerfCntrs(PerfObj *PerfCntrs);
89double ReportPerfCntrs(PerfObj *PerfCntrs);  /* returns CPU utilization */
90void ClosePerfCntrs(PerfObj *PerfCntrs);
91
92
93void
94cpu_util_init(void)
95{
96  if (NtQuerySystemInformation == NULL) {
97    // Open the performance counter interface
98    PerfCntrs = InitPerfCntrs();
99  }
100  return;
101}
102
103void
104cpu_util_terminate(void)
105{
106  return;
107}
108
109int
110get_cpu_method(void)
111{
112  return NT_METHOD;
113}
114
115typedef unsigned __int64    uint64_t;
116
117void
118get_cpu_idle(uint64_t *res)
119{
120  RestartPerfCntrs(PerfCntrs);
121  return;
122}
123
124float
125calibrate_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
141PerfObj *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
200void 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
276double 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
431void ClosePerfCntrs(PerfObj *PerfCntrs)
432{
433        GlobalFree(PerfCntrs);
434
435        NtQuerySystemInformation = NULL;
436}   /* ClosePerfCntrs */
437
438void
439cpu_start_internal(void)
440{
441  RestartPerfCntrs(PerfCntrs);
442}
443
444void
445cpu_stop_internal(void)
446{
447  RestartPerfCntrs(PerfCntrs);
448}
449
450float
451calc_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