1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// +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
}
|