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, ×[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 }