From 9bdb5db6d6feeeabaa0e0e6df6fc0d052af133d8 Mon Sep 17 00:00:00 2001 From: braydonk Date: Fri, 21 Mar 2025 13:58:40 +0000 Subject: [PATCH 1/7] vendor perflib in perfcounterscraper --- receiver/hostmetricsreceiver/go.mod | 5 +- receiver/hostmetricsreceiver/go.sum | 10 +- .../perfcounters/internal/perflib/LICENSE | 21 + .../internal/perflib/collector.go | 175 +++++++ .../perfcounters/internal/perflib/errors.go | 5 + .../internal/perflib/nametable.go | 86 +++ .../internal/perflib/pdh_const.go | 63 +++ .../perfcounters/internal/perflib/perflib.go | 492 ++++++++++++++++++ .../internal/perflib/perflib_test.go | 11 + .../internal/perflib/raw_types.go | 173 ++++++ .../perfcounters/internal/perflib/utf16.go | 49 ++ .../perfcounters/internal/perflib/utils.go | 9 + .../perfcounters/perfcounter_scraper.go | 5 +- 13 files changed, 1089 insertions(+), 15 deletions(-) create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go create mode 100644 receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go diff --git a/receiver/hostmetricsreceiver/go.mod b/receiver/hostmetricsreceiver/go.mod index 5d3eff80353e1..285f131331354 100644 --- a/receiver/hostmetricsreceiver/go.mod +++ b/receiver/hostmetricsreceiver/go.mod @@ -8,7 +8,6 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter v0.123.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.123.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.123.0 - github.com/prometheus-community/windows_exporter v0.27.2 github.com/prometheus/procfs v0.16.0 github.com/shirou/gopsutil/v4 v4.25.3 github.com/stretchr/testify v1.10.0 @@ -51,8 +50,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.8.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -63,7 +60,7 @@ require ( github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.2 // indirect diff --git a/receiver/hostmetricsreceiver/go.sum b/receiver/hostmetricsreceiver/go.sum index 8014db66e6e64..8a86cb63d4c72 100644 --- a/receiver/hostmetricsreceiver/go.sum +++ b/receiver/hostmetricsreceiver/go.sum @@ -33,10 +33,6 @@ github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -67,8 +63,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= @@ -116,8 +112,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus-community/windows_exporter v0.27.2 h1:/tdRTouPMVsC4qt8+s9NOPEm7L/9qdDxmasiETlx+Wk= -github.com/prometheus-community/windows_exporter v0.27.2/go.mod h1:8+T6hfv71nvgVIzguouXkIGoa15ni+uXHHULBOA2bZo= github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE new file mode 100644 index 0000000000000..169f2ab94741b --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Leopold Schabel / The perflib_exporter authors + +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/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go new file mode 100644 index 0000000000000..f9baf8b822db0 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go @@ -0,0 +1,175 @@ +package perflib + +import ( + "fmt" + "reflect" + "strings" +) + +type Collector struct { + object string + query string + + counters map[string]Counter + nameIndexValue int +} + +type Counter struct { + Name string + Desc string + Instances map[string]uint32 + Type uint32 + Frequency float64 + + FieldIndexValue int + FieldIndexSecondValue int +} + +func NewCollector[T any](object string, _ []string) (*Collector, error) { + collector := &Collector{ + object: object, + query: MapCounterToIndex(object), + nameIndexValue: -1, + counters: make(map[string]Counter), + } + + var values [0]T + valueType := reflect.TypeOf(values).Elem() + + if f, ok := valueType.FieldByName("Name"); ok { + if f.Type.Kind() == reflect.String { + collector.nameIndexValue = f.Index[0] + } + } + + for _, f := range reflect.VisibleFields(valueType) { + counterName, ok := f.Tag.Lookup("perfdata") + if !ok { + continue + } + + var counter Counter + if counter, ok = collector.counters[counterName]; !ok { + counter = Counter{ + Name: counterName, + FieldIndexSecondValue: -1, + FieldIndexValue: -1, + } + } + + if strings.HasSuffix(counterName, ",secondvalue") { + counterName = strings.TrimSuffix(counterName, ",secondvalue") + + counter.FieldIndexSecondValue = f.Index[0] + } else { + counter.FieldIndexValue = f.Index[0] + } + + collector.counters[counterName] = counter + } + + var collectValues []T + + if err := collector.Collect(&collectValues); err != nil { + return nil, fmt.Errorf("failed to collect initial data: %w", err) + } + + return collector, nil +} + +func (c *Collector) Describe() map[string]string { + return map[string]string{} +} + +func (c *Collector) Collect(data any) error { + dv := reflect.ValueOf(data) + if dv.Kind() != reflect.Ptr || dv.IsNil() { + return ErrInvalidEntityType + } + + dv = dv.Elem() + + elemType := dv.Type().Elem() + elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem() + + if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct { + return ErrInvalidEntityType + } + + perfObjects, err := QueryPerformanceData(c.query, c.object) + if err != nil { + return fmt.Errorf("QueryPerformanceData: %w", err) + } + + if len(perfObjects) == 0 || perfObjects[0] == nil || len(perfObjects[0].Instances) == 0 { + return nil + } + + if dv.Len() != 0 { + dv.Set(reflect.MakeSlice(dv.Type(), 0, len(perfObjects[0].Instances))) + } + + dv.Clear() + + for _, perfObject := range perfObjects { + if perfObject.Name != c.object { + continue + } + + for _, perfInstance := range perfObject.Instances { + instanceName := perfInstance.Name + if strings.HasSuffix(instanceName, "_Total") { + continue + } + + if instanceName == "" || instanceName == "*" { + instanceName = InstanceEmpty + } + + if c.nameIndexValue != -1 { + elemValue.Field(c.nameIndexValue).SetString(instanceName) + } + + dv.Set(reflect.Append(dv, elemValue)) + index := dv.Len() - 1 + + for _, perfCounter := range perfInstance.Counters { + if perfCounter.Def.IsBaseValue && !perfCounter.Def.IsNanosecondCounter { + continue + } + + counter, ok := c.counters[perfCounter.Def.Name] + if !ok { + continue + } + + switch perfCounter.Def.CounterType { + case PERF_ELAPSED_TIME: + dv.Index(index). + Field(counter.FieldIndexValue). + SetFloat(float64((perfCounter.Value - WindowsEpoch) / perfObject.Frequency)) + case PERF_100NSEC_TIMER, PERF_PRECISION_100NS_TIMER: + dv.Index(index). + Field(counter.FieldIndexValue). + SetFloat(float64(perfCounter.Value) * TicksToSecondScaleFactor) + default: + if counter.FieldIndexSecondValue != -1 { + dv.Index(index). + Field(counter.FieldIndexSecondValue). + SetFloat(float64(perfCounter.SecondValue)) + } + + if counter.FieldIndexValue != -1 { + dv.Index(index). + Field(counter.FieldIndexValue). + SetFloat(float64(perfCounter.Value)) + } + } + } + } + } + + return nil +} + +func (c *Collector) Close() {} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go new file mode 100644 index 0000000000000..1cba8aabee3b5 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go @@ -0,0 +1,5 @@ +package perflib + +import "errors" + +var ErrInvalidEntityType = errors.New("invalid entity type") diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go new file mode 100644 index 0000000000000..22dc079ace44e --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go @@ -0,0 +1,86 @@ +package perflib + +import ( + "bytes" + "fmt" + "strconv" + "sync" +) + +// CounterNameTable Initialize global name tables +// profiling, add option to disable name tables if necessary +// Not sure if we should resolve the names at all or just have the caller do it on demand +// (for many use cases the index is sufficient) +// +//nolint:gochecknoglobals +var CounterNameTable = *QueryNameTable("Counter 009") + +func (p *perfObjectType) LookupName() string { + return CounterNameTable.LookupString(p.ObjectNameTitleIndex) +} + +type NameTable struct { + once sync.Once + + name string + + table struct { + index map[uint32]string + string map[string]uint32 + } +} + +func (t *NameTable) LookupString(index uint32) string { + t.initialize() + + return t.table.index[index] +} + +func (t *NameTable) LookupIndex(str string) uint32 { + t.initialize() + + return t.table.string[str] +} + +// QueryNameTable Query a perflib name table from the v1. Specify the type and the language +// code (i.e. "Counter 009" or "Help 009") for English language. +func QueryNameTable(tableName string) *NameTable { + return &NameTable{ + name: tableName, + } +} + +func (t *NameTable) initialize() { + t.once.Do(func() { + t.table.index = make(map[uint32]string) + t.table.string = make(map[string]uint32) + + buffer, err := queryRawData(t.name) + if err != nil { + panic(err) + } + + r := bytes.NewReader(buffer) + + for { + index, err := readUTF16String(r) + if err != nil { + break + } + + desc, err := readUTF16String(r) + if err != nil { + break + } + + if err != nil { + panic(fmt.Sprint("Invalid index ", index)) + } + + indexInt, _ := strconv.Atoi(index) + + t.table.index[uint32(indexInt)] = desc + t.table.string[desc] = uint32(indexInt) + } + }) +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go new file mode 100644 index 0000000000000..50fc141dd5810 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go @@ -0,0 +1,63 @@ +// Constants copied from: +// https://github.com/prometheus-community/windows_exporter/tree/4c7df1ccaf65df8662a2d67a7eff93019c94d98c/internal/pdh +// Required for perflib functionality. + +package perflib + +// Based on https://github.com/leoluk/perflib_exporter/blob/master/collector/mapper.go +// +//goland:noinspection GoUnusedConst +const ( + PERF_COUNTER_RAWCOUNT_HEX = 0x00000000 + PERF_COUNTER_LARGE_RAWCOUNT_HEX = 0x00000100 + PERF_COUNTER_TEXT = 0x00000b00 + PERF_COUNTER_RAWCOUNT = 0x00010000 + PERF_COUNTER_LARGE_RAWCOUNT = 0x00010100 + PERF_DOUBLE_RAW = 0x00012000 + PERF_COUNTER_DELTA = 0x00400400 + PERF_COUNTER_LARGE_DELTA = 0x00400500 + PERF_SAMPLE_COUNTER = 0x00410400 + PERF_COUNTER_QUEUELEN_TYPE = 0x00450400 + PERF_COUNTER_LARGE_QUEUELEN_TYPE = 0x00450500 + PERF_COUNTER_100NS_QUEUELEN_TYPE = 0x00550500 + PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE = 0x00650500 + PERF_COUNTER_COUNTER = 0x10410400 + PERF_COUNTER_BULK_COUNT = 0x10410500 + PERF_RAW_FRACTION = 0x20020400 + PERF_LARGE_RAW_FRACTION = 0x20020500 + PERF_COUNTER_TIMER = 0x20410500 + PERF_PRECISION_SYSTEM_TIMER = 0x20470500 + PERF_100NSEC_TIMER = 0x20510500 + PERF_PRECISION_100NS_TIMER = 0x20570500 + PERF_OBJ_TIME_TIMER = 0x20610500 + PERF_PRECISION_OBJECT_TIMER = 0x20670500 + PERF_SAMPLE_FRACTION = 0x20c20400 + PERF_COUNTER_TIMER_INV = 0x21410500 + PERF_100NSEC_TIMER_INV = 0x21510500 + PERF_COUNTER_MULTI_TIMER = 0x22410500 + PERF_100NSEC_MULTI_TIMER = 0x22510500 + PERF_COUNTER_MULTI_TIMER_INV = 0x23410500 + PERF_100NSEC_MULTI_TIMER_INV = 0x23510500 + PERF_AVERAGE_TIMER = 0x30020400 + PERF_ELAPSED_TIME = 0x30240500 + PERF_COUNTER_NODATA = 0x40000200 + PERF_AVERAGE_BULK = 0x40020500 + PERF_SAMPLE_BASE = 0x40030401 + PERF_AVERAGE_BASE = 0x40030402 + PERF_RAW_BASE = 0x40030403 + PERF_PRECISION_TIMESTAMP = 0x40030500 + PERF_LARGE_RAW_BASE = 0x40030503 + PERF_COUNTER_MULTI_BASE = 0x42030500 + PERF_COUNTER_HISTOGRAM_TYPE = 0x80000000 +) + +// Conversion factors. +const ( + TicksToSecondScaleFactor = 1 / 1e7 + WindowsEpoch int64 = 116444736000000000 +) + +const ( + InstanceEmpty = "------" + InstanceTotal = "_Total" +) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go new file mode 100644 index 0000000000000..d2b33767587fd --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go @@ -0,0 +1,492 @@ +package perflib + +/* +Go bindings for the HKEY_PERFORMANCE_DATA perflib / Performance Counters interface. + +# Overview + +HKEY_PERFORMANCE_DATA is a low-level alternative to the higher-level PDH library and WMI. +It operates on blocks of counters and only returns raw values without calculating rates +or formatting them, which is exactly what you want for, say, a Prometheus exporter +(not so much for a GUI like Windows Performance Monitor). + +Its overhead is much lower than the high-level libraries. + +It operates on the same set of perflib providers as PDH and WMI. See this document +for more details on the relationship between the different libraries: +https://msdn.microsoft.com/en-us/library/windows/desktop/aa371643(v=vs.85).aspx + +Example C++ source code: +https://msdn.microsoft.com/de-de/library/windows/desktop/aa372138(v=vs.85).aspx + +For now, the API is not stable and is probably going to change in future +perflib_exporter releases. If you want to use this library, send the author an email +so we can discuss your requirements and stabilize the API. + +# Names + +Counter names and help texts are resolved by looking up an index in a name table. +Since Microsoft loves internalization, both names and help texts can be requested +any locally available language. + +The library automatically loads the name tables and resolves all identifiers +in English ("Name" and "HelpText" struct members). You can manually resolve +identifiers in a different language by using the NameTable API. + +# Performance Counters intro + +Windows has a system-wide performance counter mechanism. Most performance counters +are stored as actual counters, not gauges (with some exceptions). +There's additional metadata which defines how the counter should be presented to the user +(for example, as a calculated rate). This library disregards all of the display metadata. + +At the top level, there's a number of performance counter objects. +Each object has counter definitions, which contain the metadata for a particular +counter, and either zero or multiple instances. We hide the fact that there are +objects with no instances, and simply return a single null instance. + +There's one counter per counter definition and instance (or the object itself, if +there are no instances). + +Behind the scenes, every perflib DLL provides one or more objects. +Perflib has a v1 where DLLs are dynamically registered and +unregistered. Some third party applications like VMWare provide their own counters, +but this is, sadly, a rare occurrence. + +Different Windows releases have different numbers of counters. + +Objects and counters are identified by well-known indices. + +Here's an example object with one instance: + + 4320 WSMan Quota Statistics [7 counters, 1 instance(s)] + `-- "WinRMService" + `-- Total Requests/Second [4322] = 59 + `-- User Quota Violations/Second [4324] = 0 + `-- System Quota Violations/Second [4326] = 0 + `-- Active Shells [4328] = 0 + `-- Active Operations [4330] = 0 + `-- Active Users [4332] = 0 + `-- Process ID [4334] = 928 + +All "per second" metrics are counters, the rest are gauges. + +Another example, with no instance: + + 4600 Network QoS Policy [6 counters, 1 instance(s)] + `-- (default) + `-- Packets transmitted [4602] = 1744 + `-- Packets transmitted/sec [4604] = 4852 + `-- Bytes transmitted [4606] = 4853 + `-- Bytes transmitted/sec [4608] = 180388626632 + `-- Packets dropped [4610] = 0 + `-- Packets dropped/sec [4612] = 0 + +You can access the same values using PowerShell's Get-Counter cmdlet +or the Performance Monitor. + + > Get-Counter '\WSMan Quota Statistics(WinRMService)\Process ID' + + Timestamp CounterSamples + --------- -------------- + 1/28/2018 10:18:00 PM \\DEV\wsman quota statistics(winrmservice)\process id : + 928 + + > (Get-Counter '\Process(Idle)\% Processor Time').CounterSamples[0] | Format-List * + [..detailed output...] + +Data for some of the objects is also available through WMI: + + > Get-CimInstance Win32_PerfRawData_Counters_WSManQuotaStatistics + + Name : WinRMService + [...] + ActiveOperations : 0 + ActiveShells : 0 + ActiveUsers : 0 + ProcessID : 928 + SystemQuotaViolationsPerSecond : 0 + TotalRequestsPerSecond : 59 + UserQuotaViolationsPerSecond : 0 +*/ + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "strings" + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +// There's a LittleEndian field in the PERF header - we ought to check it. +// +//nolint:gochecknoglobals +var bo = binary.LittleEndian + +// PerfObject Top-level performance object (like "Process"). +type PerfObject struct { + Name string + // NameIndex Same index you pass to QueryPerformanceData + NameIndex uint + Instances []*PerfInstance + CounterDefs []*PerfCounterDef + + Frequency int64 + + rawData *perfObjectType +} + +// PerfInstance Each object can have multiple instances. For example, +// In case the object has no instances, we return one single PerfInstance with an empty name. +type PerfInstance struct { + // *not* resolved using a name table + Name string + Counters []*PerfCounter + + rawData *perfInstanceDefinition + rawCounterBlock *perfCounterBlock +} + +type PerfCounterDef struct { + Name string + NameIndex uint + + // For debugging - subject to removal. CounterType is a perflib + // implementation detail (see perflib.h) and should not be used outside + // of this package. We export it so we can show it on /dump. + CounterType uint32 + + // PERF_TYPE_COUNTER (otherwise, it's a gauge) + IsCounter bool + // PERF_COUNTER_BASE (base value of a multi-value fraction) + IsBaseValue bool + // PERF_TIMER_100NS + IsNanosecondCounter bool + HasSecondValue bool + + rawData *perfCounterDefinition +} + +type PerfCounter struct { + Value int64 + Def *PerfCounterDef + SecondValue int64 +} + +//nolint:gochecknoglobals +var ( + bufLenGlobal = uint32(400000) + bufLenCostly = uint32(2000000) +) + +// queryRawData Queries the performance counter buffer using RegQueryValueEx, returning raw bytes. See: +// https://msdn.microsoft.com/de-de/library/windows/desktop/aa373219(v=vs.85).aspx +func queryRawData(query string) ([]byte, error) { + var ( + valType uint32 + buffer []byte + bufLen uint32 + ) + + switch query { + case "Global": + bufLen = bufLenGlobal + case "Costly": + bufLen = bufLenCostly + default: + // depends on the number of values requested + // need make an educated guess + numCounters := len(strings.Split(query, " ")) + bufLen = uint32(150000 * numCounters) + } + + buffer = make([]byte, bufLen) + + name, err := windows.UTF16PtrFromString(query) + if err != nil { + return nil, fmt.Errorf("failed to encode query string: %w", err) + } + + for { + bufLen := uint32(len(buffer)) + + err := windows.RegQueryValueEx( + windows.HKEY_PERFORMANCE_DATA, + name, + nil, + &valType, + (*byte)(unsafe.Pointer(&buffer[0])), + &bufLen) + + switch { + case errors.Is(err, error(windows.ERROR_MORE_DATA)): + newBuffer := make([]byte, len(buffer)+16384) + copy(newBuffer, buffer) + buffer = newBuffer + + continue + case errors.Is(err, error(windows.ERROR_BUSY)): + time.Sleep(50 * time.Millisecond) + + continue + case err != nil: + var errNo windows.Errno + if errors.As(err, &errNo) { + return nil, fmt.Errorf("ReqQueryValueEx failed: %w errno %d", err, uint(errNo)) + } + + return nil, err + } + + buffer = buffer[:bufLen] + + switch query { + case "Global": + if bufLen > bufLenGlobal { + bufLenGlobal = bufLen + } + case "Costly": + if bufLen > bufLenCostly { + bufLenCostly = bufLen + } + } + + return buffer, nil + } +} + +/* +QueryPerformanceData Query all performance counters that match a given query. + +The query can be any of the following: + +- "Global" (all performance counters except those Windows marked as costly) + +- "Costly" (only the costly ones) + +- One or more object indices, separated by spaces ("238 2 5") + +Many objects have dependencies - if you query one of them, you often get back +more than you asked for. +*/ +func QueryPerformanceData(query string, counterName string) ([]*PerfObject, error) { + buffer, err := queryRawData(query) + if err != nil { + return nil, err + } + + r := bytes.NewReader(buffer) + + // Read global header + + header := new(perfDataBlock) + + err = header.BinaryReadFrom(r) + if err != nil { + return nil, fmt.Errorf("failed to read performance data block for %q with: %w", query, err) + } + + // Check for "PERF" signature + if header.Signature != [4]uint16{80, 69, 82, 70} { + panic("Invalid performance block header") + } + + // Parse the performance data + + numObjects := int(header.NumObjectTypes) + numFilteredObjects := 0 + + objects := make([]*PerfObject, numObjects) + + objOffset := int64(header.HeaderLength) + + for i := range numObjects { + _, err := r.Seek(objOffset, io.SeekStart) + if err != nil { + return nil, err + } + + obj := new(perfObjectType) + + err = obj.BinaryReadFrom(r) + if err != nil { + return nil, err + } + + perfCounterName := obj.LookupName() + + if counterName != "" && perfCounterName != counterName { + objOffset += int64(obj.TotalByteLength) + + continue + } + + numCounterDefs := int(obj.NumCounters) + numInstances := int(obj.NumInstances) + + // Perf objects can have no instances. The perflib differentiates + // between objects with instances and without, but we just create + // an empty instance in order to simplify the interface. + if numInstances <= 0 { + numInstances = 1 + } + + instances := make([]*PerfInstance, numInstances) + counterDefs := make([]*PerfCounterDef, numCounterDefs) + + objects[i] = &PerfObject{ + Name: perfCounterName, + NameIndex: uint(obj.ObjectNameTitleIndex), + Instances: instances, + CounterDefs: counterDefs, + Frequency: obj.PerfFreq, + rawData: obj, + } + + for i := range numCounterDefs { + def := new(perfCounterDefinition) + + err := def.BinaryReadFrom(r) + if err != nil { + return nil, err + } + + counterDefs[i] = &PerfCounterDef{ + Name: def.LookupName(), + NameIndex: uint(def.CounterNameTitleIndex), + rawData: def, + + CounterType: def.CounterType, + + IsCounter: def.CounterType&0x400 == 0x400, + IsBaseValue: def.CounterType&0x00030000 == 0x00030000, + IsNanosecondCounter: def.CounterType&0x00100000 == 0x00100000, + HasSecondValue: def.CounterType == PERF_AVERAGE_BULK, + } + } + + if obj.NumInstances <= 0 { //nolint:nestif + blockOffset := objOffset + int64(obj.DefinitionLength) + + if _, err := r.Seek(blockOffset, io.SeekStart); err != nil { + return nil, err + } + + _, counters, err := parseCounterBlock(buffer, r, blockOffset, counterDefs) + if err != nil { + return nil, err + } + + instances[0] = &PerfInstance{ + Name: "", + Counters: counters, + rawData: nil, + rawCounterBlock: nil, + } + } else { + instOffset := objOffset + int64(obj.DefinitionLength) + + for i := range numInstances { + if _, err := r.Seek(instOffset, io.SeekStart); err != nil { + return nil, err + } + + inst := new(perfInstanceDefinition) + + if err = inst.BinaryReadFrom(r); err != nil { + return nil, err + } + + name, _ := readUTF16StringAtPos(r, instOffset+int64(inst.NameOffset), inst.NameLength) + pos := instOffset + int64(inst.ByteLength) + + offset, counters, err := parseCounterBlock(buffer, r, pos, counterDefs) + if err != nil { + return nil, err + } + + instances[i] = &PerfInstance{ + Name: name, + Counters: counters, + rawData: inst, + } + + instOffset = pos + offset + } + } + + if counterName != "" { + return objects[i : i+1], nil + } + + // Next perfObjectType + objOffset += int64(obj.TotalByteLength) + numFilteredObjects++ + } + + return objects[:numFilteredObjects], nil +} + +func parseCounterBlock(b []byte, r io.ReadSeeker, pos int64, defs []*PerfCounterDef) (int64, []*PerfCounter, error) { + _, err := r.Seek(pos, io.SeekStart) + if err != nil { + return 0, nil, err + } + + block := new(perfCounterBlock) + + err = block.BinaryReadFrom(r) + if err != nil { + return 0, nil, err + } + + counters := make([]*PerfCounter, len(defs)) + + for i, def := range defs { + valueOffset := pos + int64(def.rawData.CounterOffset) + value := convertCounterValue(def.rawData, b, valueOffset) + secondValue := int64(0) + + if def.HasSecondValue { + secondValue = convertCounterValue(def.rawData, b, valueOffset+8) + } + + counters[i] = &PerfCounter{ + Value: value, + Def: def, + SecondValue: secondValue, + } + } + + return int64(block.ByteLength), counters, nil +} + +func convertCounterValue(counterDef *perfCounterDefinition, buffer []byte, valueOffset int64) int64 { + /* + We can safely ignore the type since we're not interested in anything except the raw value. + We also ignore all of the other attributes (timestamp, presentation, multi counter values...) + + See also: winperf.h. + + Here's the most common value for CounterType: + + 65536 32bit counter + 65792 64bit counter + 272696320 32bit rate + 272696576 64bit rate + + */ + switch counterDef.CounterSize { + case 4: + return int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)])) + case 8: + return int64(bo.Uint64(buffer[valueOffset:(valueOffset + 8)])) + default: + return int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)])) + } +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go new file mode 100644 index 0000000000000..be92fb3186924 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go @@ -0,0 +1,11 @@ +package perflib + +import ( + "testing" +) + +func BenchmarkQueryPerformanceData(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = QueryPerformanceData("Global", "") + } +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go new file mode 100644 index 0000000000000..e4bbd7c18dc9f --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go @@ -0,0 +1,173 @@ +package perflib + +import ( + "encoding/binary" + "io" + + "golang.org/x/sys/windows" +) + +/* +perfDataBlock +See: https://msdn.microsoft.com/de-de/library/windows/desktop/aa373157(v=vs.85).aspx + + typedef struct _PERF_DATA_BLOCK { + WCHAR Signature[4]; + DWORD LittleEndian; + DWORD Version; + DWORD Revision; + DWORD TotalByteLength; + DWORD HeaderLength; + DWORD NumObjectTypes; + DWORD DefaultObject; + SYSTEMTIME SystemTime; + LARGE_INTEGER PerfTime; + LARGE_INTEGER PerfFreq; + LARGE_INTEGER PerfTime100nSec; + DWORD SystemNameLength; + DWORD SystemNameOffset; + } PERF_DATA_BLOCK; +*/ +type perfDataBlock struct { + Signature [4]uint16 + LittleEndian uint32 + Version uint32 + Revision uint32 + TotalByteLength uint32 + HeaderLength uint32 + NumObjectTypes uint32 + DefaultObject int32 + SystemTime windows.Systemtime + _ uint32 // unknown field + PerfTime int64 + PerfFreq int64 + PerfTime100nSec int64 + SystemNameLength uint32 + SystemNameOffset uint32 +} + +func (p *perfDataBlock) BinaryReadFrom(r io.Reader) error { + return binary.Read(r, bo, p) +} + +/* +perfObjectType +See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373160(v=vs.85).aspx + + typedef struct _PERF_OBJECT_TYPE { + DWORD TotalByteLength; + DWORD DefinitionLength; + DWORD HeaderLength; + DWORD ObjectNameTitleIndex; + LPWSTR ObjectNameTitle; + DWORD ObjectHelpTitleIndex; + LPWSTR ObjectHelpTitle; + DWORD DetailLevel; + DWORD NumCounters; + DWORD DefaultCounter; + DWORD NumInstances; + DWORD CodePage; + LARGE_INTEGER PerfTime; + LARGE_INTEGER PerfFreq; + } PERF_OBJECT_TYPE; +*/ +type perfObjectType struct { + TotalByteLength uint32 + DefinitionLength uint32 + HeaderLength uint32 + ObjectNameTitleIndex uint32 + ObjectNameTitle uint32 + ObjectHelpTitleIndex uint32 + ObjectHelpTitle uint32 + DetailLevel uint32 + NumCounters uint32 + DefaultCounter int32 + NumInstances int32 + CodePage uint32 + PerfTime int64 + PerfFreq int64 +} + +func (p *perfObjectType) BinaryReadFrom(r io.Reader) error { + return binary.Read(r, bo, p) +} + +/* +perfCounterDefinition +See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373150(v=vs.85).aspx + + typedef struct _PERF_COUNTER_DEFINITION { + DWORD ByteLength; + DWORD CounterNameTitleIndex; + LPWSTR CounterNameTitle; + DWORD CounterHelpTitleIndex; + LPWSTR CounterHelpTitle; + LONG DefaultScale; + DWORD DetailLevel; + DWORD CounterType; + DWORD CounterSize; + DWORD CounterOffset; + } PERF_COUNTER_DEFINITION; +*/ +type perfCounterDefinition struct { + ByteLength uint32 + CounterNameTitleIndex uint32 + CounterNameTitle uint32 + CounterHelpTitleIndex uint32 + CounterHelpTitle uint32 + DefaultScale int32 + DetailLevel uint32 + CounterType uint32 + CounterSize uint32 + CounterOffset uint32 +} + +func (p *perfCounterDefinition) BinaryReadFrom(r io.Reader) error { + return binary.Read(r, bo, p) +} + +func (p *perfCounterDefinition) LookupName() string { + return CounterNameTable.LookupString(p.CounterNameTitleIndex) +} + +/* +perfCounterBlock +See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373147(v=vs.85).aspx + + typedef struct _PERF_COUNTER_BLOCK { + DWORD ByteLength; + } PERF_COUNTER_BLOCK; +*/ +type perfCounterBlock struct { + ByteLength uint32 +} + +func (p *perfCounterBlock) BinaryReadFrom(r io.Reader) error { + return binary.Read(r, bo, p) +} + +/* +perfInstanceDefinition +See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373159(v=vs.85).aspx + + typedef struct _PERF_INSTANCE_DEFINITION { + DWORD ByteLength; + DWORD ParentObjectTitleIndex; + DWORD ParentObjectInstance; + DWORD UniqueID; + DWORD NameOffset; + DWORD NameLength; + } PERF_INSTANCE_DEFINITION; +*/ +type perfInstanceDefinition struct { + ByteLength uint32 + ParentObjectTitleIndex uint32 + ParentObjectInstance uint32 + UniqueID uint32 + NameOffset uint32 + NameLength uint32 +} + +func (p *perfInstanceDefinition) BinaryReadFrom(r io.Reader) error { + return binary.Read(r, bo, p) +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go new file mode 100644 index 0000000000000..81e2c324f1486 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go @@ -0,0 +1,49 @@ +package perflib + +import ( + "encoding/binary" + "io" + + "golang.org/x/sys/windows" +) + +// readUTF16StringAtPos Read an unterminated UTF16 string at a given position, specifying its length. +func readUTF16StringAtPos(r io.ReadSeeker, absPos int64, length uint32) (string, error) { + value := make([]uint16, length/2) + + _, err := r.Seek(absPos, io.SeekStart) + if err != nil { + return "", err + } + + err = binary.Read(r, bo, value) + if err != nil { + return "", err + } + + return windows.UTF16ToString(value), nil +} + +// readUTF16String Reads a null-terminated UTF16 string at the current offset. +func readUTF16String(r io.Reader) (string, error) { + var err error + + b := make([]byte, 2) + out := make([]uint16, 0, 100) + + for i := 0; err == nil; i += 2 { + _, err = r.Read(b) + + if b[0] == 0 && b[1] == 0 { + break + } + + out = append(out, bo.Uint16(b)) + } + + if err != nil { + return "", err + } + + return windows.UTF16ToString(out), nil +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go new file mode 100644 index 0000000000000..ac4616d4e8469 --- /dev/null +++ b/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go @@ -0,0 +1,9 @@ +package perflib + +import ( + "strconv" +) + +func MapCounterToIndex(name string) string { + return strconv.Itoa(int(CounterNameTable.LookupIndex(name))) +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go index 5efd74685feb4..b8ba93696fda5 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go @@ -10,9 +10,8 @@ import ( "strconv" "strings" - "github.com/prometheus-community/windows_exporter/pkg/perflib" - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib" ) const totalInstanceName = "_Total" @@ -67,7 +66,7 @@ func (p *PerfLibScraper) Initialize(objects ...string) error { } func (p *PerfLibScraper) Scrape() (PerfDataCollection, error) { - objects, err := perflib.QueryPerformanceData(p.objectIndices) + objects, err := perflib.QueryPerformanceData(p.objectIndices, "") if err != nil { return nil, fmt.Errorf("failed to query performance data: %w", err) } From 1f1f104f8f292cdb1f84aca02bd6c3be7fee0333 Mon Sep 17 00:00:00 2001 From: braydonk Date: Wed, 2 Apr 2025 15:42:18 +0000 Subject: [PATCH 2/7] rename perflib's containing folder to third_party --- .../perfcounters/perfcounter_scraper.go | 396 +++++++++--------- .../{internal => third_party}/perflib/LICENSE | 0 .../perflib/collector.go | 0 .../perflib/errors.go | 0 .../perflib/nametable.go | 0 .../perflib/pdh_const.go | 0 .../perflib/perflib.go | 0 .../perflib/perflib_test.go | 0 .../perflib/raw_types.go | 0 .../perflib/utf16.go | 0 .../perflib/utils.go | 0 11 files changed, 198 insertions(+), 198 deletions(-) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/LICENSE (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/collector.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/errors.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/nametable.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/pdh_const.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/perflib.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/perflib_test.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/raw_types.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/utf16.go (100%) rename receiver/hostmetricsreceiver/internal/perfcounters/{internal => third_party}/perflib/utils.go (100%) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go index b8ba93696fda5..2607acc2ed02e 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go @@ -1,198 +1,198 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:build windows - -package perfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters" - -import ( - "fmt" - "strconv" - "strings" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib" -) - -const totalInstanceName = "_Total" - -// PerfCounterScraper scrapes performance counter data. -type PerfCounterScraper interface { - // start initializes the PerfCounterScraper so that subsequent calls - // to scrape will return performance counter data for the specified set. - // of objects - Initialize(objects ...string) error - // scrape returns performance data for the initialized objects. - Scrape() (PerfDataCollection, error) -} - -// PerfLibScraper is an implementation of PerfCounterScraper that uses -// perflib to scrape performance counter data. -type PerfLibScraper struct { - objectIndices string -} - -func (p *PerfLibScraper) Initialize(objects ...string) error { - // "Counter 009" reads perf counter names in English. - // This is always present regardless of the OS language. - nameTable := perflib.QueryNameTable("Counter 009") - - initErr := &PerfCounterInitError{} - - // lookup object indices from name table - objectIndicesMap := map[uint32]struct{}{} - for _, name := range objects { - index := nameTable.LookupIndex(name) - if index == 0 { - initErr.AddFailure(name) - continue - } - - objectIndicesMap[index] = struct{}{} - } - - // convert to a space-separated string - objectIndicesSlice := make([]string, 0, len(objectIndicesMap)) - for k := range objectIndicesMap { - objectIndicesSlice = append(objectIndicesSlice, strconv.Itoa(int(k))) - } - p.objectIndices = strings.Join(objectIndicesSlice, " ") - - if len(initErr.FailedObjects) > 0 { - return initErr - } - - return nil -} - -func (p *PerfLibScraper) Scrape() (PerfDataCollection, error) { - objects, err := perflib.QueryPerformanceData(p.objectIndices, "") - if err != nil { - return nil, fmt.Errorf("failed to query performance data: %w", err) - } - - indexed := make(map[string]*perflib.PerfObject) - for _, obj := range objects { - indexed[obj.Name] = obj - } - - return perfDataCollection{perfObject: indexed}, nil -} - -// PerfDataCollection represents a collection of perf counter data. -type PerfDataCollection interface { - // GetObject returns the perf counter data associated with the specified object, - // or returns an error if no data exists for this object name. - GetObject(objectName string) (PerfDataObject, error) -} - -type perfDataCollection struct { - perfObject map[string]*perflib.PerfObject -} - -func (p perfDataCollection) GetObject(objectName string) (PerfDataObject, error) { - obj, ok := p.perfObject[objectName] - if !ok { - return nil, fmt.Errorf("Unable to find object %q", objectName) - } - - return perfDataObject{obj}, nil -} - -// PerfDataCollection represents a collection of perf counter values -// and associated instances. -type PerfDataObject interface { - // Filter filters the perf counter data to only retain data related to - // relevant instances based on the supplied parameters. - Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) - // GetValues returns the performance counter data associated with the specified - // counters, or returns an error if any of the specified counter names do not - // exist. - GetValues(counterNames ...string) ([]*CounterValues, error) -} - -type perfDataObject struct { - *perflib.PerfObject -} - -func (obj perfDataObject) Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) { - if includeFS == nil && excludeFS == nil && includeTotal { - return - } - - filteredDevices := make([]*perflib.PerfInstance, 0, len(obj.Instances)) - for _, device := range obj.Instances { - if includeDevice(device.Name, includeFS, excludeFS, includeTotal) { - filteredDevices = append(filteredDevices, device) - } - } - obj.Instances = filteredDevices -} - -func includeDevice(deviceName string, includeFS, excludeFS filterset.FilterSet, includeTotal bool) bool { - if deviceName == totalInstanceName { - return includeTotal - } - - return (includeFS == nil || includeFS.Matches(deviceName)) && - (excludeFS == nil || !excludeFS.Matches(deviceName)) -} - -// CounterValues represents a set of perf counter values for a given instance. -type CounterValues struct { - InstanceName string - Values map[string]int64 -} - -type counterIndex struct { - index int - name string -} - -func (obj perfDataObject) GetValues(counterNames ...string) ([]*CounterValues, error) { - counterIndices := make([]counterIndex, 0, len(counterNames)) - for idx, counter := range obj.CounterDefs { - // "Base" values give the value of a related counter that pdh.dll uses to compute the derived - // value for this counter. We only care about raw values so ignore base values. See - // https://docs.microsoft.com/en-us/windows/win32/perfctrs/retrieving-counter-data. - if counter.IsBaseValue && !counter.IsNanosecondCounter { - continue - } - - for _, counterName := range counterNames { - if counter.Name == counterName { - counterIndices = append(counterIndices, counterIndex{index: idx, name: counter.Name}) - break - } - } - } - - if len(counterIndices) < len(counterNames) { - return nil, fmt.Errorf("Unable to find counters %q in object %q", missingCounterNames(counterNames, counterIndices), obj.Name) - } - - values := make([]*CounterValues, len(obj.Instances)) - for i, instance := range obj.Instances { - instanceValues := &CounterValues{InstanceName: instance.Name, Values: make(map[string]int64, len(counterIndices))} - for _, counter := range counterIndices { - instanceValues.Values[counter.name] = instance.Counters[counter.index].Value - } - values[i] = instanceValues - } - return values, nil -} - -func missingCounterNames(counterNames []string, counterIndices []counterIndex) []string { - matchedCounters := make(map[string]struct{}, len(counterIndices)) - for _, counter := range counterIndices { - matchedCounters[counter.name] = struct{}{} - } - - counters := make([]string, 0, len(counterNames)-len(matchedCounters)) - for _, counter := range counterNames { - if _, ok := matchedCounters[counter]; !ok { - counters = append(counters, counter) - } - } - return counters -} +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:build windows + +package perfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters" + +import ( + "fmt" + "strconv" + "strings" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" +) + +const totalInstanceName = "_Total" + +// PerfCounterScraper scrapes performance counter data. +type PerfCounterScraper interface { + // start initializes the PerfCounterScraper so that subsequent calls + // to scrape will return performance counter data for the specified set. + // of objects + Initialize(objects ...string) error + // scrape returns performance data for the initialized objects. + Scrape() (PerfDataCollection, error) +} + +// PerfLibScraper is an implementation of PerfCounterScraper that uses +// perflib to scrape performance counter data. +type PerfLibScraper struct { + objectIndices string +} + +func (p *PerfLibScraper) Initialize(objects ...string) error { + // "Counter 009" reads perf counter names in English. + // This is always present regardless of the OS language. + nameTable := perflib.QueryNameTable("Counter 009") + + initErr := &PerfCounterInitError{} + + // lookup object indices from name table + objectIndicesMap := map[uint32]struct{}{} + for _, name := range objects { + index := nameTable.LookupIndex(name) + if index == 0 { + initErr.AddFailure(name) + continue + } + + objectIndicesMap[index] = struct{}{} + } + + // convert to a space-separated string + objectIndicesSlice := make([]string, 0, len(objectIndicesMap)) + for k := range objectIndicesMap { + objectIndicesSlice = append(objectIndicesSlice, strconv.Itoa(int(k))) + } + p.objectIndices = strings.Join(objectIndicesSlice, " ") + + if len(initErr.FailedObjects) > 0 { + return initErr + } + + return nil +} + +func (p *PerfLibScraper) Scrape() (PerfDataCollection, error) { + objects, err := perflib.QueryPerformanceData(p.objectIndices, "") + if err != nil { + return nil, fmt.Errorf("failed to query performance data: %w", err) + } + + indexed := make(map[string]*perflib.PerfObject) + for _, obj := range objects { + indexed[obj.Name] = obj + } + + return perfDataCollection{perfObject: indexed}, nil +} + +// PerfDataCollection represents a collection of perf counter data. +type PerfDataCollection interface { + // GetObject returns the perf counter data associated with the specified object, + // or returns an error if no data exists for this object name. + GetObject(objectName string) (PerfDataObject, error) +} + +type perfDataCollection struct { + perfObject map[string]*perflib.PerfObject +} + +func (p perfDataCollection) GetObject(objectName string) (PerfDataObject, error) { + obj, ok := p.perfObject[objectName] + if !ok { + return nil, fmt.Errorf("Unable to find object %q", objectName) + } + + return perfDataObject{obj}, nil +} + +// PerfDataCollection represents a collection of perf counter values +// and associated instances. +type PerfDataObject interface { + // Filter filters the perf counter data to only retain data related to + // relevant instances based on the supplied parameters. + Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) + // GetValues returns the performance counter data associated with the specified + // counters, or returns an error if any of the specified counter names do not + // exist. + GetValues(counterNames ...string) ([]*CounterValues, error) +} + +type perfDataObject struct { + *perflib.PerfObject +} + +func (obj perfDataObject) Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) { + if includeFS == nil && excludeFS == nil && includeTotal { + return + } + + filteredDevices := make([]*perflib.PerfInstance, 0, len(obj.Instances)) + for _, device := range obj.Instances { + if includeDevice(device.Name, includeFS, excludeFS, includeTotal) { + filteredDevices = append(filteredDevices, device) + } + } + obj.Instances = filteredDevices +} + +func includeDevice(deviceName string, includeFS, excludeFS filterset.FilterSet, includeTotal bool) bool { + if deviceName == totalInstanceName { + return includeTotal + } + + return (includeFS == nil || includeFS.Matches(deviceName)) && + (excludeFS == nil || !excludeFS.Matches(deviceName)) +} + +// CounterValues represents a set of perf counter values for a given instance. +type CounterValues struct { + InstanceName string + Values map[string]int64 +} + +type counterIndex struct { + index int + name string +} + +func (obj perfDataObject) GetValues(counterNames ...string) ([]*CounterValues, error) { + counterIndices := make([]counterIndex, 0, len(counterNames)) + for idx, counter := range obj.CounterDefs { + // "Base" values give the value of a related counter that pdh.dll uses to compute the derived + // value for this counter. We only care about raw values so ignore base values. See + // https://docs.microsoft.com/en-us/windows/win32/perfctrs/retrieving-counter-data. + if counter.IsBaseValue && !counter.IsNanosecondCounter { + continue + } + + for _, counterName := range counterNames { + if counter.Name == counterName { + counterIndices = append(counterIndices, counterIndex{index: idx, name: counter.Name}) + break + } + } + } + + if len(counterIndices) < len(counterNames) { + return nil, fmt.Errorf("Unable to find counters %q in object %q", missingCounterNames(counterNames, counterIndices), obj.Name) + } + + values := make([]*CounterValues, len(obj.Instances)) + for i, instance := range obj.Instances { + instanceValues := &CounterValues{InstanceName: instance.Name, Values: make(map[string]int64, len(counterIndices))} + for _, counter := range counterIndices { + instanceValues.Values[counter.name] = instance.Counters[counter.index].Value + } + values[i] = instanceValues + } + return values, nil +} + +func missingCounterNames(counterNames []string, counterIndices []counterIndex) []string { + matchedCounters := make(map[string]struct{}, len(counterIndices)) + for _, counter := range counterIndices { + matchedCounters[counter.name] = struct{}{} + } + + counters := make([]string, 0, len(counterNames)-len(matchedCounters)) + for _, counter := range counterNames { + if _, ok := matchedCounters[counter]; !ok { + counters = append(counters, counter) + } + } + return counters +} diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/LICENSE similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/LICENSE rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/LICENSE diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/collector.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/errors.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/nametable.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/pdh_const.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/perflib_test.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/raw_types.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utf16.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go similarity index 100% rename from receiver/hostmetricsreceiver/internal/perfcounters/internal/perflib/utils.go rename to receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go From 17336b0ab2d7460949bb1a3f574a3ef7a027d96a Mon Sep 17 00:00:00 2001 From: braydonk Date: Wed, 2 Apr 2025 18:38:52 +0000 Subject: [PATCH 3/7] make goporto --- .../internal/perfcounters/perfcounter_scraper.go | 2 +- .../internal/perfcounters/third_party/perflib/collector.go | 2 +- .../internal/perfcounters/third_party/perflib/errors.go | 2 +- .../internal/perfcounters/third_party/perflib/nametable.go | 2 +- .../internal/perfcounters/third_party/perflib/pdh_const.go | 2 +- .../internal/perfcounters/third_party/perflib/perflib.go | 2 +- .../internal/perfcounters/third_party/perflib/raw_types.go | 2 +- .../internal/perfcounters/third_party/perflib/utf16.go | 2 +- .../internal/perfcounters/third_party/perflib/utils.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go index 2607acc2ed02e..76148231c81d2 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go @@ -3,7 +3,7 @@ //go:build windows -package perfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters" +package perfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters" import ( "fmt" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go index f9baf8b822db0..026cc8ea70934 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( "fmt" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go index 1cba8aabee3b5..dcf4e0935f295 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import "errors" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go index 22dc079ace44e..55df47ca93853 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( "bytes" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go index 50fc141dd5810..3c71509746218 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go @@ -2,7 +2,7 @@ // https://github.com/prometheus-community/windows_exporter/tree/4c7df1ccaf65df8662a2d67a7eff93019c94d98c/internal/pdh // Required for perflib functionality. -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" // Based on https://github.com/leoluk/perflib_exporter/blob/master/collector/mapper.go // diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go index d2b33767587fd..197195336bbe3 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" /* Go bindings for the HKEY_PERFORMANCE_DATA perflib / Performance Counters interface. diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go index e4bbd7c18dc9f..b9ee787a91f3c 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( "encoding/binary" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go index 81e2c324f1486..123b879ea96cd 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( "encoding/binary" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go index ac4616d4e8469..73e11051343ec 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go @@ -1,4 +1,4 @@ -package perflib +package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( "strconv" From 386a62d24c047f43839ab74c00998ef4fb0b8169 Mon Sep 17 00:00:00 2001 From: braydonk Date: Wed, 2 Apr 2025 19:34:55 +0000 Subject: [PATCH 4/7] windows line endings ugh --- .../perfcounters/perfcounter_scraper.go | 394 +++++++++--------- 1 file changed, 197 insertions(+), 197 deletions(-) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go index 76148231c81d2..556319a9cd457 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/perfcounter_scraper.go @@ -1,198 +1,198 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:build windows - +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:build windows + package perfcounters // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters" - -import ( - "fmt" - "strconv" - "strings" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" -) - -const totalInstanceName = "_Total" - -// PerfCounterScraper scrapes performance counter data. -type PerfCounterScraper interface { - // start initializes the PerfCounterScraper so that subsequent calls - // to scrape will return performance counter data for the specified set. - // of objects - Initialize(objects ...string) error - // scrape returns performance data for the initialized objects. - Scrape() (PerfDataCollection, error) -} - -// PerfLibScraper is an implementation of PerfCounterScraper that uses -// perflib to scrape performance counter data. -type PerfLibScraper struct { - objectIndices string -} - -func (p *PerfLibScraper) Initialize(objects ...string) error { - // "Counter 009" reads perf counter names in English. - // This is always present regardless of the OS language. - nameTable := perflib.QueryNameTable("Counter 009") - - initErr := &PerfCounterInitError{} - - // lookup object indices from name table - objectIndicesMap := map[uint32]struct{}{} - for _, name := range objects { - index := nameTable.LookupIndex(name) - if index == 0 { - initErr.AddFailure(name) - continue - } - - objectIndicesMap[index] = struct{}{} - } - - // convert to a space-separated string - objectIndicesSlice := make([]string, 0, len(objectIndicesMap)) - for k := range objectIndicesMap { - objectIndicesSlice = append(objectIndicesSlice, strconv.Itoa(int(k))) - } - p.objectIndices = strings.Join(objectIndicesSlice, " ") - - if len(initErr.FailedObjects) > 0 { - return initErr - } - - return nil -} - -func (p *PerfLibScraper) Scrape() (PerfDataCollection, error) { - objects, err := perflib.QueryPerformanceData(p.objectIndices, "") - if err != nil { - return nil, fmt.Errorf("failed to query performance data: %w", err) - } - - indexed := make(map[string]*perflib.PerfObject) - for _, obj := range objects { - indexed[obj.Name] = obj - } - - return perfDataCollection{perfObject: indexed}, nil -} - -// PerfDataCollection represents a collection of perf counter data. -type PerfDataCollection interface { - // GetObject returns the perf counter data associated with the specified object, - // or returns an error if no data exists for this object name. - GetObject(objectName string) (PerfDataObject, error) -} - -type perfDataCollection struct { - perfObject map[string]*perflib.PerfObject -} - -func (p perfDataCollection) GetObject(objectName string) (PerfDataObject, error) { - obj, ok := p.perfObject[objectName] - if !ok { - return nil, fmt.Errorf("Unable to find object %q", objectName) - } - - return perfDataObject{obj}, nil -} - -// PerfDataCollection represents a collection of perf counter values -// and associated instances. -type PerfDataObject interface { - // Filter filters the perf counter data to only retain data related to - // relevant instances based on the supplied parameters. - Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) - // GetValues returns the performance counter data associated with the specified - // counters, or returns an error if any of the specified counter names do not - // exist. - GetValues(counterNames ...string) ([]*CounterValues, error) -} - -type perfDataObject struct { - *perflib.PerfObject -} - -func (obj perfDataObject) Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) { - if includeFS == nil && excludeFS == nil && includeTotal { - return - } - - filteredDevices := make([]*perflib.PerfInstance, 0, len(obj.Instances)) - for _, device := range obj.Instances { - if includeDevice(device.Name, includeFS, excludeFS, includeTotal) { - filteredDevices = append(filteredDevices, device) - } - } - obj.Instances = filteredDevices -} - -func includeDevice(deviceName string, includeFS, excludeFS filterset.FilterSet, includeTotal bool) bool { - if deviceName == totalInstanceName { - return includeTotal - } - - return (includeFS == nil || includeFS.Matches(deviceName)) && - (excludeFS == nil || !excludeFS.Matches(deviceName)) -} - -// CounterValues represents a set of perf counter values for a given instance. -type CounterValues struct { - InstanceName string - Values map[string]int64 -} - -type counterIndex struct { - index int - name string -} - -func (obj perfDataObject) GetValues(counterNames ...string) ([]*CounterValues, error) { - counterIndices := make([]counterIndex, 0, len(counterNames)) - for idx, counter := range obj.CounterDefs { - // "Base" values give the value of a related counter that pdh.dll uses to compute the derived - // value for this counter. We only care about raw values so ignore base values. See - // https://docs.microsoft.com/en-us/windows/win32/perfctrs/retrieving-counter-data. - if counter.IsBaseValue && !counter.IsNanosecondCounter { - continue - } - - for _, counterName := range counterNames { - if counter.Name == counterName { - counterIndices = append(counterIndices, counterIndex{index: idx, name: counter.Name}) - break - } - } - } - - if len(counterIndices) < len(counterNames) { - return nil, fmt.Errorf("Unable to find counters %q in object %q", missingCounterNames(counterNames, counterIndices), obj.Name) - } - - values := make([]*CounterValues, len(obj.Instances)) - for i, instance := range obj.Instances { - instanceValues := &CounterValues{InstanceName: instance.Name, Values: make(map[string]int64, len(counterIndices))} - for _, counter := range counterIndices { - instanceValues.Values[counter.name] = instance.Counters[counter.index].Value - } - values[i] = instanceValues - } - return values, nil -} - -func missingCounterNames(counterNames []string, counterIndices []counterIndex) []string { - matchedCounters := make(map[string]struct{}, len(counterIndices)) - for _, counter := range counterIndices { - matchedCounters[counter.name] = struct{}{} - } - - counters := make([]string, 0, len(counterNames)-len(matchedCounters)) - for _, counter := range counterNames { - if _, ok := matchedCounters[counter]; !ok { - counters = append(counters, counter) - } - } - return counters -} + +import ( + "fmt" + "strconv" + "strings" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterset" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" +) + +const totalInstanceName = "_Total" + +// PerfCounterScraper scrapes performance counter data. +type PerfCounterScraper interface { + // start initializes the PerfCounterScraper so that subsequent calls + // to scrape will return performance counter data for the specified set. + // of objects + Initialize(objects ...string) error + // scrape returns performance data for the initialized objects. + Scrape() (PerfDataCollection, error) +} + +// PerfLibScraper is an implementation of PerfCounterScraper that uses +// perflib to scrape performance counter data. +type PerfLibScraper struct { + objectIndices string +} + +func (p *PerfLibScraper) Initialize(objects ...string) error { + // "Counter 009" reads perf counter names in English. + // This is always present regardless of the OS language. + nameTable := perflib.QueryNameTable("Counter 009") + + initErr := &PerfCounterInitError{} + + // lookup object indices from name table + objectIndicesMap := map[uint32]struct{}{} + for _, name := range objects { + index := nameTable.LookupIndex(name) + if index == 0 { + initErr.AddFailure(name) + continue + } + + objectIndicesMap[index] = struct{}{} + } + + // convert to a space-separated string + objectIndicesSlice := make([]string, 0, len(objectIndicesMap)) + for k := range objectIndicesMap { + objectIndicesSlice = append(objectIndicesSlice, strconv.Itoa(int(k))) + } + p.objectIndices = strings.Join(objectIndicesSlice, " ") + + if len(initErr.FailedObjects) > 0 { + return initErr + } + + return nil +} + +func (p *PerfLibScraper) Scrape() (PerfDataCollection, error) { + objects, err := perflib.QueryPerformanceData(p.objectIndices, "") + if err != nil { + return nil, fmt.Errorf("failed to query performance data: %w", err) + } + + indexed := make(map[string]*perflib.PerfObject) + for _, obj := range objects { + indexed[obj.Name] = obj + } + + return perfDataCollection{perfObject: indexed}, nil +} + +// PerfDataCollection represents a collection of perf counter data. +type PerfDataCollection interface { + // GetObject returns the perf counter data associated with the specified object, + // or returns an error if no data exists for this object name. + GetObject(objectName string) (PerfDataObject, error) +} + +type perfDataCollection struct { + perfObject map[string]*perflib.PerfObject +} + +func (p perfDataCollection) GetObject(objectName string) (PerfDataObject, error) { + obj, ok := p.perfObject[objectName] + if !ok { + return nil, fmt.Errorf("Unable to find object %q", objectName) + } + + return perfDataObject{obj}, nil +} + +// PerfDataCollection represents a collection of perf counter values +// and associated instances. +type PerfDataObject interface { + // Filter filters the perf counter data to only retain data related to + // relevant instances based on the supplied parameters. + Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) + // GetValues returns the performance counter data associated with the specified + // counters, or returns an error if any of the specified counter names do not + // exist. + GetValues(counterNames ...string) ([]*CounterValues, error) +} + +type perfDataObject struct { + *perflib.PerfObject +} + +func (obj perfDataObject) Filter(includeFS, excludeFS filterset.FilterSet, includeTotal bool) { + if includeFS == nil && excludeFS == nil && includeTotal { + return + } + + filteredDevices := make([]*perflib.PerfInstance, 0, len(obj.Instances)) + for _, device := range obj.Instances { + if includeDevice(device.Name, includeFS, excludeFS, includeTotal) { + filteredDevices = append(filteredDevices, device) + } + } + obj.Instances = filteredDevices +} + +func includeDevice(deviceName string, includeFS, excludeFS filterset.FilterSet, includeTotal bool) bool { + if deviceName == totalInstanceName { + return includeTotal + } + + return (includeFS == nil || includeFS.Matches(deviceName)) && + (excludeFS == nil || !excludeFS.Matches(deviceName)) +} + +// CounterValues represents a set of perf counter values for a given instance. +type CounterValues struct { + InstanceName string + Values map[string]int64 +} + +type counterIndex struct { + index int + name string +} + +func (obj perfDataObject) GetValues(counterNames ...string) ([]*CounterValues, error) { + counterIndices := make([]counterIndex, 0, len(counterNames)) + for idx, counter := range obj.CounterDefs { + // "Base" values give the value of a related counter that pdh.dll uses to compute the derived + // value for this counter. We only care about raw values so ignore base values. See + // https://docs.microsoft.com/en-us/windows/win32/perfctrs/retrieving-counter-data. + if counter.IsBaseValue && !counter.IsNanosecondCounter { + continue + } + + for _, counterName := range counterNames { + if counter.Name == counterName { + counterIndices = append(counterIndices, counterIndex{index: idx, name: counter.Name}) + break + } + } + } + + if len(counterIndices) < len(counterNames) { + return nil, fmt.Errorf("Unable to find counters %q in object %q", missingCounterNames(counterNames, counterIndices), obj.Name) + } + + values := make([]*CounterValues, len(obj.Instances)) + for i, instance := range obj.Instances { + instanceValues := &CounterValues{InstanceName: instance.Name, Values: make(map[string]int64, len(counterIndices))} + for _, counter := range counterIndices { + instanceValues.Values[counter.name] = instance.Counters[counter.index].Value + } + values[i] = instanceValues + } + return values, nil +} + +func missingCounterNames(counterNames []string, counterIndices []counterIndex) []string { + matchedCounters := make(map[string]struct{}, len(counterIndices)) + for _, counter := range counterIndices { + matchedCounters[counter.name] = struct{}{} + } + + counters := make([]string, 0, len(counterNames)-len(matchedCounters)) + for _, counter := range counterNames { + if _, ok := matchedCounters[counter]; !ok { + counters = append(counters, counter) + } + } + return counters +} From c155d75d418fa5996bc5e4ddea70e3ac2f683f70 Mon Sep 17 00:00:00 2001 From: Sam DeHaan Date: Thu, 3 Apr 2025 16:36:11 -0400 Subject: [PATCH 5/7] Mark perflib library as windows build only --- receiver/hostmetricsreceiver/internal/perfcounters/doc.go | 2 +- .../internal/perfcounters/third_party/perflib/collector.go | 2 ++ .../internal/perfcounters/third_party/perflib/errors.go | 2 ++ .../internal/perfcounters/third_party/perflib/nametable.go | 2 ++ .../internal/perfcounters/third_party/perflib/pdh_const.go | 2 ++ .../internal/perfcounters/third_party/perflib/perflib.go | 2 ++ .../internal/perfcounters/third_party/perflib/perflib_test.go | 2 ++ .../internal/perfcounters/third_party/perflib/raw_types.go | 2 ++ .../internal/perfcounters/third_party/perflib/utf16.go | 2 ++ .../internal/perfcounters/third_party/perflib/utils.go | 2 ++ 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/doc.go b/receiver/hostmetricsreceiver/internal/perfcounters/doc.go index 8c4610a53f3a3..8bc0c5c6dde45 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/doc.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/doc.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package perfcounters is a thin wrapper around +// Package perfcounters is a thin wrapper around code vendored from // https://pkg.go.dev/github.com/prometheus-community/windows_exporter/pkg/perflib that // provides functions to scrape raw performance counter data, without // calculating rates or formatting them, from the registry. diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go index 026cc8ea70934..67343e9f05b8b 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/collector.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go index dcf4e0935f295..424dda1d9c6f1 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/errors.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import "errors" diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go index 55df47ca93853..d4d37de119825 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go index 3c71509746218..7d7a4d5bd627b 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/pdh_const.go @@ -1,3 +1,5 @@ +//go:build windows + // Constants copied from: // https://github.com/prometheus-community/windows_exporter/tree/4c7df1ccaf65df8662a2d67a7eff93019c94d98c/internal/pdh // Required for perflib functionality. diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go index 197195336bbe3..7f2ff2cbf7546 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" /* diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go index be92fb3186924..5912fe104b668 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/perflib_test.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib import ( diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go index b9ee787a91f3c..e6865b4f1dee6 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/raw_types.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go index 123b879ea96cd..1c58c145296ee 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utf16.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go index 73e11051343ec..67c6644cae21f 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/utils.go @@ -1,3 +1,5 @@ +//go:build windows + package perflib // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib" import ( From 58272b91c7afa4ef776fa4f9eac301c0fc62cc33 Mon Sep 17 00:00:00 2001 From: Sam DeHaan Date: Thu, 3 Apr 2025 16:47:42 -0400 Subject: [PATCH 6/7] Clean up go.mod from datadog components that use perflib --- connector/datadogconnector/go.sum | 2 -- exporter/datadogexporter/go.mod | 1 - exporter/datadogexporter/go.sum | 2 -- exporter/datadogexporter/integrationtest/go.mod | 1 - exporter/datadogexporter/integrationtest/go.sum | 2 -- 5 files changed, 8 deletions(-) diff --git a/connector/datadogconnector/go.sum b/connector/datadogconnector/go.sum index 89098f176fe43..b3b7df0db4fc6 100644 --- a/connector/datadogconnector/go.sum +++ b/connector/datadogconnector/go.sum @@ -826,8 +826,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus-community/windows_exporter v0.27.2 h1:/tdRTouPMVsC4qt8+s9NOPEm7L/9qdDxmasiETlx+Wk= -github.com/prometheus-community/windows_exporter v0.27.2/go.mod h1:8+T6hfv71nvgVIzguouXkIGoa15ni+uXHHULBOA2bZo= github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= diff --git a/exporter/datadogexporter/go.mod b/exporter/datadogexporter/go.mod index c8846254d007a..cc981e928ed81 100644 --- a/exporter/datadogexporter/go.mod +++ b/exporter/datadogexporter/go.mod @@ -354,7 +354,6 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus-community/windows_exporter v0.27.2 // indirect github.com/prometheus/alertmanager v0.27.0 // indirect github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect diff --git a/exporter/datadogexporter/go.sum b/exporter/datadogexporter/go.sum index 63635ccd7493c..4ebf41141ad91 100644 --- a/exporter/datadogexporter/go.sum +++ b/exporter/datadogexporter/go.sum @@ -941,8 +941,6 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus-community/windows_exporter v0.27.2 h1:/tdRTouPMVsC4qt8+s9NOPEm7L/9qdDxmasiETlx+Wk= -github.com/prometheus-community/windows_exporter v0.27.2/go.mod h1:8+T6hfv71nvgVIzguouXkIGoa15ni+uXHHULBOA2bZo= github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= diff --git a/exporter/datadogexporter/integrationtest/go.mod b/exporter/datadogexporter/integrationtest/go.mod index ca5550110ec99..aae824ccf09ed 100644 --- a/exporter/datadogexporter/integrationtest/go.mod +++ b/exporter/datadogexporter/integrationtest/go.mod @@ -323,7 +323,6 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus-community/windows_exporter v0.27.2 // indirect github.com/prometheus/alertmanager v0.27.0 // indirect github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect diff --git a/exporter/datadogexporter/integrationtest/go.sum b/exporter/datadogexporter/integrationtest/go.sum index 9317e0528a297..78c2cb6196bb1 100644 --- a/exporter/datadogexporter/integrationtest/go.sum +++ b/exporter/datadogexporter/integrationtest/go.sum @@ -941,8 +941,6 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus-community/windows_exporter v0.27.2 h1:/tdRTouPMVsC4qt8+s9NOPEm7L/9qdDxmasiETlx+Wk= -github.com/prometheus-community/windows_exporter v0.27.2/go.mod h1:8+T6hfv71nvgVIzguouXkIGoa15ni+uXHHULBOA2bZo= github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= From 5d863f1acf5d3dfa24de56ad9006ad113c08f0db Mon Sep 17 00:00:00 2001 From: Sam DeHaan Date: Sun, 6 Apr 2025 23:37:44 -0400 Subject: [PATCH 7/7] Remove unreachable panic --- .../internal/perfcounters/third_party/perflib/nametable.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go index d4d37de119825..1813f109e057f 100644 --- a/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go +++ b/receiver/hostmetricsreceiver/internal/perfcounters/third_party/perflib/nametable.go @@ -4,7 +4,6 @@ package perflib // import "github.com/open-telemetry/opentelemetry-collector-con import ( "bytes" - "fmt" "strconv" "sync" ) @@ -75,10 +74,6 @@ func (t *NameTable) initialize() { break } - if err != nil { - panic(fmt.Sprint("Invalid index ", index)) - } - indexInt, _ := strconv.Atoi(index) t.table.index[uint32(indexInt)] = desc