helpers/windows/pdh/pdh_windows.go (288 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build windows
package pdh
import (
"sort"
"strconv"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
"golang.org/x/sys/windows"
)
// Windows API calls
//sys _PdhOpenQuery(dataSource *uint16, userData uintptr, query *PdhQueryHandle) (errcode error) [failretval!=0] = pdh.PdhOpenQueryW
//sys _PdhAddEnglishCounter(query PdhQueryHandle, counterPath string, userData uintptr, counter *PdhCounterHandle) (errcode error) [failretval!=0] = pdh.PdhAddEnglishCounterW
//sys _PdhAddCounter(query PdhQueryHandle, counterPath string, userData uintptr, counter *PdhCounterHandle) (errcode error) [failretval!=0] = pdh.PdhAddCounterW
//sys _PdhRemoveCounter(counter PdhCounterHandle) (errcode error) [failretval!=0] = pdh.PdhRemoveCounter
//sys _PdhCollectQueryData(query PdhQueryHandle) (errcode error) [failretval!=0] = pdh.PdhCollectQueryData
//sys _PdhCollectQueryDataEx(query PdhQueryHandle, interval uint32, event windows.Handle) (errcode error) [failretval!=0] = pdh.PdhCollectQueryDataEx
//sys _PdhGetFormattedCounterValueDouble(counter PdhCounterHandle, format PdhCounterFormat, counterType *uint32, value *PdhCounterValueDouble) (errcode error) [failretval!=0] = pdh.PdhGetFormattedCounterValue
//sys _PdhGetFormattedCounterValueLarge(counter PdhCounterHandle, format PdhCounterFormat, counterType *uint32, value *PdhCounterValueLarge) (errcode error) [failretval!=0] = pdh.PdhGetFormattedCounterValue
//sys _PdhGetFormattedCounterValueLong(counter PdhCounterHandle, format PdhCounterFormat, counterType *uint32, value *PdhCounterValueLong) (errcode error) [failretval!=0]= pdh.PdhGetFormattedCounterValue
//sys _PdhCloseQuery(query PdhQueryHandle) (errcode error) [failretval!=0] = pdh.PdhCloseQuery
//sys _PdhExpandWildCardPath(dataSource *uint16, wildcardPath *uint16, expandedPathList *uint16, pathListLength *uint32) (errcode error) [failretval!=0] = pdh.PdhExpandWildCardPathW
//sys _PdhExpandCounterPath(wildcardPath *uint16, expandedPathList *uint16, pathListLength *uint32) (errcode error) [failretval!=0] = pdh.PdhExpandCounterPathW
//sys _PdhGetCounterInfo(counter PdhCounterHandle, text uint16, size *uint32, lpBuffer *byte) (errcode error) [failretval!=0] = pdh.PdhGetCounterInfoW
//sys _PdhEnumObjectItems(dataSource uint16, machineName uint16, objectName *uint16, counterList *uint16, counterListSize *uint32, instanceList *uint16, instanceListSize *uint32, detailLevel uint32, flags uint32) (errcode error) [failretval!=0] = pdh.PdhEnumObjectItemsW
type PdhQueryHandle uintptr
var InvalidQueryHandle = ^PdhQueryHandle(0)
type PdhCounterHandle uintptr
var InvalidCounterHandle = ^PdhCounterHandle(0)
// PerformanceDetailWizard is the counter detail level
const PerformanceDetailWizard = 400
// PdhCounterInfo struct contains the performance counter details
type PdhCounterInfo struct {
DwLength uint32
DwType uint32
CVersion uint32
CStatus uint32
LScale int32
LDefaultScale int32
DwUserData *uint32
DwQueryUserData *uint32
SzFullPath *uint16 // pointer to a string
SzMachineName *uint16 // pointer to a string
SzObjectName *uint16 // pointer to a string
SzInstanceName *uint16 // pointer to a string
SzParentInstance *uint16 // pointer to a string
DwInstanceIndex uint32 // pointer to a string
SzCounterName *uint16 // pointer to a string
Padding [4]byte
SzExplainText *uint16 // pointer to a string
DataBuffer [1]uint32 // pointer to an extra space
}
// PdhCounterValueDouble for double values
type PdhCounterValueDouble struct {
CStatus uint32
Pad_cgo_0 [4]byte
Value float64
Pad_cgo_1 [4]byte
}
// PdhCounterValueLarge for 64 bit integer values
type PdhCounterValueLarge struct {
CStatus uint32
Pad_cgo_0 [4]byte
Value int64
Pad_cgo_1 [4]byte
}
// PdhCounterValueLong for long values
type PdhCounterValueLong struct {
CStatus uint32
Pad_cgo_0 [4]byte
Value int32
Pad_cgo_1 [4]byte
}
type PdhRawCounter struct {
CStatus uint32
TimeStamp windows.Filetime
FirstValue int64
SecondValue int64
MultiCount uint32
}
type pdhRawCounterItem struct {
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
SzName *uint16
//A pdhRawCounter structure that contains the raw counter value of the instance
RawValue PdhRawCounter
}
type PdhRawCounterItem struct {
InstanceName string
RawValue PdhRawCounter
}
type RawCounterArray []PdhRawCounterItem
func (a RawCounterArray) Len() int {
return len(a)
}
func (a RawCounterArray) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a RawCounterArray) Less(i, j int) bool {
return a[i].InstanceName < a[j].InstanceName
}
// PdhOpenQuery creates a new query.
func PdhOpenQuery(dataSource string, userData uintptr) (PdhQueryHandle, error) {
var dataSourcePtr *uint16
if dataSource != "" {
var err error
dataSourcePtr, err = syscall.UTF16PtrFromString(dataSource)
if err != nil {
return InvalidQueryHandle, err
}
}
var handle PdhQueryHandle
if err := _PdhOpenQuery(dataSourcePtr, userData, &handle); err != nil {
return InvalidQueryHandle, PdhErrno(err.(syscall.Errno))
}
return handle, nil
}
// PdhAddEnglishCounter adds the specified counter to the query.
func PdhAddEnglishCounter(query PdhQueryHandle, counterPath string, userData uintptr) (PdhCounterHandle, error) {
var handle PdhCounterHandle
if err := _PdhAddEnglishCounter(query, counterPath, userData, &handle); err != nil {
return InvalidCounterHandle, PdhErrno(err.(syscall.Errno))
}
return handle, nil
}
// PdhAddCounter adds the specified counter to the query.
func PdhAddCounter(query PdhQueryHandle, counterPath string, userData uintptr) (PdhCounterHandle, error) {
var handle PdhCounterHandle
if err := _PdhAddCounter(query, counterPath, userData, &handle); err != nil {
return InvalidCounterHandle, PdhErrno(err.(syscall.Errno))
}
return handle, nil
}
// PdhRemoveCounter removes the specified counter to the query.
func PdhRemoveCounter(counter PdhCounterHandle) error {
if err := _PdhRemoveCounter(counter); err != nil {
return PdhErrno(err.(syscall.Errno))
}
return nil
}
// PdhCollectQueryData collects the current raw data value for all counters in the specified query.
func PdhCollectQueryData(query PdhQueryHandle) error {
if err := _PdhCollectQueryData(query); err != nil {
return PdhErrno(err.(syscall.Errno))
}
return nil
}
// PdhCollectQueryDataEx collects the current raw data value for all counters in the specified query.
func PdhCollectQueryDataEx(query PdhQueryHandle, interval uint32, event windows.Handle) error {
if err := _PdhCollectQueryDataEx(query, interval, event); err != nil {
return PdhErrno(err.(syscall.Errno))
}
return nil
}
// PdhGetFormattedCounterValueDouble computes a displayable double value for the specified counter.
func PdhGetFormattedCounterValueDouble(counter PdhCounterHandle) (uint32, *PdhCounterValueDouble, error) {
var counterType uint32
var value PdhCounterValueDouble
if err := _PdhGetFormattedCounterValueDouble(counter, PdhFmtDouble|PdhFmtNoCap100, &counterType, &value); err != nil {
return 0, &value, PdhErrno(err.(syscall.Errno))
}
return counterType, &value, nil
}
// PdhGetFormattedCounterValueLarge computes a displayable large value for the specified counter.
func PdhGetFormattedCounterValueLarge(counter PdhCounterHandle) (uint32, *PdhCounterValueLarge, error) {
var counterType uint32
var value PdhCounterValueLarge
if err := _PdhGetFormattedCounterValueLarge(counter, PdhFmtLarge|PdhFmtNoCap100, &counterType, &value); err != nil {
return 0, &value, PdhErrno(err.(syscall.Errno))
}
return counterType, &value, nil
}
// PdhGetFormattedCounterValueLong computes a displayable long value for the specified counter.
func PdhGetFormattedCounterValueLong(counter PdhCounterHandle) (uint32, *PdhCounterValueLong, error) {
var counterType uint32
var value PdhCounterValueLong
if err := _PdhGetFormattedCounterValueLong(counter, PdhFmtLong|PdhFmtNoCap100, &counterType, &value); err != nil {
return 0, &value, PdhErrno(err.(syscall.Errno))
}
return counterType, &value, nil
}
// PdhGetRawCounterValue returns the raw value of a given counter.
func PdhGetRawCounterValue(counter PdhCounterHandle) (PdhRawCounter, error) {
var value PdhRawCounter
if err := _PdhGetRawCounter(counter, uintptr(unsafe.Pointer(&value))); err != nil {
return value, PdhErrno(err.(syscall.Errno))
}
return value, nil
}
func PdhGetRawCounterArray(counter PdhCounterHandle, filterTotal bool) (RawCounterArray, error) {
var bufferSize, itemCount uint32
if err := _PdhGetRawCounterArray(counter, &bufferSize, &itemCount, nil); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, PdhErrno(err.(syscall.Errno))
}
buf := make([]byte, bufferSize)
if err := _PdhGetRawCounterArray(counter, &bufferSize, &itemCount, &buf[0]); err != nil {
return nil, PdhErrno(err.(syscall.Errno))
}
items := unsafe.Slice((*pdhRawCounterItem)(unsafe.Pointer(&buf[0])), itemCount)
ret := make([]PdhRawCounterItem, 0, len(items))
for _, item := range items {
instance := windows.UTF16PtrToString(item.SzName)
if filterTotal && strings.Contains(instance, "_Total") {
continue
}
ret = append(ret, PdhRawCounterItem{
RawValue: item.RawValue,
InstanceName: instance,
})
}
// we sort the array by the instance name to ensure that each index in the final array corresponds to a specific core
// This is important because we will be collecting three different types of counters, and sorting ensures that each index in each counter aligns with the correct core.
sort.Sort(RawCounterArray(ret))
return ret, nil
}
return nil, PdhErrno(syscall.ERROR_NOT_FOUND)
}
// PdhExpandWildCardPath returns counter paths that match the given counter path.
func PdhExpandWildCardPath(utfPath *uint16) ([]uint16, error) {
var bufferSize uint32
if err := _PdhExpandWildCardPath(nil, utfPath, nil, &bufferSize); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, PdhErrno(err.(syscall.Errno))
}
expandPaths := make([]uint16, bufferSize)
if err = _PdhExpandWildCardPath(nil, utfPath, &expandPaths[0], &bufferSize); err != nil {
return nil, PdhErrno(err.(syscall.Errno))
}
return expandPaths, err
}
return nil, PdhErrno(syscall.ERROR_NOT_FOUND)
}
// PdhExpandCounterPath returns counter paths that match the given counter path, for 32 bit windows.
func PdhExpandCounterPath(utfPath *uint16) ([]uint16, error) {
var bufferSize uint32
if err := _PdhExpandCounterPath(utfPath, nil, &bufferSize); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, PdhErrno(err.(syscall.Errno))
}
expandPaths := make([]uint16, bufferSize)
if err := _PdhExpandCounterPath(utfPath, &expandPaths[0], &bufferSize); err != nil {
return nil, PdhErrno(err.(syscall.Errno))
}
return expandPaths, nil
}
return nil, nil
}
// PdhGetCounterInfo returns the counter information for given handle
func PdhGetCounterInfo(handle PdhCounterHandle) (*PdhCounterInfo, error) {
var bufSize uint32
var buff []byte
if err := _PdhGetCounterInfo(handle, 0, &bufSize, nil); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, PdhErrno(err.(syscall.Errno))
}
buff = make([]byte, bufSize)
bufSize = uint32(len(buff))
if err = _PdhGetCounterInfo(handle, 0, &bufSize, &buff[0]); err == nil {
counterInfo := (*PdhCounterInfo)(unsafe.Pointer(&buff[0]))
if counterInfo != nil {
return counterInfo, nil
}
}
}
return nil, nil
}
// PdhCloseQuery closes all counters contained in the specified query.
func PdhCloseQuery(query PdhQueryHandle) error {
if err := _PdhCloseQuery(query); err != nil {
return PdhErrno(err.(syscall.Errno))
}
return nil
}
// PdhEnumObjectItems returns the counters and instance info for given object
func PdhEnumObjectItems(objectName string) ([]uint16, []uint16, error) {
var (
cBuff = make([]uint16, 1)
cBuffSize = uint32(0)
iBuff = make([]uint16, 1)
iBuffSize = uint32(0)
)
obj := windows.StringToUTF16Ptr(objectName)
if err := _PdhEnumObjectItems(
0,
0,
obj,
&cBuff[0],
&cBuffSize,
&iBuff[0],
&iBuffSize,
PerformanceDetailWizard,
0); err != nil {
if PdhErrno(err.(syscall.Errno)) != PDH_MORE_DATA {
return nil, nil, PdhErrno(err.(syscall.Errno))
}
cBuff = make([]uint16, cBuffSize)
iBuff = make([]uint16, iBuffSize)
if err = _PdhEnumObjectItems(
0,
0,
obj,
&cBuff[0],
&cBuffSize,
&iBuff[0],
&iBuffSize,
PerformanceDetailWizard,
0); err != nil {
return nil, nil, err
}
return cBuff, iBuff, nil
}
return nil, nil, nil
}
// Error returns a more explicit error message.
func (e PdhErrno) Error() string {
// If the value is not one of the known PDH errors then assume its a
// general windows error.
if _, found := pdhErrors[e]; !found {
return syscall.Errno(e).Error()
}
// Use FormatMessage to convert the PDH errno to a string.
// Example: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373046(v=vs.85).aspx
var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS
b := make([]uint16, 300)
n, err := windows.FormatMessage(flags, modpdh.Handle(), uint32(e), 0, b, nil)
if err != nil {
return "pdh error #" + strconv.Itoa(int(e))
}
// Trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
}
return string(utf16.Decode(b[:n]))
}