diff --git a/.golangci.yml b/.golangci.yml index 713bd5d61b934..b8b1b1f8e826d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,6 +17,7 @@ run: # default value is empty list, but default dirs are skipped independently # from this option's value (see skip-dirs-use-default). skip-dirs: + - third_party # default is true. Enables skipping of directories: # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ diff --git a/Makefile.Common b/Makefile.Common index 0e777686602c0..fac58c94db377 100644 --- a/Makefile.Common +++ b/Makefile.Common @@ -2,6 +2,7 @@ SRC_ROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) ALL_SRC := $(shell find . -name '*.go' \ + -not -path '*/third_party/*' \ -type f | sort) # All source code and documents. Used in spell check. @@ -126,4 +127,4 @@ impi: .PHONY: dep dep: - go mod download \ No newline at end of file + go mod download diff --git a/go.mod b/go.mod index 3cf1a296223d4..d1f41a5b20f86 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/receiver/splunkhecreceiver v0.0.0-00010101000000-000000000000 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.0.0-00010101000000-000000000000 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/wavefrontreceiver v0.0.0-00010101000000-000000000000 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver v0.0.0-00010101000000-000000000000 github.com/pavius/impi v0.0.3 github.com/securego/gosec v0.0.0-20200316084457-7da9f46445fd // indirect github.com/stretchr/testify v1.6.1 @@ -139,6 +140,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/wavef replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/dockerstatsreceiver => ./receiver/dockerstatsreceiver +replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver => ./receiver/windowsperfcountersreceiver + replace github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sprocessor => ./processor/k8sprocessor/ replace github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor => ./processor/resourcedetectionprocessor/ diff --git a/receiver/windowsperfcountersreceiver/Makefile b/receiver/windowsperfcountersreceiver/Makefile new file mode 100644 index 0000000000000..c1496226e5905 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common \ No newline at end of file diff --git a/receiver/windowsperfcountersreceiver/go.mod b/receiver/windowsperfcountersreceiver/go.mod new file mode 100644 index 0000000000000..eed8f8a154788 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/go.mod @@ -0,0 +1,8 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver + +go 1.14 + +require ( + github.com/stretchr/testify v1.6.1 + golang.org/x/sys v0.0.0-20201005172224-997123666555 +) diff --git a/receiver/windowsperfcountersreceiver/go.sum b/receiver/windowsperfcountersreceiver/go.sum new file mode 100644 index 0000000000000..69fb4f47862db --- /dev/null +++ b/receiver/windowsperfcountersreceiver/go.sum @@ -0,0 +1,13 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20201005172224-997123666555 h1:fihtqzYxy4E31W1yUlyRGveTZT1JIP0bmKaDZ2ceKAw= +golang.org/x/sys v0.0.0-20201005172224-997123666555/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/receiver/windowsperfcountersreceiver/internal/pdh/pdh.go b/receiver/windowsperfcountersreceiver/internal/pdh/pdh.go new file mode 100644 index 0000000000000..3f40f880b5995 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/pdh/pdh.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed 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. + +// +build windows + +package pdh + +import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters" + +const totalInstanceName = "_Total" + +type PerfCounter struct { + path string + query win_perf_counters.PerformanceQuery + handle win_perf_counters.PDH_HCOUNTER +} + +// NewPerfCounter returns a new performance counter for the specified descriptor. +func NewPerfCounter(counterPath string, collectOnStartup bool) (*PerfCounter, error) { + query := &win_perf_counters.PerformanceQueryImpl{} + err := query.Open() + if err != nil { + return nil, err + } + + var handle win_perf_counters.PDH_HCOUNTER + handle, err = query.AddEnglishCounterToQuery(counterPath) + if err != nil { + return nil, err + } + + // Some perf counters (e.g. cpu) return the usage stats since the last measure. + // We collect data on startup to avoid an invalid initial reading + if collectOnStartup { + err = query.CollectData() + if err != nil { + return nil, err + } + } + + counter := &PerfCounter{ + path: counterPath, + query: query, + handle: handle, + } + + return counter, nil +} + +// Close +func (pc *PerfCounter) Close() error { + return pc.query.Close() +} + +// Path +func (pc *PerfCounter) Path() string { + return pc.path +} + +// ScrapeData +func (pc *PerfCounter) ScrapeData() ([]win_perf_counters.CounterValue, error) { + err := pc.query.CollectData() + if err != nil { + return nil, err + } + + vals, err := pc.query.GetFormattedCounterArrayDouble(pc.handle) + if err != nil { + return nil, err + } + + vals = removeTotalIfMultipleValues(vals) + return vals, nil +} + +func removeTotalIfMultipleValues(vals []win_perf_counters.CounterValue) []win_perf_counters.CounterValue { + if len(vals) <= 1 { + return vals + } + + for i, val := range vals { + if val.InstanceName == totalInstanceName { + return removeItemAt(vals, i) + } + } + + return vals +} + +func removeItemAt(vals []win_perf_counters.CounterValue, idx int) []win_perf_counters.CounterValue { + vals[idx] = vals[len(vals)-1] + vals[len(vals)-1] = win_perf_counters.CounterValue{} + return vals[:len(vals)-1] +} diff --git a/receiver/windowsperfcountersreceiver/internal/pdh/pdh_notwindows.go b/receiver/windowsperfcountersreceiver/internal/pdh/pdh_notwindows.go new file mode 100644 index 0000000000000..0d66a9674b49a --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/pdh/pdh_notwindows.go @@ -0,0 +1,17 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed 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. + +// +build !windows + +package pdh diff --git a/receiver/windowsperfcountersreceiver/internal/pdh/pdh_test.go b/receiver/windowsperfcountersreceiver/internal/pdh/pdh_test.go new file mode 100644 index 0000000000000..02a06f5787f02 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/pdh/pdh_test.go @@ -0,0 +1,91 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed 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. + +// +build windows + +package pdh + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters" +) + +func TestNewPerfCounter_InvalidPath(t *testing.T) { + _, err := NewPerfCounter("Invalid Counter Path", false) + if assert.Error(t, err) { + assert.Regexp(t, "^Unable to parse the counter path", err.Error()) + } +} + +func TestNewPerfCounter(t *testing.T) { + pc, err := NewPerfCounter(`\Memory\Committed Bytes`, false) + require.NoError(t, err, "Failed to create performance counter: %v", err) + + assert.NotNil(t, pc.query) + assert.NotNil(t, pc.handle) + + // the first collection will return a zero value + var vals []win_perf_counters.CounterValue + vals, err = pc.query.GetFormattedCounterArrayDouble(pc.handle) + require.NoError(t, err) + assert.Equal(t, []win_perf_counters.CounterValue{{InstanceName: "", Value: 0}}, vals) + + err = pc.query.Close() + require.NoError(t, err, "Failed to close initialized performance counter query: %v", err) +} + +func TestNewPerfCounter_CollectOnStartup(t *testing.T) { + pc, err := NewPerfCounter(`\Memory\Committed Bytes`, true) + require.NoError(t, err, "Failed to create performance counter: %v", err) + + assert.NotNil(t, pc.query) + assert.NotNil(t, pc.handle) + + // since we collected on startup, the next collection will return a measured value + var vals []win_perf_counters.CounterValue + vals, err = pc.query.GetFormattedCounterArrayDouble(pc.handle) + require.NoError(t, err) + assert.Greater(t, vals[0].Value, float64(0)) + + err = pc.query.Close() + require.NoError(t, err, "Failed to close initialized performance counter query: %v", err) +} + +func TestPerfCounter_Close(t *testing.T) { + pc, err := NewPerfCounter(`\Memory\Committed Bytes`, false) + require.NoError(t, err) + + err = pc.Close() + require.NoError(t, err, "Failed to close initialized performance counter query: %v", err) + + err = pc.Close() + if assert.Error(t, err) { + assert.Equal(t, "uninitialised query", err.Error()) + } +} + +func TestPerfCounter_ScrapeData(t *testing.T) { + pc, err := NewPerfCounter(`\Memory\Committed Bytes`, false) + require.NoError(t, err) + + performanceCounters, err := pc.ScrapeData() + require.NoError(t, err, "Failed to scrape data: %v", err) + + assert.Equal(t, 1, len(performanceCounters)) + assert.NotNil(t, 1, performanceCounters[0]) +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/LICENSE b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/LICENSE new file mode 100644 index 0000000000000..c736b77116ce3 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2020 InfluxData Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/kernel32.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/kernel32.go new file mode 100644 index 0000000000000..9cdadedc873bd --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/kernel32.go @@ -0,0 +1,73 @@ +// Copyright (c) 2010 The win Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This is the official list of 'win' authors for copyright purposes. +// +// Alexander Neumann +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +import ( + "syscall" +) + +type SYSTEMTIME struct { + wYear uint16 + wMonth uint16 + wDayOfWeek uint16 + wDay uint16 + wHour uint16 + wMinute uint16 + wSecond uint16 + wMilliseconds uint16 +} + +type FILETIME struct { + dwLowDateTime uint32 + dwHighDateTime uint32 +} + +var ( + // Library + libkrnDll *syscall.DLL + + // Functions + krn_FileTimeToSystemTime *syscall.Proc + krn_FileTimeToLocalFileTime *syscall.Proc + krn_LocalFileTimeToFileTime *syscall.Proc + krn_WideCharToMultiByte *syscall.Proc +) + +func init() { + libkrnDll = syscall.MustLoadDLL("Kernel32.dll") + + krn_FileTimeToSystemTime = libkrnDll.MustFindProc("FileTimeToSystemTime") + krn_FileTimeToLocalFileTime = libkrnDll.MustFindProc("FileTimeToLocalFileTime") + krn_LocalFileTimeToFileTime = libkrnDll.MustFindProc("LocalFileTimeToFileTime") + krn_WideCharToMultiByte = libkrnDll.MustFindProc("WideCharToMultiByte") +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh.go new file mode 100644 index 0000000000000..d0e8d2fabb1e7 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh.go @@ -0,0 +1,501 @@ +// Copyright (c) 2010 The win Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This is the official list of 'win' authors for copyright purposes. +// +// Alexander Neumann +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +import ( + "fmt" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +// Error codes +const ( + ERROR_SUCCESS = 0 + ERROR_FAILURE = 1 + ERROR_INVALID_FUNCTION = 1 + EPOCH_DIFFERENCE_MICROS int64 = 11644473600000000 +) + +type ( + HANDLE uintptr +) + +// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h +const ( + PDH_CSTATUS_VALID_DATA = 0x00000000 // The returned data is valid. + PDH_CSTATUS_NEW_DATA = 0x00000001 // The return data value is valid and different from the last sample. + PDH_CSTATUS_NO_MACHINE = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline. + PDH_CSTATUS_NO_INSTANCE = 0x800007D1 + PDH_MORE_DATA = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'. + PDH_CSTATUS_ITEM_NOT_VALIDATED = 0x800007D3 + PDH_RETRY = 0x800007D4 + PDH_NO_DATA = 0x800007D5 // The query does not currently contain any counters (for example, limited access) + PDH_CALC_NEGATIVE_DENOMINATOR = 0x800007D6 + PDH_CALC_NEGATIVE_TIMEBASE = 0x800007D7 + PDH_CALC_NEGATIVE_VALUE = 0x800007D8 + PDH_DIALOG_CANCELLED = 0x800007D9 + PDH_END_OF_LOG_FILE = 0x800007DA + PDH_ASYNC_QUERY_TIMEOUT = 0x800007DB + PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE = 0x800007DC + PDH_CSTATUS_NO_OBJECT = 0xC0000BB8 + PDH_CSTATUS_NO_COUNTER = 0xC0000BB9 // The specified counter could not be found. + PDH_CSTATUS_INVALID_DATA = 0xC0000BBA // The counter was successfully found, but the data returned is not valid. + PDH_MEMORY_ALLOCATION_FAILURE = 0xC0000BBB + PDH_INVALID_HANDLE = 0xC0000BBC + PDH_INVALID_ARGUMENT = 0xC0000BBD // Required argument is missing or incorrect. + PDH_FUNCTION_NOT_FOUND = 0xC0000BBE + PDH_CSTATUS_NO_COUNTERNAME = 0xC0000BBF + PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path. + PDH_INVALID_BUFFER = 0xC0000BC1 + PDH_INSUFFICIENT_BUFFER = 0xC0000BC2 + PDH_CANNOT_CONNECT_MACHINE = 0xC0000BC3 + PDH_INVALID_PATH = 0xC0000BC4 + PDH_INVALID_INSTANCE = 0xC0000BC5 + PDH_INVALID_DATA = 0xC0000BC6 // specified counter does not contain valid data or a successful status code. + PDH_NO_DIALOG_DATA = 0xC0000BC7 + PDH_CANNOT_READ_NAME_STRINGS = 0xC0000BC8 + PDH_LOG_FILE_CREATE_ERROR = 0xC0000BC9 + PDH_LOG_FILE_OPEN_ERROR = 0xC0000BCA + PDH_LOG_TYPE_NOT_FOUND = 0xC0000BCB + PDH_NO_MORE_DATA = 0xC0000BCC + PDH_ENTRY_NOT_IN_LOG_FILE = 0xC0000BCD + PDH_DATA_SOURCE_IS_LOG_FILE = 0xC0000BCE + PDH_DATA_SOURCE_IS_REAL_TIME = 0xC0000BCF + PDH_UNABLE_READ_LOG_HEADER = 0xC0000BD0 + PDH_FILE_NOT_FOUND = 0xC0000BD1 + PDH_FILE_ALREADY_EXISTS = 0xC0000BD2 + PDH_NOT_IMPLEMENTED = 0xC0000BD3 + PDH_STRING_NOT_FOUND = 0xC0000BD4 + PDH_UNABLE_MAP_NAME_FILES = 0x80000BD5 + PDH_UNKNOWN_LOG_FORMAT = 0xC0000BD6 + PDH_UNKNOWN_LOGSVC_COMMAND = 0xC0000BD7 + PDH_LOGSVC_QUERY_NOT_FOUND = 0xC0000BD8 + PDH_LOGSVC_NOT_OPENED = 0xC0000BD9 + PDH_WBEM_ERROR = 0xC0000BDA + PDH_ACCESS_DENIED = 0xC0000BDB + PDH_LOG_FILE_TOO_SMALL = 0xC0000BDC + PDH_INVALID_DATASOURCE = 0xC0000BDD + PDH_INVALID_SQLDB = 0xC0000BDE + PDH_NO_COUNTERS = 0xC0000BDF + PDH_SQL_ALLOC_FAILED = 0xC0000BE0 + PDH_SQL_ALLOCCON_FAILED = 0xC0000BE1 + PDH_SQL_EXEC_DIRECT_FAILED = 0xC0000BE2 + PDH_SQL_FETCH_FAILED = 0xC0000BE3 + PDH_SQL_ROWCOUNT_FAILED = 0xC0000BE4 + PDH_SQL_MORE_RESULTS_FAILED = 0xC0000BE5 + PDH_SQL_CONNECT_FAILED = 0xC0000BE6 + PDH_SQL_BIND_FAILED = 0xC0000BE7 + PDH_CANNOT_CONNECT_WMI_SERVER = 0xC0000BE8 + PDH_PLA_COLLECTION_ALREADY_RUNNING = 0xC0000BE9 + PDH_PLA_ERROR_SCHEDULE_OVERLAP = 0xC0000BEA + PDH_PLA_COLLECTION_NOT_FOUND = 0xC0000BEB + PDH_PLA_ERROR_SCHEDULE_ELAPSED = 0xC0000BEC + PDH_PLA_ERROR_NOSTART = 0xC0000BED + PDH_PLA_ERROR_ALREADY_EXISTS = 0xC0000BEE + PDH_PLA_ERROR_TYPE_MISMATCH = 0xC0000BEF + PDH_PLA_ERROR_FILEPATH = 0xC0000BF0 + PDH_PLA_SERVICE_ERROR = 0xC0000BF1 + PDH_PLA_VALIDATION_ERROR = 0xC0000BF2 + PDH_PLA_VALIDATION_WARNING = 0x80000BF3 + PDH_PLA_ERROR_NAME_TOO_LONG = 0xC0000BF4 + PDH_INVALID_SQL_LOG_FORMAT = 0xC0000BF5 + PDH_COUNTER_ALREADY_IN_QUERY = 0xC0000BF6 + PDH_BINARY_LOG_CORRUPT = 0xC0000BF7 + PDH_LOG_SAMPLE_TOO_SMALL = 0xC0000BF8 + PDH_OS_LATER_VERSION = 0xC0000BF9 + PDH_OS_EARLIER_VERSION = 0xC0000BFA + PDH_INCORRECT_APPEND_TIME = 0xC0000BFB + PDH_UNMATCHED_APPEND_COUNTER = 0xC0000BFC + PDH_SQL_ALTER_DETAIL_FAILED = 0xC0000BFD + PDH_QUERY_PERF_DATA_TIMEOUT = 0xC0000BFE +) + +// Formatting options for GetFormattedCounterValue(). +const ( + PDH_FMT_RAW = 0x00000010 + PDH_FMT_ANSI = 0x00000020 + PDH_FMT_UNICODE = 0x00000040 + PDH_FMT_LONG = 0x00000100 // Return data as a long int. + PDH_FMT_DOUBLE = 0x00000200 // Return data as a double precision floating point real. + PDH_FMT_LARGE = 0x00000400 // Return data as a 64 bit integer. + PDH_FMT_NOSCALE = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor. + PDH_FMT_1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000. + PDH_FMT_NODATA = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing. + PDH_FMT_NOCAP100 = 0x00008000 // can be OR-ed: do not cap values > 100. + PERF_DETAIL_COSTLY = 0x00010000 + PERF_DETAIL_STANDARD = 0x0000FFFF +) + +type ( + PDH_HQUERY HANDLE // query handle + PDH_HCOUNTER HANDLE // counter handle +) + +var ( + // Library + libpdhDll *syscall.DLL + + // Functions + pdh_AddCounterW *syscall.Proc + pdh_AddEnglishCounterW *syscall.Proc + pdh_CloseQuery *syscall.Proc + pdh_CollectQueryData *syscall.Proc + pdh_CollectQueryDataWithTime *syscall.Proc + pdh_GetFormattedCounterValue *syscall.Proc + pdh_GetFormattedCounterArrayW *syscall.Proc + pdh_OpenQuery *syscall.Proc + pdh_ValidatePathW *syscall.Proc + pdh_ExpandWildCardPathW *syscall.Proc + pdh_GetCounterInfoW *syscall.Proc +) + +func init() { + // Library + libpdhDll = syscall.MustLoadDLL("pdh.dll") + + // Functions + pdh_AddCounterW = libpdhDll.MustFindProc("PdhAddCounterW") + pdh_AddEnglishCounterW, _ = libpdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista. + pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery") + pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData") + pdh_CollectQueryDataWithTime, _ = libpdhDll.FindProc("PdhCollectQueryDataWithTime") + pdh_GetFormattedCounterValue = libpdhDll.MustFindProc("PdhGetFormattedCounterValue") + pdh_GetFormattedCounterArrayW = libpdhDll.MustFindProc("PdhGetFormattedCounterArrayW") + pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery") + pdh_ValidatePathW = libpdhDll.MustFindProc("PdhValidatePathW") + pdh_ExpandWildCardPathW = libpdhDll.MustFindProc("PdhExpandWildCardPathW") + pdh_GetCounterInfoW = libpdhDll.MustFindProc("PdhGetCounterInfoW") +} + +// PdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the +// function PdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by PdhOpenQuery. +// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version). +// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value +// later, call PdhGetCounterInfo() and access dwQueryUserData of the PDH_COUNTER_INFO structure. +// +// Examples of szFullCounterPath (in an English version of Windows): +// +// \\Processor(_Total)\\% Idle Time +// \\Processor(_Total)\\% Processor Time +// \\LogicalDisk(C:)\% Free Space +// +// To view all (internationalized...) counters on a system, there are three non-programmatic ways: perfmon utility, +// the typeperf command, and the the registry editor. perfmon.exe is perhaps the easiest way, because it's basically a +// full implemention of the pdh.dll API, except with a GUI and all that. The registry setting also provides an +// interface to the available counters, and can be found at the following key: +// +// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage +// +// This registry key contains several values as follows: +// +// 1 +// 1847 +// 2 +// System +// 4 +// Memory +// 6 +// % Processor Time +// ... many, many more +// +// Somehow, these numeric values can be used as szFullCounterPath too: +// +// \2\6 will correspond to \\System\% Processor Time +// +// The typeperf command may also be pretty easy. To find all performance counters, simply execute: +// +// typeperf -qx +func PdhAddCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 { + ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath) + ret, _, _ := pdh_AddCounterW.Call( + uintptr(hQuery), + uintptr(unsafe.Pointer(ptxt)), + dwUserData, + uintptr(unsafe.Pointer(phCounter))) + + return uint32(ret) +} + +// PdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll. +// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems + +func PdhAddEnglishCounterSupported() bool { + return pdh_AddEnglishCounterW != nil +} + +// PdhAddEnglishCounter adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on +// Windows versions higher than Vista. +func PdhAddEnglishCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 { + if pdh_AddEnglishCounterW == nil { + return ERROR_INVALID_FUNCTION + } + + ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath) + ret, _, _ := pdh_AddEnglishCounterW.Call( + uintptr(hQuery), + uintptr(unsafe.Pointer(ptxt)), + dwUserData, + uintptr(unsafe.Pointer(phCounter))) + + return uint32(ret) +} + +// PdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query, +// and frees all memory associated with the query. +func PdhCloseQuery(hQuery PDH_HQUERY) uint32 { + ret, _, _ := pdh_CloseQuery.Call(uintptr(hQuery)) + + return uint32(ret) +} + +// Collects the current raw data value for all counters in the specified query and updates the status +// code of each counter. With some counters, this function needs to be repeatedly called before the value +// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code +// requires at least two calls: +// +// var handle win.PDH_HQUERY +// var counterHandle win.PDH_HCOUNTER +// ret := win.PdhOpenQuery(0, 0, &handle) +// ret = win.PdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle) +// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE +// +// ret = win.PdhCollectQueryData(handle) +// fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA +// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp) +// +// ret = win.PdhCollectQueryData(handle) +// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS +// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp) +// +// The PdhCollectQueryData will return an error in the first call because it needs two values for +// displaying the correct data for the processor idle time. The second call will have a 0 return code. +func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 { + ret, _, _ := pdh_CollectQueryData.Call(uintptr(hQuery)) + + return uint32(ret) +} + +// PdhCollectQueryDataWithTime queries data from perfmon, retrieving the device/windows timestamp from the node it was collected on. +// Converts the filetime structure to a GO time class and returns the native time. +// +func PdhCollectQueryDataWithTime(hQuery PDH_HQUERY) (uint32, time.Time) { + var localFileTime FILETIME + ret, _, _ := pdh_CollectQueryDataWithTime.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime))) + + if ret == ERROR_SUCCESS { + var utcFileTime FILETIME + ret, _, _ := krn_LocalFileTimeToFileTime.Call( + uintptr(unsafe.Pointer(&localFileTime)), + uintptr(unsafe.Pointer(&utcFileTime))) + + if ret == 0 { + return uint32(ERROR_FAILURE), time.Now() + } + + // First convert 100-ns intervals to microseconds, then adjust for the + // epoch difference + var totalMicroSeconds int64 + totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10 + totalMicroSeconds -= EPOCH_DIFFERENCE_MICROS + + retTime := time.Unix(0, totalMicroSeconds*1000) + + return uint32(ERROR_SUCCESS), retTime + } + + return uint32(ret), time.Now() +} + +// PdhGetFormattedCounterValueDouble formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue. +// This function does not directly translate to a Windows counterpart due to union specialization tricks. +func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_DOUBLE) uint32 { + ret, _, _ := pdh_GetFormattedCounterValue.Call( + uintptr(hCounter), + uintptr(PDH_FMT_DOUBLE|PDH_FMT_NOCAP100), + uintptr(unsafe.Pointer(lpdwType)), + uintptr(unsafe.Pointer(pValue))) + + return uint32(ret) +} + +// PdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a +// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE. +// An example of how this function can be used: +// +// okPath := "\\Process(*)\\% Processor Time" // notice the wildcard * character +// +// // omitted all necessary stuff ... +// +// var bufSize uint32 +// var bufCount uint32 +// var size uint32 = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{})) +// var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr. +// +// for { +// // collect +// ret := win.PdhCollectQueryData(queryHandle) +// if ret == win.ERROR_SUCCESS { +// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. +// if ret == win.PDH_MORE_DATA { +// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size) +// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0]) +// for i := 0; i < int(bufCount); i++ { +// c := filledBuf[i] +// var s string = win.UTF16PtrToString(c.SzName) +// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue) +// } +// +// filledBuf = nil +// // Need to at least set bufSize to zero, because if not, the function will not +// // return PDH_MORE_DATA and will not set the bufSize. +// bufCount = 0 +// bufSize = 0 +// } +// +// time.Sleep(2000 * time.Millisecond) +// } +// } +func PdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *byte) uint32 { + ret, _, _ := pdh_GetFormattedCounterArrayW.Call( + uintptr(hCounter), + uintptr(PDH_FMT_DOUBLE|PDH_FMT_NOCAP100), + uintptr(unsafe.Pointer(lpdwBufferSize)), + uintptr(unsafe.Pointer(lpdwBufferCount)), + uintptr(unsafe.Pointer(itemBuffer))) + + return uint32(ret) +} + +// PdhOpenQuery creates a new query that is used to manage the collection of performance data. +// szDataSource is a null terminated string that specifies the name of the log file from which to +// retrieve the performance data. If 0, performance data is collected from a real-time data source. +// dwUserData is a user-defined value to associate with this query. To retrieve the user data later, +// call PdhGetCounterInfo and access dwQueryUserData of the PDH_COUNTER_INFO structure. phQuery is +// the handle to the query, and must be used in subsequent calls. This function returns a PDH_ +// constant error code, or ERROR_SUCCESS if the call succeeded. +func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *PDH_HQUERY) uint32 { + ret, _, _ := pdh_OpenQuery.Call( + szDataSource, + dwUserData, + uintptr(unsafe.Pointer(phQuery))) + + return uint32(ret) +} + +//PdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path which contains wildcard characters. +//The general counter path format is as follows: +// +//\\computer\object(parent/instance#index)\counter +// +//The parent, instance, index, and counter components of the counter path may contain either a valid name or a wildcard character. The computer, parent, instance, +// and index components are not necessary for all counters. +// +//The following is a list of the possible formats: +// +//\\computer\object(parent/instance#index)\counter +//\\computer\object(parent/instance)\counter +//\\computer\object(instance#index)\counter +//\\computer\object(instance)\counter +//\\computer\object\counter +//\object(parent/instance#index)\counter +//\object(parent/instance)\counter +//\object(instance#index)\counter +//\object(instance)\counter +//\object\counter +//Use an asterisk (*) as the wildcard character, for example, \object(*)\counter. +// +//If a wildcard character is specified in the parent name, all instances of the specified object that match the specified instance and counter fields will be returned. +// For example, \object(*/instance)\counter. +// +//If a wildcard character is specified in the instance name, all instances of the specified object and parent object will be returned if all instance names +// corresponding to the specified index match the wildcard character. For example, \object(parent/*)\counter. If the object does not contain an instance, an error occurs. +// +//If a wildcard character is specified in the counter name, all counters of the specified object are returned. +// +//Partial counter path string matches (for example, "pro*") are supported. +func PdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 { + ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath) + flags := uint32(0) // expand instances and counters + ret, _, _ := pdh_ExpandWildCardPathW.Call( + uintptr(unsafe.Pointer(nil)), // search counters on local computer + uintptr(unsafe.Pointer(ptxt)), + uintptr(unsafe.Pointer(mszExpandedPathList)), + uintptr(unsafe.Pointer(pcchPathListLength)), + uintptr(unsafe.Pointer(&flags))) + + return uint32(ret) +} + +// PdhValidatePath validates a path. Will return ERROR_SUCCESS when ok, or PDH_CSTATUS_BAD_COUNTERNAME when the path is +// erroneous. +func PdhValidatePath(path string) uint32 { + ptxt, _ := syscall.UTF16PtrFromString(path) + ret, _, _ := pdh_ValidatePathW.Call(uintptr(unsafe.Pointer(ptxt))) + + return uint32(ret) +} + +func PdhFormatError(msgId uint32) string { + var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS + buf := make([]uint16, 300) + _, err := windows.FormatMessage(flags, uintptr(libpdhDll.Handle), msgId, 0, buf, nil) + if err == nil { + return fmt.Sprintf("%s", UTF16PtrToString(&buf[0])) + } + return fmt.Sprintf("(pdhErr=%d) %s", msgId, err.Error()) +} + +//Retrieves information about a counter, such as data size, counter type, path, and user-supplied data values +//hCounter [in] +//Handle of the counter from which you want to retrieve information. The PdhAddCounter function returns this handle. +// +//bRetrieveExplainText [in] +//Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved. If you set this parameter to FALSE, the field in the returned buffer is NULL. +// +//pdwBufferSize [in, out] +//Size of the lpBuffer buffer, in bytes. If zero on input, the function returns PDH_MORE_DATA and sets this parameter to the required buffer size. If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used. If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer. +// +//lpBuffer [out] +//Caller-allocated buffer that receives a PDH_COUNTER_INFO structure. The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure. This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero. +func PdhGetCounterInfo(hCounter PDH_HCOUNTER, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 { + ret, _, _ := pdh_GetCounterInfoW.Call( + uintptr(hCounter), + uintptr(bRetrieveExplainText), + uintptr(unsafe.Pointer(pdwBufferSize)), + uintptr(unsafe.Pointer(lpBuffer))) + + return uint32(ret) +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_386.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_386.go new file mode 100644 index 0000000000000..134d15c8d1461 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_386.go @@ -0,0 +1,121 @@ +// Copyright (c) 2010 The win Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This is the official list of 'win' authors for copyright purposes. +// +// Alexander Neumann +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +// Union specialization for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + padding [4]byte + DoubleValue float64 +} + +// Union specialization for 64 bit integer values +type PDH_FMT_COUNTERVALUE_LARGE struct { + CStatus uint32 + padding [4]byte + LargeValue int64 +} + +// Union specialization for long values +type PDH_FMT_COUNTERVALUE_LONG struct { + CStatus uint32 + LongValue int32 + padding [4]byte +} + +type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { + SzName *uint16 + padding [4]byte + FmtValue PDH_FMT_COUNTERVALUE_DOUBLE +} + +// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge() +type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct { + SzName *uint16 // pointer to a string + padding [4]byte + FmtValue PDH_FMT_COUNTERVALUE_LARGE +} + +// Union specialization for long values, used by PdhGetFormattedCounterArrayLong() +type PDH_FMT_COUNTERVALUE_ITEM_LONG struct { + SzName *uint16 // pointer to a string + padding [4]byte + FmtValue PDH_FMT_COUNTERVALUE_LONG +} + +//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. + DwLength uint32 + //Counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit. + //The counter type constants are defined in Winperf.h. + DwType uint32 + //Counter version information. Not used. + CVersion uint32 + //Counter status that indicates if the counter value is valid. For a list of possible values, + //see Checking PDH Interface Return Values. + CStatus uint32 + //Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten. + //The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to + //PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned + LScale int32 + //Default scale factor as suggested by the counter's provider. + LDefaultScale int32 + //The value passed in the dwUserData parameter when calling PdhAddCounter. + DwUserData *uint32 + //The value passed in the dwUserData parameter when calling PdhOpenQuery. + DwQueryUserData *uint32 + //Null-terminated string that specifies the full counter path. The string follows this structure in memory. + SzFullPath *uint16 // pointer to a string + //Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer. + //The string follows this structure in memory. + SzMachineName *uint16 // pointer to a string + //Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory. + SzObjectName *uint16 // pointer to a string + //Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance. + //The string follows this structure in memory. + SzInstanceName *uint16 // pointer to a string + //Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL, if the path does not specify a parent instance. + //The string follows this structure in memory. + SzParentInstance *uint16 // pointer to a string + //Instance index specified in the counter path. Is 0, if the path does not specify an instance index. + DwInstanceIndex uint32 // pointer to a string + //Null-terminated string that contains the counter name. The string follows this structure in memory. + SzCounterName *uint16 // pointer to a string + //padding + Padding [4]byte + //Help text that describes the counter. Is NULL if the source is a log file. + SzExplainText *uint16 // pointer to a string + //Start of the string data that is appended to the structure. + DataBuffer [1]uint32 // pointer to an extra space +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_amd64.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_amd64.go new file mode 100644 index 0000000000000..ff3b39335bcd4 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/pdh_amd64.go @@ -0,0 +1,114 @@ +// Copyright (c) 2010 The win Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This is the official list of 'win' authors for copyright purposes. +// +// Alexander Neumann +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +// Union specialization for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + DoubleValue float64 +} + +// Union specialization for 64 bit integer values +type PDH_FMT_COUNTERVALUE_LARGE struct { + CStatus uint32 + LargeValue int64 +} + +// Union specialization for long values +type PDH_FMT_COUNTERVALUE_LONG struct { + CStatus uint32 + LongValue int32 + padding [4]byte +} + +type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { + SzName *uint16 + FmtValue PDH_FMT_COUNTERVALUE_DOUBLE +} + +// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge() +type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct { + SzName *uint16 // pointer to a string + FmtValue PDH_FMT_COUNTERVALUE_LARGE +} + +// Union specialization for long values, used by PdhGetFormattedCounterArrayLong() +type PDH_FMT_COUNTERVALUE_ITEM_LONG struct { + SzName *uint16 // pointer to a string + FmtValue PDH_FMT_COUNTERVALUE_LONG +} + +//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. + DwLength uint32 + //Counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit. + //The counter type constants are defined in Winperf.h. + DwType uint32 + //Counter version information. Not used. + CVersion uint32 + //Counter status that indicates if the counter value is valid. For a list of possible values, + //see Checking PDH Interface Return Values. + CStatus uint32 + //Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten. + //The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to + //PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned + LScale int32 + //Default scale factor as suggested by the counter's provider. + LDefaultScale int32 + //The value passed in the dwUserData parameter when calling PdhAddCounter. + DwUserData *uint32 + //The value passed in the dwUserData parameter when calling PdhOpenQuery. + DwQueryUserData *uint32 + //Null-terminated string that specifies the full counter path. The string follows this structure in memory. + SzFullPath *uint16 // pointer to a string + //Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer. + //The string follows this structure in memory. + SzMachineName *uint16 // pointer to a string + //Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory. + SzObjectName *uint16 // pointer to a string + //Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance. + //The string follows this structure in memory. + SzInstanceName *uint16 // pointer to a string + //Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL, if the path does not specify a parent instance. + //The string follows this structure in memory. + SzParentInstance *uint16 // pointer to a string + //Instance index specified in the counter path. Is 0, if the path does not specify an instance index. + DwInstanceIndex uint32 // pointer to a string + //Null-terminated string that contains the counter name. The string follows this structure in memory. + SzCounterName *uint16 // pointer to a string + //Help text that describes the counter. Is NULL if the source is a log file. + SzExplainText *uint16 // pointer to a string + //Start of the string data that is appended to the structure. + DataBuffer [1]uint32 // pointer to an extra space +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/performance_query.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/performance_query.go new file mode 100644 index 0000000000000..ce247a495a7ea --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/performance_query.go @@ -0,0 +1,231 @@ +// Go API over pdh syscalls +// +build windows + +package win_perf_counters + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +//PerformanceQuery is abstraction for PDH_FMT_COUNTERVALUE_ITEM_DOUBLE +type CounterValue struct { + InstanceName string + Value float64 +} + +//PerformanceQuery provides wrappers around Windows performance counters API for easy usage in GO +type PerformanceQuery interface { + Open() error + Close() error + AddCounterToQuery(counterPath string) (PDH_HCOUNTER, error) + AddEnglishCounterToQuery(counterPath string) (PDH_HCOUNTER, error) + GetCounterPath(counterHandle PDH_HCOUNTER) (string, error) + ExpandWildCardPath(counterPath string) ([]string, error) + GetFormattedCounterValueDouble(hCounter PDH_HCOUNTER) (float64, error) + GetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER) ([]CounterValue, error) + CollectData() error + CollectDataWithTime() (time.Time, error) + IsVistaOrNewer() bool +} + +//PdhError represents error returned from Performance Counters API +type PdhError struct { + ErrorCode uint32 + errorText string +} + +func (m *PdhError) Error() string { + return m.errorText +} + +func NewPdhError(code uint32) error { + return &PdhError{ + ErrorCode: code, + errorText: PdhFormatError(code), + } +} + +//PerformanceQueryImpl is implementation of PerformanceQuery interface, which calls phd.dll functions +type PerformanceQueryImpl struct { + query PDH_HQUERY +} + +// Open creates a new counterPath that is used to manage the collection of performance data. +// It returns counterPath handle used for subsequent calls for adding counters and querying data +func (m *PerformanceQueryImpl) Open() error { + if m.query != 0 { + err := m.Close() + if err != nil { + return err + } + } + var handle PDH_HQUERY + + if ret := PdhOpenQuery(0, 0, &handle); ret != ERROR_SUCCESS { + return NewPdhError(ret) + } + m.query = handle + return nil +} + +// Close closes the counterPath, releases associated counter handles and frees resources +func (m *PerformanceQueryImpl) Close() error { + if m.query == 0 { + return errors.New("uninitialised query") + } + + if ret := PdhCloseQuery(m.query); ret != ERROR_SUCCESS { + return NewPdhError(ret) + } + m.query = 0 + return nil +} + +func (m *PerformanceQueryImpl) AddCounterToQuery(counterPath string) (PDH_HCOUNTER, error) { + var counterHandle PDH_HCOUNTER + if m.query == 0 { + return 0, errors.New("uninitialised query") + } + + if ret := PdhAddCounter(m.query, counterPath, 0, &counterHandle); ret != ERROR_SUCCESS { + return 0, NewPdhError(ret) + } + return counterHandle, nil +} + +func (m *PerformanceQueryImpl) AddEnglishCounterToQuery(counterPath string) (PDH_HCOUNTER, error) { + var counterHandle PDH_HCOUNTER + if m.query == 0 { + return 0, errors.New("uninitialised query") + } + if ret := PdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle); ret != ERROR_SUCCESS { + return 0, NewPdhError(ret) + } + return counterHandle, nil +} + +//GetCounterPath return counter information for given handle +func (m *PerformanceQueryImpl) GetCounterPath(counterHandle PDH_HCOUNTER) (string, error) { + var bufSize uint32 + var buff []byte + var ret uint32 + if ret = PdhGetCounterInfo(counterHandle, 0, &bufSize, nil); ret == PDH_MORE_DATA { + buff = make([]byte, bufSize) + bufSize = uint32(len(buff)) + if ret = PdhGetCounterInfo(counterHandle, 0, &bufSize, &buff[0]); ret == ERROR_SUCCESS { + ci := (*PDH_COUNTER_INFO)(unsafe.Pointer(&buff[0])) + return UTF16PtrToString(ci.SzFullPath), nil + } + } + return "", NewPdhError(ret) +} + +// ExpandWildCardPath examines local computer and returns those counter paths that match the given counter path which contains wildcard characters. +func (m *PerformanceQueryImpl) ExpandWildCardPath(counterPath string) ([]string, error) { + var bufSize uint32 + var buff []uint16 + var ret uint32 + + if ret = PdhExpandWildCardPath(counterPath, nil, &bufSize); ret == PDH_MORE_DATA { + buff = make([]uint16, bufSize) + bufSize = uint32(len(buff)) + ret = PdhExpandWildCardPath(counterPath, &buff[0], &bufSize) + if ret == ERROR_SUCCESS { + list := UTF16ToStringArray(buff) + return list, nil + } + } + return nil, NewPdhError(ret) +} + +//GetFormattedCounterValueDouble computes a displayable value for the specified counter +func (m *PerformanceQueryImpl) GetFormattedCounterValueDouble(hCounter PDH_HCOUNTER) (float64, error) { + var counterType uint32 + var value PDH_FMT_COUNTERVALUE_DOUBLE + var ret uint32 + + if ret = PdhGetFormattedCounterValueDouble(hCounter, &counterType, &value); ret == ERROR_SUCCESS { + if value.CStatus == PDH_CSTATUS_VALID_DATA || value.CStatus == PDH_CSTATUS_NEW_DATA { + return value.DoubleValue, nil + } else { + return 0, NewPdhError(value.CStatus) + } + } else { + return 0, NewPdhError(ret) + } +} + +func (m *PerformanceQueryImpl) GetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER) ([]CounterValue, error) { + var buffSize uint32 + var itemCount uint32 + var ret uint32 + + if ret = PdhGetFormattedCounterArrayDouble(hCounter, &buffSize, &itemCount, nil); ret == PDH_MORE_DATA { + buff := make([]byte, buffSize) + + if ret = PdhGetFormattedCounterArrayDouble(hCounter, &buffSize, &itemCount, &buff[0]); ret == ERROR_SUCCESS { + items := (*[1 << 20]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE)(unsafe.Pointer(&buff[0]))[:itemCount] + values := make([]CounterValue, 0, itemCount) + for _, item := range items { + if item.FmtValue.CStatus == PDH_CSTATUS_VALID_DATA || item.FmtValue.CStatus == PDH_CSTATUS_NEW_DATA { + val := CounterValue{UTF16PtrToString(item.SzName), item.FmtValue.DoubleValue} + values = append(values, val) + } + } + return values, nil + } + } + return nil, NewPdhError(ret) +} + +func (m *PerformanceQueryImpl) CollectData() error { + var ret uint32 + if m.query == 0 { + return errors.New("uninitialised query") + } + + if ret = PdhCollectQueryData(m.query); ret != ERROR_SUCCESS { + return NewPdhError(ret) + } + return nil +} + +func (m *PerformanceQueryImpl) CollectDataWithTime() (time.Time, error) { + if m.query == 0 { + return time.Now(), errors.New("uninitialised query") + } + ret, mtime := PdhCollectQueryDataWithTime(m.query) + if ret != ERROR_SUCCESS { + return time.Now(), NewPdhError(ret) + } + return mtime, nil +} + +func (m *PerformanceQueryImpl) IsVistaOrNewer() bool { + return PdhAddEnglishCounterSupported() +} + +// UTF16PtrToString converts Windows API LPTSTR (pointer to string) to go string +func UTF16PtrToString(s *uint16) string { + if s == nil { + return "" + } + return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:]) +} + +// UTF16ToStringArray converts list of Windows API NULL terminated strings to go string array +func UTF16ToStringArray(buf []uint16) []string { + var strings []string + nextLineStart := 0 + stringLine := UTF16PtrToString(&buf[0]) + for stringLine != "" { + strings = append(strings, stringLine) + nextLineStart += len([]rune(stringLine)) + 1 + remainingBuf := buf[nextLineStart:] + stringLine = UTF16PtrToString(&remainingBuf[0]) + } + return strings +} diff --git a/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/win_perf_counters_notwindows.go b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/win_perf_counters_notwindows.go new file mode 100644 index 0000000000000..427f5d5461ff3 --- /dev/null +++ b/receiver/windowsperfcountersreceiver/internal/third_party/telegraf/win_perf_counters/win_perf_counters_notwindows.go @@ -0,0 +1,3 @@ +// +build !windows + +package win_perf_counters