133 lines
5.1 KiB
Go
133 lines
5.1 KiB
Go
|
// +build windows
|
||
|
|
||
|
package windows
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"io"
|
||
|
"runtime"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// On both 32-bit and 64-bit systems NtQuerySystemInformation expects the
|
||
|
// size of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION to be 48.
|
||
|
const sizeofSystemProcessorPerformanceInformation = 48
|
||
|
|
||
|
// ProcessBasicInformation is an equivalent representation of
|
||
|
// PROCESS_BASIC_INFORMATION in the Windows API.
|
||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
|
||
|
type ProcessBasicInformation struct {
|
||
|
ExitStatus uint
|
||
|
PebBaseAddress uintptr
|
||
|
AffinityMask uint
|
||
|
BasePriority uint
|
||
|
UniqueProcessID uint
|
||
|
InheritedFromUniqueProcessID uint
|
||
|
}
|
||
|
|
||
|
// NtQueryProcessBasicInformation queries basic information about the process
|
||
|
// associated with the given handle (provided by OpenProcess). It uses the
|
||
|
// NtQueryInformationProcess function to collect the data.
|
||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
|
||
|
func NtQueryProcessBasicInformation(handle syscall.Handle) (ProcessBasicInformation, error) {
|
||
|
var processBasicInfo ProcessBasicInformation
|
||
|
processBasicInfoPtr := (*byte)(unsafe.Pointer(&processBasicInfo))
|
||
|
size := uint32(unsafe.Sizeof(processBasicInfo))
|
||
|
ntStatus, _ := _NtQueryInformationProcess(handle, 0, processBasicInfoPtr, size, nil)
|
||
|
if ntStatus != 0 {
|
||
|
return ProcessBasicInformation{}, errors.Errorf("NtQueryInformationProcess failed, NTSTATUS=0x%X", ntStatus)
|
||
|
}
|
||
|
|
||
|
return processBasicInfo, nil
|
||
|
}
|
||
|
|
||
|
// SystemProcessorPerformanceInformation contains CPU performance information
|
||
|
// for a single CPU.
|
||
|
type SystemProcessorPerformanceInformation struct {
|
||
|
IdleTime time.Duration // Amount of time spent idle.
|
||
|
KernelTime time.Duration // Kernel time does NOT include time spent in idle.
|
||
|
UserTime time.Duration // Amount of time spent executing in user mode.
|
||
|
}
|
||
|
|
||
|
// _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION is an equivalent representation of
|
||
|
// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION in the Windows API. This struct is
|
||
|
// used internally with NtQuerySystemInformation call and is not exported. The
|
||
|
// exported equivalent is SystemProcessorPerformanceInformation.
|
||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||
|
type _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION struct {
|
||
|
IdleTime int64
|
||
|
KernelTime int64
|
||
|
UserTime int64
|
||
|
Reserved1 [2]int64
|
||
|
Reserved2 uint32
|
||
|
}
|
||
|
|
||
|
// NtQuerySystemProcessorPerformanceInformation queries CPU performance
|
||
|
// information for each CPU. It uses the NtQuerySystemInformation function to
|
||
|
// collect the SystemProcessorPerformanceInformation.
|
||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||
|
func NtQuerySystemProcessorPerformanceInformation() ([]SystemProcessorPerformanceInformation, error) {
|
||
|
// NTSTATUS code for success.
|
||
|
// https://msdn.microsoft.com/en-us/library/cc704588.aspx
|
||
|
const STATUS_SUCCESS = 0
|
||
|
|
||
|
// From the _SYSTEM_INFORMATION_CLASS enum.
|
||
|
// http://processhacker.sourceforge.net/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
|
||
|
const systemProcessorPerformanceInformation = 8
|
||
|
|
||
|
// Create a buffer large enough to hold an entry for each processor.
|
||
|
b := make([]byte, runtime.NumCPU()*sizeofSystemProcessorPerformanceInformation)
|
||
|
|
||
|
// Query the performance information. Note that this function uses 0 to
|
||
|
// indicate success. Most other Windows functions use non-zero for success.
|
||
|
var returnLength uint32
|
||
|
ntStatus, _ := _NtQuerySystemInformation(systemProcessorPerformanceInformation, &b[0], uint32(len(b)), &returnLength)
|
||
|
if ntStatus != STATUS_SUCCESS {
|
||
|
return nil, errors.Errorf("NtQuerySystemInformation failed, NTSTATUS=0x%X, bufLength=%v, returnLength=%v", ntStatus, len(b), returnLength)
|
||
|
}
|
||
|
|
||
|
return readSystemProcessorPerformanceInformationBuffer(b)
|
||
|
}
|
||
|
|
||
|
// readSystemProcessorPerformanceInformationBuffer reads from a buffer
|
||
|
// containing SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION data. The buffer should
|
||
|
// contain one entry for each CPU.
|
||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||
|
func readSystemProcessorPerformanceInformationBuffer(b []byte) ([]SystemProcessorPerformanceInformation, error) {
|
||
|
n := len(b) / sizeofSystemProcessorPerformanceInformation
|
||
|
r := bytes.NewReader(b)
|
||
|
|
||
|
rtn := make([]SystemProcessorPerformanceInformation, 0, n)
|
||
|
for i := 0; i < n; i++ {
|
||
|
_, err := r.Seek(int64(i*sizeofSystemProcessorPerformanceInformation), io.SeekStart)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "failed to seek to cpuN=%v in buffer", i)
|
||
|
}
|
||
|
|
||
|
times := make([]uint64, 3)
|
||
|
for j := range times {
|
||
|
err := binary.Read(r, binary.LittleEndian, ×[j])
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "failed reading cpu times for cpuN=%v", i)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idleTime := time.Duration(times[0] * 100)
|
||
|
kernelTime := time.Duration(times[1] * 100)
|
||
|
userTime := time.Duration(times[2] * 100)
|
||
|
|
||
|
rtn = append(rtn, SystemProcessorPerformanceInformation{
|
||
|
IdleTime: idleTime,
|
||
|
KernelTime: kernelTime - idleTime, // Subtract out idle time from kernel time.
|
||
|
UserTime: userTime,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return rtn, nil
|
||
|
}
|