Skip to content

[pkg/winperfcounters] Add support to retrieve raw values #39835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .chloggen/add-raw-value-scrape-to-winperfcounters.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: pkg/winperfcounters

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add methods to scrape raw values from Windows performance counters.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [39835]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ var (
pdh_ValidatePathW *syscall.Proc
pdh_ExpandWildCardPathW *syscall.Proc
pdh_GetCounterInfoW *syscall.Proc
pdh_GetRawCounterValue *syscall.Proc
pdh_GetRawCounterArrayW *syscall.Proc
)

func init() {
Expand All @@ -198,6 +200,8 @@ func init() {
pdh_ValidatePathW = libpdhDll.MustFindProc("PdhValidatePathW")
pdh_ExpandWildCardPathW = libpdhDll.MustFindProc("PdhExpandWildCardPathW")
pdh_GetCounterInfoW = libpdhDll.MustFindProc("PdhGetCounterInfoW")
pdh_GetRawCounterValue = libpdhDll.MustFindProc("PdhGetRawCounterValue")
pdh_GetRawCounterArrayW = libpdhDll.MustFindProc("PdhGetRawCounterArrayW")
}

// PdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the
Expand Down Expand Up @@ -498,3 +502,45 @@ func PdhGetCounterInfo(hCounter PDH_HCOUNTER, bRetrieveExplainText int, pdwBuffe

return uint32(ret)
}

// PdhGetRawCounterValue retrieves the current raw value of the specified counter.
// hCounter [in]
// Handle of the counter from which you want to retrieve the raw value. The PdhAddCounter function returns this handle.
//
// lpdwType [out]
// Pointer to a variable that receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server documentation.
//
// pValue [out]
// Pointer to a PDH_RAW_COUNTER structure that receives the raw counter value.
func PdhGetRawCounterValue(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_RAW_COUNTER) uint32 {
ret, _, _ := pdh_GetRawCounterValue.Call(
uintptr(hCounter),
uintptr(unsafe.Pointer(lpdwType)),
uintptr(unsafe.Pointer(pValue)),
)

return uint32(ret)
}

// PdhGetRawCounterArrayW retrieves an array of raw counter values for all instances of the specified counter.
// hCounter [in]
// Handle of the counter from which you want to retrieve the raw values. The PdhAddCounter function returns this handle.
//
// lpdwBufferSize [in, out]
// Pointer to a variable that specifies the size of the buffer, in bytes. If the buffer is too small, the function sets this variable to the required buffer size.
//
// lpdwBufferCount [out]
// Pointer to a variable that receives the number of elements in the buffer.
//
// itemBuffer [out]
// Pointer to a buffer that receives an array of PDH_RAW_COUNTER_ITEM structures. Each structure contains the raw counter value for an instance.
func PdhGetRawCounterArrayW(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
ret, _, _ := pdh_GetRawCounterArrayW.Call(
uintptr(hCounter),
uintptr(unsafe.Pointer(lpdwBufferSize)),
uintptr(unsafe.Pointer(lpdwBufferCount)),
uintptr(unsafe.Pointer(itemBuffer)),
)

return uint32(ret)
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
FmtValue PDH_FMT_COUNTERVALUE_LONG
}

// PDH_RAW_COUNTER structure contains the raw data value of a counter.
type PDH_RAW_COUNTER struct {
CStatus uint32 // Counter status that indicates if the counter value is valid.
padding [4]byte
TimeStamp FILETIME // Time at which the sample was taken.
FirstValue int64 // First raw counter value.
SecondValue int64 // Second raw counter value (used for some calculations).
MultiCount uint32 // Counter type-specific value.
padding2 [4]byte
}

// PDH_RAW_COUNTER_ITEM structure contains the raw counter value for a specific instance.
type PDH_RAW_COUNTER_ITEM struct {
SzName *uint16 // Pointer to the instance name.
padding [4]byte
RawValue PDH_RAW_COUNTER // Raw counter value for the instance.
}

// PDH_COUNTER_INFO structure contains information describing the properties of a counter. This information also includes the counter path.
type PDH_COUNTER_INFO struct {
// Size of the structure, including the appended strings, in bytes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
FmtValue PDH_FMT_COUNTERVALUE_LONG
}

// PDH_RAW_COUNTER structure contains the raw data value of a counter.
type PDH_RAW_COUNTER struct {
CStatus uint32 // Counter status that indicates if the counter value is valid.
TimeStamp FILETIME // Time at which the sample was taken.
FirstValue int64 // First raw counter value.
SecondValue int64 // Second raw counter value (used for some calculations).
MultiCount uint32 // Counter type-specific value.
}

// PDH_RAW_COUNTER_ITEM structure contains the raw counter value for a specific instance.
type PDH_RAW_COUNTER_ITEM struct {
SzName *uint16 // Pointer to the instance name.
RawValue PDH_RAW_COUNTER // Raw counter value for the instance.
}

// PDH_COUNTER_INFO structure contains information describing the properties of a counter. This information also includes the counter path.
type PDH_COUNTER_INFO struct {
// Size of the structure, including the appended strings, in bytes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ import (
"unsafe"
)

// PerformanceQuery is abstraction for PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
// CounterValue is abstraction for PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
type CounterValue struct {
InstanceName string
Value float64
}

// RawCounterValue is abstraction for PDH_RAW_COUNTER_ITEM
type RawCounterValue struct {
InstanceName string
RawValue int64
}

// PerformanceQuery provides wrappers around Windows performance counters API for easy usage in GO
type PerformanceQuery interface {
Open() error
Expand All @@ -26,6 +32,8 @@ type PerformanceQuery interface {
GetCounterPath(counterHandle PDH_HCOUNTER) (string, error)
GetFormattedCounterValueDouble(hCounter PDH_HCOUNTER) (float64, error)
GetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER) ([]CounterValue, error)
GetRawCounterValue(hCounter PDH_HCOUNTER) (int64, error)
GetRawCounterArray(hCounter PDH_HCOUNTER) ([]RawCounterValue, error)
CollectData() error
CollectDataWithTime() (time.Time, error)
IsVistaOrNewer() bool
Expand Down Expand Up @@ -163,6 +171,45 @@ func (m *PerformanceQueryImpl) GetFormattedCounterArrayDouble(hCounter PDH_HCOUN
return nil, NewPdhError(ret)
}

func (m *PerformanceQueryImpl) GetRawCounterValue(hCounter PDH_HCOUNTER) (int64, error) {
var counterType uint32
var rawValue PDH_RAW_COUNTER
var ret uint32

if ret = PdhGetRawCounterValue(hCounter, &counterType, &rawValue); ret == ERROR_SUCCESS {
if rawValue.CStatus == PDH_CSTATUS_VALID_DATA {
return rawValue.FirstValue, nil
} else {
return 0, NewPdhError(rawValue.CStatus)
}
} else {
return 0, NewPdhError(ret)
}
}

func (m *PerformanceQueryImpl) GetRawCounterArray(hCounter PDH_HCOUNTER) ([]RawCounterValue, error) {
var buffSize uint32
var itemCount uint32
var ret uint32

if ret = PdhGetRawCounterArrayW(hCounter, &buffSize, &itemCount, nil); ret == PDH_MORE_DATA {
buff := make([]byte, buffSize)

if ret = PdhGetRawCounterArrayW(hCounter, &buffSize, &itemCount, &buff[0]); ret == ERROR_SUCCESS {
items := unsafe.Slice((*PDH_RAW_COUNTER_ITEM)(unsafe.Pointer(&buff[0])), itemCount)
values := make([]RawCounterValue, 0, itemCount)
for _, item := range items {
if item.RawValue.CStatus == PDH_CSTATUS_VALID_DATA {
val := RawCounterValue{UTF16PtrToString(item.SzName), item.RawValue.FirstValue}
values = append(values, val)
}
}
return values, nil
}
}
return nil, NewPdhError(ret)
}

func (m *PerformanceQueryImpl) CollectData() error {
var ret uint32
if m.query == 0 {
Expand Down
Loading
Loading