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