1char   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
57typedef 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//
70typedef ULONG (__stdcall *NT_QUERY_SYSTEM_INFORMATION)(
71                                                                                        ULONG SystemInformationClass,
72                                                                                        PVOID SystemInformation,
73                                                                                        ULONG SystemInformationLength,
74                                                                                        PULONG ReturnLength
75                                                                                        );
76
77NT_QUERY_SYSTEM_INFORMATION NtQuerySystemInformation = NULL;
78
79
80static 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
95typedef 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
103static PerfObj *PerfCntrs;
104
105// Forward declarations
106
107PerfObj *InitPerfCntrs();
108void RestartPerfCntrs(PerfObj *PerfCntrs);
109double ReportPerfCntrs(PerfObj *PerfCntrs);  /* returns CPU utilization */
110void ClosePerfCntrs(PerfObj *PerfCntrs);
111
112
113void
114cpu_util_init(void)
115{
116  if (NtQuerySystemInformation == NULL) {
117    // Open the performance counter interface
118    PerfCntrs = InitPerfCntrs();
119  }
120  return;
121}
122
123void
124cpu_util_terminate(void)
125{
126  return;
127}
128
129int
130get_cpu_method(void)
131{
132  return NT_METHOD;
133}
134
135typedef unsigned __int64    uint64_t;
136
137void
138get_cpu_idle(uint64_t *res)
139{
140  RestartPerfCntrs(PerfCntrs);
141  return;
142}
143
144float
145calibrate_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
161PerfObj *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
220void 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
296double 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
451void ClosePerfCntrs(PerfObj *PerfCntrs)
452{
453        GlobalFree(PerfCntrs);
454
455        NtQuerySystemInformation = NULL;
456}   /* ClosePerfCntrs */
457
458void
459cpu_start_internal(void)
460{
461  RestartPerfCntrs(PerfCntrs);
462}
463
464void
465cpu_stop_internal(void)
466{
467  RestartPerfCntrs(PerfCntrs);
468}
469
470float
471calc_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