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 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 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 124 cpu_util_terminate(void) 125 { 126 return; 127 } 128 129 int 130 get_cpu_method(void) 131 { 132 return NT_METHOD; 133 } 134 135 typedef unsigned __int64 uint64_t; 136 137 void 138 get_cpu_idle(uint64_t *res) 139 { 140 RestartPerfCntrs(PerfCntrs); 141 return; 142 } 143 144 float 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 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 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 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 451 void ClosePerfCntrs(PerfObj *PerfCntrs) 452 { 453 GlobalFree(PerfCntrs); 454 455 NtQuerySystemInformation = NULL; 456 } /* ClosePerfCntrs */ 457 458 void 459 cpu_start_internal(void) 460 { 461 RestartPerfCntrs(PerfCntrs); 462 } 463 464 void 465 cpu_stop_internal(void) 466 { 467 RestartPerfCntrs(PerfCntrs); 468 } 469 470 float 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