aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/elastic/gosigar/sys/windows/ntquery.go
blob: 85de365e1a89455fed3a6e6939dbbd3352f2d9b1 (plain) (tree)



































































































































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