github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/cpu/cpu_windows.go (about) 1 //go:build windows 2 // +build windows 3 4 package cpu 5 6 import ( 7 "context" 8 "fmt" 9 "unsafe" 10 11 "github.com/gofiber/fiber/v2/internal/gopsutil/common" 12 "github.com/gofiber/fiber/v2/internal/wmi" 13 "golang.org/x/sys/windows" 14 ) 15 16 var ( 17 procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount") 18 procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") 19 ) 20 21 type Win32_Processor struct { 22 LoadPercentage *uint16 23 Family uint16 24 Manufacturer string 25 Name string 26 NumberOfLogicalProcessors uint32 27 NumberOfCores uint32 28 ProcessorID *string 29 Stepping *string 30 MaxClockSpeed uint32 31 } 32 33 // SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION 34 // defined in windows api doc with the following 35 // https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information 36 // additional fields documented here 37 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm 38 type win32_SystemProcessorPerformanceInformation struct { 39 IdleTime int64 // idle time in 100ns (this is not a filetime). 40 KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime). 41 UserTime int64 // usertime in 100ns (this is not a filetime). 42 DpcTime int64 // dpc time in 100ns (this is not a filetime). 43 InterruptTime int64 // interrupt time in 100ns 44 InterruptCount uint32 45 } 46 47 // Win32_PerfFormattedData_PerfOS_System struct to have count of processes and processor queue length 48 type Win32_PerfFormattedData_PerfOS_System struct { 49 Processes uint32 50 ProcessorQueueLength uint32 51 } 52 53 const ( 54 ClocksPerSec = 10000000.0 55 56 // systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation 57 // https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0 58 win32_SystemProcessorPerformanceInformationClass = 8 59 60 // size of systemProcessorPerformanceInfoSize in memory 61 win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{})) 62 ) 63 64 // Times returns times stat per cpu and combined for all CPUs 65 func Times(percpu bool) ([]TimesStat, error) { 66 return TimesWithContext(context.Background(), percpu) 67 } 68 69 func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 70 if percpu { 71 return perCPUTimes() 72 } 73 74 var ret []TimesStat 75 var lpIdleTime common.FILETIME 76 var lpKernelTime common.FILETIME 77 var lpUserTime common.FILETIME 78 r, _, _ := common.ProcGetSystemTimes.Call( 79 uintptr(unsafe.Pointer(&lpIdleTime)), 80 uintptr(unsafe.Pointer(&lpKernelTime)), 81 uintptr(unsafe.Pointer(&lpUserTime))) 82 if r == 0 { 83 return ret, windows.GetLastError() 84 } 85 86 LOT := float64(0.0000001) 87 HIT := (LOT * 4294967296.0) 88 idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) 89 user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) 90 kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) 91 system := (kernel - idle) 92 93 ret = append(ret, TimesStat{ 94 CPU: "cpu-total", 95 Idle: float64(idle), 96 User: float64(user), 97 System: float64(system), 98 }) 99 return ret, nil 100 } 101 102 func Info() ([]InfoStat, error) { 103 return InfoWithContext(context.Background()) 104 } 105 106 func InfoWithContext(ctx context.Context) ([]InfoStat, error) { 107 var ret []InfoStat 108 var dst []Win32_Processor 109 q := wmi.CreateQuery(&dst, "") 110 if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { 111 return ret, err 112 } 113 114 var procID string 115 for i, l := range dst { 116 procID = "" 117 if l.ProcessorID != nil { 118 procID = *l.ProcessorID 119 } 120 121 cpu := InfoStat{ 122 CPU: int32(i), 123 Family: fmt.Sprintf("%d", l.Family), 124 VendorID: l.Manufacturer, 125 ModelName: l.Name, 126 Cores: int32(l.NumberOfLogicalProcessors), 127 PhysicalID: procID, 128 Mhz: float64(l.MaxClockSpeed), 129 Flags: []string{}, 130 } 131 ret = append(ret, cpu) 132 } 133 134 return ret, nil 135 } 136 137 // ProcInfo returns processes count and processor queue length in the system. 138 // There is a single queue for processor even on multiprocessors systems. 139 func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) { 140 return ProcInfoWithContext(context.Background()) 141 } 142 143 func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_System, error) { 144 var ret []Win32_PerfFormattedData_PerfOS_System 145 q := wmi.CreateQuery(&ret, "") 146 err := common.WMIQueryWithContext(ctx, q, &ret) 147 if err != nil { 148 return []Win32_PerfFormattedData_PerfOS_System{}, err 149 } 150 return ret, err 151 } 152 153 // perCPUTimes returns times stat per cpu, per core and overall for all CPUs 154 func perCPUTimes() ([]TimesStat, error) { 155 var ret []TimesStat 156 stats, err := perfInfo() 157 if err != nil { 158 return nil, err 159 } 160 for core, v := range stats { 161 c := TimesStat{ 162 CPU: fmt.Sprintf("cpu%d", core), 163 User: float64(v.UserTime) / ClocksPerSec, 164 System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec, 165 Idle: float64(v.IdleTime) / ClocksPerSec, 166 Irq: float64(v.InterruptTime) / ClocksPerSec, 167 } 168 ret = append(ret, c) 169 } 170 return ret, nil 171 } 172 173 // makes call to Windows API function to retrieve performance information for each core 174 func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) { 175 // Make maxResults large for safety. 176 // We can't invoke the api call with a results array that's too small. 177 // If we have more than 2056 cores on a single host, then it's probably the future. 178 maxBuffer := 2056 179 // buffer for results from the windows proc 180 resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer) 181 // size of the buffer in memory 182 bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer) 183 // size of the returned response 184 var retSize uint32 185 186 // Invoke windows api proc. 187 // The returned err from the windows dll proc will always be non-nil even when successful. 188 // See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information 189 retCode, _, err := common.ProcNtQuerySystemInformation.Call( 190 win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation 191 uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer 192 bufferSize, // size of the buffer in memory 193 uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this 194 ) 195 196 // check return code for errors 197 if retCode != 0 { 198 return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error()) 199 } 200 201 // calculate the number of returned elements based on the returned size 202 numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize 203 204 // trim results to the number of returned elements 205 resultBuffer = resultBuffer[:numReturnedElements] 206 207 return resultBuffer, nil 208 } 209 210 // SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API. 211 // https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396 212 // https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43 213 type systemInfo struct { 214 wProcessorArchitecture uint16 215 wReserved uint16 216 dwPageSize uint32 217 lpMinimumApplicationAddress uintptr 218 lpMaximumApplicationAddress uintptr 219 dwActiveProcessorMask uintptr 220 dwNumberOfProcessors uint32 221 dwProcessorType uint32 222 dwAllocationGranularity uint32 223 wProcessorLevel uint16 224 wProcessorRevision uint16 225 } 226 227 func CountsWithContext(ctx context.Context, logical bool) (int, error) { 228 if logical { 229 // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97 230 err := procGetActiveProcessorCount.Find() 231 if err == nil { // Win7+ 232 ret, _, _ := procGetActiveProcessorCount.Call(uintptr(0xffff)) // ALL_PROCESSOR_GROUPS is 0xffff according to Rust's winapi lib https://docs.rs/winapi/*/x86_64-pc-windows-msvc/src/winapi/shared/ntdef.rs.html#120 233 if ret != 0 { 234 return int(ret), nil 235 } 236 } 237 var systemInfo systemInfo 238 _, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) 239 if systemInfo.dwNumberOfProcessors == 0 { 240 return 0, err 241 } 242 return int(systemInfo.dwNumberOfProcessors), nil 243 } 244 // physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499 245 // for the time being, try with unreliable and slow WMI call… 246 var dst []Win32_Processor 247 q := wmi.CreateQuery(&dst, "") 248 if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { 249 return 0, err 250 } 251 var count uint32 252 for _, d := range dst { 253 count += d.NumberOfCores 254 } 255 return int(count), nil 256 }