...

Source file src/github.com/shirou/gopsutil/cpu/cpu_windows.go

Documentation: github.com/shirou/gopsutil/cpu

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

View as plain text