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