aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/elastic/gosigar/sys/windows/ntquery.go
blob: 85de365e1a89455fed3a6e6939dbbd3352f2d9b1 (plain) (blame)
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, &times[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
}