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
}
|