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 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 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 104 cpu_util_terminate(void) 105 { 106 return; 107 } 108 109 int 110 get_cpu_method(void) 111 { 112 return NT_METHOD; 113 } 114 115 typedef unsigned __int64 uint64_t; 116 117 void 118 get_cpu_idle(uint64_t *res) 119 { 120 RestartPerfCntrs(PerfCntrs); 121 return; 122 } 123 124 float 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 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 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 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 431 void ClosePerfCntrs(PerfObj *PerfCntrs) 432 { 433 GlobalFree(PerfCntrs); 434 435 NtQuerySystemInformation = NULL; 436 } /* ClosePerfCntrs */ 437 438 void 439 cpu_start_internal(void) 440 { 441 RestartPerfCntrs(PerfCntrs); 442 } 443 444 void 445 cpu_stop_internal(void) 446 { 447 RestartPerfCntrs(PerfCntrs); 448 } 449 450 float 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