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