github.com/elastic/gosigar@v0.14.3/sys/windows/ntquery.go (about)

     1  // +build windows
     2  
     3  package windows
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"io"
     9  	"runtime"
    10  	"syscall"
    11  	"time"
    12  	"unsafe"
    13  
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // On both 32-bit and 64-bit systems NtQuerySystemInformation expects the
    18  // size of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION to be 48.
    19  const sizeofSystemProcessorPerformanceInformation = 48
    20  
    21  // ProcessBasicInformation is an equivalent representation of
    22  // PROCESS_BASIC_INFORMATION in the Windows API.
    23  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
    24  type ProcessBasicInformation struct {
    25  	ExitStatus                   uint
    26  	PebBaseAddress               uintptr
    27  	AffinityMask                 uint
    28  	BasePriority                 uint
    29  	UniqueProcessID              uint
    30  	InheritedFromUniqueProcessID uint
    31  }
    32  
    33  // NtQueryProcessBasicInformation queries basic information about the process
    34  // associated with the given handle (provided by OpenProcess). It uses the
    35  // NtQueryInformationProcess function to collect the data.
    36  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
    37  func NtQueryProcessBasicInformation(handle syscall.Handle) (ProcessBasicInformation, error) {
    38  	var processBasicInfo ProcessBasicInformation
    39  	processBasicInfoPtr := (*byte)(unsafe.Pointer(&processBasicInfo))
    40  	size := uint32(unsafe.Sizeof(processBasicInfo))
    41  	ntStatus, _ := _NtQueryInformationProcess(handle, 0, processBasicInfoPtr, size, nil)
    42  	if ntStatus != 0 {
    43  		return ProcessBasicInformation{}, errors.Errorf("NtQueryInformationProcess failed, NTSTATUS=0x%X", ntStatus)
    44  	}
    45  
    46  	return processBasicInfo, nil
    47  }
    48  
    49  // SystemProcessorPerformanceInformation contains CPU performance information
    50  // for a single CPU.
    51  type SystemProcessorPerformanceInformation struct {
    52  	IdleTime   time.Duration // Amount of time spent idle.
    53  	KernelTime time.Duration // Kernel time does NOT include time spent in idle.
    54  	UserTime   time.Duration // Amount of time spent executing in user mode.
    55  }
    56  
    57  // _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION is an equivalent representation of
    58  // SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION in the Windows API. This struct is
    59  // used internally with NtQuerySystemInformation call and is not exported. The
    60  // exported equivalent is SystemProcessorPerformanceInformation.
    61  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
    62  type _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION struct {
    63  	IdleTime   int64
    64  	KernelTime int64
    65  	UserTime   int64
    66  	Reserved1  [2]int64
    67  	Reserved2  uint32
    68  }
    69  
    70  // NtQuerySystemProcessorPerformanceInformation queries CPU performance
    71  // information for each CPU. It uses the NtQuerySystemInformation function to
    72  // collect the SystemProcessorPerformanceInformation.
    73  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
    74  func NtQuerySystemProcessorPerformanceInformation() ([]SystemProcessorPerformanceInformation, error) {
    75  	// NTSTATUS code for success.
    76  	// https://msdn.microsoft.com/en-us/library/cc704588.aspx
    77  	const STATUS_SUCCESS = 0
    78  
    79  	// From the _SYSTEM_INFORMATION_CLASS enum.
    80  	// http://processhacker.sourceforge.net/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
    81  	const systemProcessorPerformanceInformation = 8
    82  
    83  	// Create a buffer large enough to hold an entry for each processor.
    84  	b := make([]byte, runtime.NumCPU()*sizeofSystemProcessorPerformanceInformation)
    85  
    86  	// Query the performance information. Note that this function uses 0 to
    87  	// indicate success. Most other Windows functions use non-zero for success.
    88  	var returnLength uint32
    89  	ntStatus, _ := _NtQuerySystemInformation(systemProcessorPerformanceInformation, &b[0], uint32(len(b)), &returnLength)
    90  	if ntStatus != STATUS_SUCCESS {
    91  		return nil, errors.Errorf("NtQuerySystemInformation failed, NTSTATUS=0x%X, bufLength=%v, returnLength=%v", ntStatus, len(b), returnLength)
    92  	}
    93  
    94  	return readSystemProcessorPerformanceInformationBuffer(b)
    95  }
    96  
    97  // readSystemProcessorPerformanceInformationBuffer reads from a buffer
    98  // containing SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION data. The buffer should
    99  // contain one entry for each CPU.
   100  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
   101  func readSystemProcessorPerformanceInformationBuffer(b []byte) ([]SystemProcessorPerformanceInformation, error) {
   102  	n := len(b) / sizeofSystemProcessorPerformanceInformation
   103  	r := bytes.NewReader(b)
   104  
   105  	rtn := make([]SystemProcessorPerformanceInformation, 0, n)
   106  	for i := 0; i < n; i++ {
   107  		_, err := r.Seek(int64(i*sizeofSystemProcessorPerformanceInformation), io.SeekStart)
   108  		if err != nil {
   109  			return nil, errors.Wrapf(err, "failed to seek to cpuN=%v in buffer", i)
   110  		}
   111  
   112  		times := make([]uint64, 3)
   113  		for j := range times {
   114  			err := binary.Read(r, binary.LittleEndian, &times[j])
   115  			if err != nil {
   116  				return nil, errors.Wrapf(err, "failed reading cpu times for cpuN=%v", i)
   117  			}
   118  		}
   119  
   120  		idleTime := time.Duration(times[0] * 100)
   121  		kernelTime := time.Duration(times[1] * 100)
   122  		userTime := time.Duration(times[2] * 100)
   123  
   124  		rtn = append(rtn, SystemProcessorPerformanceInformation{
   125  			IdleTime:   idleTime,
   126  			KernelTime: kernelTime - idleTime, // Subtract out idle time from kernel time.
   127  			UserTime:   userTime,
   128  		})
   129  	}
   130  
   131  	return rtn, nil
   132  }