| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // +build windows
- package cpu
- import (
- "context"
- "fmt"
- "strings"
- "unsafe"
- "github.com/yusufpapurcu/wmi"
- "github.com/shirou/gopsutil/internal/common"
- "golang.org/x/sys/windows"
- )
- var (
- procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount")
- procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
- )
- type Win32_Processor struct {
- Win32_ProcessorWithoutLoadPct
- LoadPercentage *uint16
- }
- // LoadPercentage takes a linearly more time as the number of sockets increases.
- // For vSphere by default corespersocket = 1, meaning for a 40 vCPU VM Get Processor Info
- // could take more than half a minute.
- type Win32_ProcessorWithoutLoadPct struct {
- Family uint16
- Manufacturer string
- Name string
- NumberOfLogicalProcessors uint32
- NumberOfCores uint32
- ProcessorID *string
- Stepping *string
- MaxClockSpeed uint32
- }
- // SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
- // defined in windows api doc with the following
- // https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information
- // additional fields documented here
- // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm
- type win32_SystemProcessorPerformanceInformation struct {
- IdleTime int64 // idle time in 100ns (this is not a filetime).
- KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime).
- UserTime int64 // usertime in 100ns (this is not a filetime).
- DpcTime int64 // dpc time in 100ns (this is not a filetime).
- InterruptTime int64 // interrupt time in 100ns
- InterruptCount uint32
- }
- // Win32_PerfFormattedData_PerfOS_System struct to have count of processes and processor queue length
- type Win32_PerfFormattedData_PerfOS_System struct {
- Processes uint32
- ProcessorQueueLength uint32
- }
- const (
- ClocksPerSec = 10000000.0
- // systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation
- // https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
- win32_SystemProcessorPerformanceInformationClass = 8
- // size of systemProcessorPerformanceInfoSize in memory
- win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{}))
- )
- // Times returns times stat per cpu and combined for all CPUs
- func Times(percpu bool) ([]TimesStat, error) {
- return TimesWithContext(context.Background(), percpu)
- }
- func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
- if percpu {
- return perCPUTimes()
- }
- var ret []TimesStat
- var lpIdleTime common.FILETIME
- var lpKernelTime common.FILETIME
- var lpUserTime common.FILETIME
- r, _, _ := common.ProcGetSystemTimes.Call(
- uintptr(unsafe.Pointer(&lpIdleTime)),
- uintptr(unsafe.Pointer(&lpKernelTime)),
- uintptr(unsafe.Pointer(&lpUserTime)))
- if r == 0 {
- return ret, windows.GetLastError()
- }
- LOT := float64(0.0000001)
- HIT := (LOT * 4294967296.0)
- idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime)))
- user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime)))
- kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime)))
- system := (kernel - idle)
- ret = append(ret, TimesStat{
- CPU: "cpu-total",
- Idle: float64(idle),
- User: float64(user),
- System: float64(system),
- })
- return ret, nil
- }
- func Info() ([]InfoStat, error) {
- return InfoWithContext(context.Background())
- }
- func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
- var ret []InfoStat
- var dst []Win32_ProcessorWithoutLoadPct
- q := wmi.CreateQuery(&dst, "")
- q = strings.ReplaceAll(q, "Win32_ProcessorWithoutLoadPct", "Win32_Processor")
- if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
- return ret, err
- }
- var procID string
- for i, l := range dst {
- procID = ""
- if l.ProcessorID != nil {
- procID = *l.ProcessorID
- }
- cpu := InfoStat{
- CPU: int32(i),
- Family: fmt.Sprintf("%d", l.Family),
- VendorID: l.Manufacturer,
- ModelName: l.Name,
- Cores: int32(l.NumberOfLogicalProcessors),
- PhysicalID: procID,
- Mhz: float64(l.MaxClockSpeed),
- Flags: []string{},
- }
- ret = append(ret, cpu)
- }
- return ret, nil
- }
- // ProcInfo returns processes count and processor queue length in the system.
- // There is a single queue for processor even on multiprocessors systems.
- func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) {
- return ProcInfoWithContext(context.Background())
- }
- func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_System, error) {
- var ret []Win32_PerfFormattedData_PerfOS_System
- q := wmi.CreateQuery(&ret, "")
- err := common.WMIQueryWithContext(ctx, q, &ret)
- if err != nil {
- return []Win32_PerfFormattedData_PerfOS_System{}, err
- }
- return ret, err
- }
- // perCPUTimes returns times stat per cpu, per core and overall for all CPUs
- func perCPUTimes() ([]TimesStat, error) {
- var ret []TimesStat
- stats, err := perfInfo()
- if err != nil {
- return nil, err
- }
- for core, v := range stats {
- c := TimesStat{
- CPU: fmt.Sprintf("cpu%d", core),
- User: float64(v.UserTime) / ClocksPerSec,
- System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec,
- Idle: float64(v.IdleTime) / ClocksPerSec,
- Irq: float64(v.InterruptTime) / ClocksPerSec,
- }
- ret = append(ret, c)
- }
- return ret, nil
- }
- // makes call to Windows API function to retrieve performance information for each core
- func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) {
- // Make maxResults large for safety.
- // We can't invoke the api call with a results array that's too small.
- // If we have more than 2056 cores on a single host, then it's probably the future.
- maxBuffer := 2056
- // buffer for results from the windows proc
- resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer)
- // size of the buffer in memory
- bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer)
- // size of the returned response
- var retSize uint32
- // Invoke windows api proc.
- // The returned err from the windows dll proc will always be non-nil even when successful.
- // See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information
- retCode, _, err := common.ProcNtQuerySystemInformation.Call(
- win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation
- uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer
- bufferSize, // size of the buffer in memory
- uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this
- )
- // check return code for errors
- if retCode != 0 {
- return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error())
- }
- // calculate the number of returned elements based on the returned size
- numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize
- // trim results to the number of returned elements
- resultBuffer = resultBuffer[:numReturnedElements]
- return resultBuffer, nil
- }
- // SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API.
- // https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396
- // https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43
- type systemInfo struct {
- wProcessorArchitecture uint16
- wReserved uint16
- dwPageSize uint32
- lpMinimumApplicationAddress uintptr
- lpMaximumApplicationAddress uintptr
- dwActiveProcessorMask uintptr
- dwNumberOfProcessors uint32
- dwProcessorType uint32
- dwAllocationGranularity uint32
- wProcessorLevel uint16
- wProcessorRevision uint16
- }
- func CountsWithContext(ctx context.Context, logical bool) (int, error) {
- if logical {
- // https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
- err := procGetActiveProcessorCount.Find()
- if err == nil { // Win7+
- 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
- if ret != 0 {
- return int(ret), nil
- }
- }
- var systemInfo systemInfo
- _, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
- if systemInfo.dwNumberOfProcessors == 0 {
- return 0, err
- }
- return int(systemInfo.dwNumberOfProcessors), nil
- }
- // physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499
- // for the time being, try with unreliable and slow WMI call…
- var dst []Win32_ProcessorWithoutLoadPct
- q := wmi.CreateQuery(&dst, "")
- q = strings.ReplaceAll(q, "Win32_ProcessorWithoutLoadPct", "Win32_Processor")
- if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
- return 0, err
- }
- var count uint32
- for _, d := range dst {
- count += d.NumberOfCores
- }
- return int(count), nil
- }
|