Skip to content
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

[windows] - Introduce PdhGetRawCounterArrayW #254

Merged
merged 6 commits into from
Nov 26, 2024
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
24 changes: 20 additions & 4 deletions helpers/windows/pdh/pdh_query_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (q *Query) AddEnglishCounter(counterPath string) (PdhCounterHandle, error)
}

// AddCounter adds the specified counter to the query.
func (q *Query) AddCounter(counterPath string, instance string, format string, wildcard bool) error {
func (q *Query) AddCounter(counterPath string, instance string, format string, wildcard bool, english bool) error {
if _, found := q.Counters[counterPath]; found {
return nil
}
Expand All @@ -95,7 +95,12 @@ func (q *Query) AddCounter(counterPath string, instance string, format string, w
} else {
instanceName = instance
}
h, err := PdhAddCounter(q.Handle, counterPath, 0)
var h PdhCounterHandle
if english {
h, err = PdhAddEnglishCounter(q.Handle, counterPath, 0)
} else {
h, err = PdhAddCounter(q.Handle, counterPath, 0)
}
Copy link
Contributor Author

@VihasMakwana VihasMakwana Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leehinman These are two different API calls.
PdhAddCounter is language specific.
PdhAddEnglishCounter is language neutral.

if err != nil {
return err
}
Expand Down Expand Up @@ -203,11 +208,22 @@ func (q *Query) GetCountersAndInstances(objectName string) ([]string, []string,
return UTF16ToStringArray(counters), UTF16ToStringArray(instances), nil
}

func (q *Query) GetRawCounterValue(counterName string) (*PdhRawCounter, error) {
func (q *Query) GetRawCounterValue(counterName string) (PdhRawCounter, error) {
if _, ok := q.Counters[counterName]; !ok {
return nil, fmt.Errorf("%s doesn't exist in the map; call AddCounter()", counterName)
return PdhRawCounter{}, fmt.Errorf("%s doesn't exist in the map; call AddCounter()", counterName)
}
c, err := PdhGetRawCounterValue(q.Counters[counterName].handle)
if err != nil {
return PdhRawCounter{}, err
}
return c, nil
}

func (q *Query) GetRawCounterArray(counterName string, filterTotal bool) (RawCounterArray, error) {
if _, ok := q.Counters[counterName]; !ok {
return nil, fmt.Errorf("%s doesn't exist in the map; call AddCounter()", counterName)
}
c, err := PdhGetRawCounterArray(q.Counters[counterName].handle, filterTotal)
if err != nil {
return nil, err
}
Expand Down
50 changes: 48 additions & 2 deletions helpers/windows/pdh/pdh_query_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestAddCounterInvalidArgWhenQueryClosed(t *testing.T) {
queryPath, err := q.GetCounterPaths(validQuery)
// if windows os language is ENG then err will be nil, else the GetCounterPaths will execute the AddCounter
if assert.NoError(t, err) {
err = q.AddCounter(queryPath[0], "TestInstanceName", "float", false)
err = q.AddCounter(queryPath[0], "TestInstanceName", "float", false, false)
assert.Error(t, err, PDH_INVALID_HANDLE)
} else {
assert.Error(t, err, PDH_INVALID_ARGUMENT)
Expand Down Expand Up @@ -73,7 +73,7 @@ func TestSuccessfulQuery(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = q.AddCounter(queryPath[0], "TestInstanceName", "floar", false)
err = q.AddCounter(queryPath[0], "TestInstanceName", "floar", false, false)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -193,3 +193,49 @@ func TestUTF16ToStringArray(t *testing.T) {
assert.Contains(t, array, res)
}
}

func TestSortOrder(t *testing.T) {
scenarios := []struct {
name string
counters []string
}{{
name: "processor-information",
counters: []string{
"\\Processor Information(*)\\% User Time",
"\\Processor Information(*)\\% Privileged Time",
"\\Processor Information(*)\\% Idle Time",
},
}, {
name: "processor",
counters: []string{
"\\Processor(*)\\% User Time",
"\\Processor(*)\\% Privileged Time",
"\\Processor(*)\\% Idle Time",
},
}}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
var q Query
assert.NoError(t, q.Open())
for _, counter := range s.counters {
assert.NoError(t, q.AddCounter(counter, "", "", false, false))
}
assert.NoError(t, q.CollectData())
rawCounters := make([][]PdhRawCounterItem, 0)
for _, counter := range s.counters {
arr, err := q.GetRawCounterArray(counter, true)
assert.NoError(t, err)
rawCounters = append(rawCounters, arr)
}
for i := 0; i < len(rawCounters)-1; i++ {
assert.Equalf(t, len(rawCounters[i]), len(rawCounters[i+1]), "returned counters should be equal")
}
// confirm that each index corresponds to one particular instance (i.e. core)
for i := 0; i < len(rawCounters)-1; i++ {
for j := 0; j < len(rawCounters[i]); j++ {
assert.Equalf(t, rawCounters[i][j].InstanceName, rawCounters[i+1][j].InstanceName, "Instance name should be equal")
}
}
})
}
}
63 changes: 60 additions & 3 deletions helpers/windows/pdh/pdh_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
package pdh

import (
"sort"
"strconv"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
Expand Down Expand Up @@ -108,6 +110,31 @@ type PdhRawCounter struct {
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) {
Expand Down Expand Up @@ -207,13 +234,43 @@ func PdhGetFormattedCounterValueLong(counter PdhCounterHandle) (uint32, *PdhCoun
}

// PdhGetRawCounterValue returns the raw value of a given counter.
func PdhGetRawCounterValue(counter PdhCounterHandle) (*PdhRawCounter, error) {
func PdhGetRawCounterValue(counter PdhCounterHandle) (PdhRawCounter, error) {
leehinman marked this conversation as resolved.
Show resolved Hide resolved
var value PdhRawCounter
if err := _PdhGetRawCounter(counter, uintptr(unsafe.Pointer(&value))); err != nil {
return &value, PdhErrno(err.(syscall.Errno))
return value, PdhErrno(err.(syscall.Errno))
}

return &value, nil
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") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to worry if this is a non-English Windows system? Just wondering if we might have something besides "_Total".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance name? Nope. The name remains _Total.
I did a quick test on windows VM by changing language to Spanish.

For counter name? yes, the language makes a lot of change.
Hence, I've updated the AddCounter function in this PR. PTAL!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to worry if this is a non-English Windows system? Just wondering if we might have something besides "_Total".

For this specific piece of code? Nope.

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.
Expand Down
15 changes: 15 additions & 0 deletions helpers/windows/pdh/zpdh_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading