mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-14 08:56:36 +00:00
Refactor cpu collector to use perflib instead of WMI.
This commit is contained in:
21
vendor/github.com/leoluk/perflib_exporter/LICENSE
generated
vendored
Normal file
21
vendor/github.com/leoluk/perflib_exporter/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||
184
vendor/github.com/leoluk/perflib_exporter/collector/collector.go
generated
vendored
Normal file
184
vendor/github.com/leoluk/perflib_exporter/collector/collector.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/leoluk/perflib_exporter/perflib"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/log"
|
||||
)
|
||||
|
||||
// ...
|
||||
const (
|
||||
Namespace = "perflib"
|
||||
|
||||
// Conversion factors
|
||||
hundredNsToSecondsScaleFactor = 1 / 1e7
|
||||
)
|
||||
|
||||
// Collector is the interface a collector has to implement.
|
||||
type Collector interface {
|
||||
// Get new metrics and expose them via prometheus registry.
|
||||
Collect(ch chan<- prometheus.Metric) (err error)
|
||||
}
|
||||
|
||||
type CounterKey struct {
|
||||
ObjectIndex uint
|
||||
CounterIndex uint
|
||||
CounterType uint32 // This is a bit mask
|
||||
}
|
||||
|
||||
func NewCounterKey(object *perflib.PerfObject, def *perflib.PerfCounterDef) CounterKey {
|
||||
return CounterKey{object.NameIndex, def.NameIndex, def.CounterType}
|
||||
}
|
||||
|
||||
type PerflibCollector struct {
|
||||
perflibQuery string
|
||||
perflibDescs map[CounterKey]*prometheus.Desc
|
||||
}
|
||||
|
||||
func NewPerflibCollector(query string) (c PerflibCollector) {
|
||||
c.perflibQuery = query
|
||||
|
||||
objects, err := perflib.QueryPerformanceData(c.perflibQuery)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Debugf("Number of objects: %d", len(objects))
|
||||
|
||||
c.perflibDescs = make(map[CounterKey]*prometheus.Desc)
|
||||
|
||||
for _, object := range objects {
|
||||
for _, def := range object.CounterDefs {
|
||||
desc := descFromCounterDef(*object, *def)
|
||||
|
||||
key := NewCounterKey(object, def)
|
||||
c.perflibDescs[key] = desc
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c PerflibCollector) Collect(ch chan<- prometheus.Metric) (err error) {
|
||||
// TODO QueryPerformanceData timing metric
|
||||
objects, err := perflib.QueryPerformanceData(c.perflibQuery)
|
||||
|
||||
if err != nil {
|
||||
// TODO - we shouldn't panic if a single call fails
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Debugf("Number of objects: %d", len(objects))
|
||||
|
||||
for _, object := range objects {
|
||||
n := object.NameIndex
|
||||
|
||||
for _, instance := range object.Instances {
|
||||
name := instance.Name
|
||||
|
||||
// _Total metrics do not fit into the Prometheus model - we try to merge similar
|
||||
// metrics and give them labels, so you'd sum() them instead. Having a _Total label
|
||||
// would make
|
||||
if strings.HasSuffix(name, "_Total") || strings.HasPrefix(name, "Total") {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, counter := range instance.Counters {
|
||||
if IsDefPromotedLabel(n, counter.Def.NameIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
if counter == nil {
|
||||
log.Debugf("nil counter for %s -> %s", object.Name, instance.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if counter.Def.NameIndex == 0 {
|
||||
log.Debugf("null counter index for %s -> %s", object.Name, instance.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if counter.Def.Name == "" {
|
||||
log.Debugf("no counter name for %s -> %s", object.Name, instance.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if counter.Def.Name == "No name" {
|
||||
log.Debugf("no name counter %s -> %s -> %s", object.Name, instance.Name, counter.Def.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
key := NewCounterKey(object, counter.Def)
|
||||
|
||||
desc, ok := c.perflibDescs[key]
|
||||
|
||||
if !ok {
|
||||
log.Debugf("missing metric description for counter %s -> %s -> %s", object.Name, instance.Name, counter.Def.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
labels := []string{name}
|
||||
|
||||
if len(object.Instances) == 1 {
|
||||
labels = []string{}
|
||||
}
|
||||
|
||||
if HasPromotedLabels(n) {
|
||||
labels = append(labels, PromotedLabelValuesForInstance(n, instance)...)
|
||||
}
|
||||
|
||||
// TODO - Label merging needs to be fixed for [230] Process
|
||||
//if HasMergedLabels(n) {
|
||||
// _, value := MergedMetricForInstance(n, counter.Def.NameIndex)
|
||||
//
|
||||
// // Null string in definition means we should skip this metric (it's probably a sum)
|
||||
// if value == "" {
|
||||
// log.Debugf("Skipping %d -> %s (empty merge label)", n, counter.Def.NameIndex)
|
||||
// continue
|
||||
// }
|
||||
// labels = append(labels, value)
|
||||
//}
|
||||
|
||||
valueType, err := GetPrometheusValueType(counter.Def.CounterType)
|
||||
|
||||
if err != nil {
|
||||
// TODO - Is this too verbose? There will always be counter types we don't support
|
||||
log.Debug(err)
|
||||
continue
|
||||
}
|
||||
|
||||
value := float64(counter.Value)
|
||||
|
||||
if counter.Def.IsNanosecondCounter {
|
||||
value = value * hundredNsToSecondsScaleFactor
|
||||
}
|
||||
|
||||
if IsElapsedTime(counter.Def.CounterType) {
|
||||
// convert from Windows timestamp (1 jan 1601) to unix timestamp (1 jan 1970)
|
||||
value = float64(counter.Value-116444736000000000) / float64(object.Frequency)
|
||||
}
|
||||
|
||||
metric := prometheus.MustNewConstMetric(
|
||||
desc,
|
||||
valueType,
|
||||
value,
|
||||
labels...,
|
||||
)
|
||||
|
||||
ch <- metric
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*ch <- prometheus.MustNewConstMetric(
|
||||
prometheus.NewDesc(),
|
||||
prometheus.CounterValue,
|
||||
float64(0),
|
||||
"ds_client",
|
||||
)*/
|
||||
|
||||
return nil
|
||||
}
|
||||
90
vendor/github.com/leoluk/perflib_exporter/collector/mangle.go
generated
vendored
Normal file
90
vendor/github.com/leoluk/perflib_exporter/collector/mangle.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/leoluk/perflib_exporter/perflib"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func manglePerflibName(s string) string {
|
||||
s = strings.ToLower(s)
|
||||
s = strings.Replace(s, " ", "_", -1)
|
||||
s = strings.Replace(s, ".", "", -1)
|
||||
s = strings.Replace(s, "(", "", -1)
|
||||
s = strings.Replace(s, ")", "", -1)
|
||||
s = strings.Replace(s, "+", "", -1)
|
||||
s = strings.Replace(s, "-", "", -1)
|
||||
s = strings.Replace(s, ",", "", -1)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func manglePerflibCounterName(s string) string {
|
||||
s = manglePerflibName(s)
|
||||
|
||||
s = strings.Replace(s, "total_", "", -1)
|
||||
s = strings.Replace(s, "_total", "", -1)
|
||||
s = strings.Replace(s, "/second", "", -1)
|
||||
s = strings.Replace(s, "/sec", "", -1)
|
||||
s = strings.Replace(s, "_%", "", -1)
|
||||
s = strings.Replace(s, "%_", "", -1)
|
||||
s = strings.Replace(s, "/", "_per_", -1)
|
||||
s = strings.Replace(s, "&", "and", -1)
|
||||
s = strings.Replace(s, "#_of_", "", -1)
|
||||
s = strings.Replace(s, ":", "", -1)
|
||||
s = strings.Replace(s, "__", "_", -1)
|
||||
|
||||
s = strings.Trim(s, " _")
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func MakePrometheusLabel(def *perflib.PerfCounterDef) (s string) {
|
||||
s = manglePerflibCounterName(def.Name)
|
||||
|
||||
if len(s) > 0 {
|
||||
if IsCounter(def.CounterType) {
|
||||
s += "_total"
|
||||
} else if IsBaseValue(def.CounterType) && !strings.HasSuffix(s, "_base") {
|
||||
s += "_max"
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func pdhNameFromCounterDef(obj perflib.PerfObject, def perflib.PerfCounterDef) string {
|
||||
return fmt.Sprintf(`\%s(*)\%s`, obj.Name, def.Name)
|
||||
}
|
||||
|
||||
func descFromCounterDef(obj perflib.PerfObject, def perflib.PerfCounterDef) *prometheus.Desc {
|
||||
subsystem := manglePerflibName(obj.Name)
|
||||
counterName := MakePrometheusLabel(&def)
|
||||
|
||||
labels := []string{"name"}
|
||||
|
||||
if len(obj.Instances) == 1 {
|
||||
labels = []string{}
|
||||
}
|
||||
|
||||
if HasPromotedLabels(obj.NameIndex) {
|
||||
labels = append(labels, PromotedLabelsForObject(obj.NameIndex)...)
|
||||
}
|
||||
|
||||
// TODO - Label merging needs to be fixed for [230] Process
|
||||
//if HasMergedLabels(obj.NameIndex) {
|
||||
// s, labelsForObject := MergedLabelsForInstance(obj.NameIndex, def.NameIndex)
|
||||
// counterName = s
|
||||
// labels = append(labels, labelsForObject)
|
||||
//}
|
||||
|
||||
return prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, counterName),
|
||||
fmt.Sprintf("perflib metric: %s (see /dump for docs) [%d]",
|
||||
pdhNameFromCounterDef(obj, def), def.NameIndex),
|
||||
labels,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
91
vendor/github.com/leoluk/perflib_exporter/collector/mapper.go
generated
vendored
Normal file
91
vendor/github.com/leoluk/perflib_exporter/collector/mapper.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
var supportedCounterTypes = map[uint32]prometheus.ValueType{
|
||||
PERF_COUNTER_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||
PERF_COUNTER_LARGE_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||
PERF_COUNTER_RAWCOUNT: prometheus.GaugeValue,
|
||||
PERF_COUNTER_LARGE_RAWCOUNT: prometheus.GaugeValue,
|
||||
PERF_COUNTER_DELTA: prometheus.CounterValue,
|
||||
PERF_COUNTER_COUNTER: prometheus.CounterValue,
|
||||
PERF_COUNTER_BULK_COUNT: prometheus.CounterValue,
|
||||
PERF_RAW_FRACTION: prometheus.GaugeValue,
|
||||
PERF_LARGE_RAW_FRACTION: prometheus.GaugeValue,
|
||||
PERF_100NSEC_TIMER: prometheus.CounterValue,
|
||||
PERF_PRECISION_100NS_TIMER: prometheus.CounterValue,
|
||||
PERF_SAMPLE_FRACTION: prometheus.GaugeValue,
|
||||
PERF_100NSEC_TIMER_INV: prometheus.CounterValue,
|
||||
PERF_ELAPSED_TIME: prometheus.GaugeValue,
|
||||
PERF_SAMPLE_BASE: prometheus.GaugeValue,
|
||||
PERF_RAW_BASE: prometheus.GaugeValue,
|
||||
PERF_LARGE_RAW_BASE: prometheus.GaugeValue,
|
||||
}
|
||||
|
||||
func IsCounter(counterType uint32) bool {
|
||||
return supportedCounterTypes[counterType] == prometheus.CounterValue
|
||||
}
|
||||
|
||||
func IsBaseValue(counterType uint32) bool {
|
||||
return counterType == PERF_SAMPLE_BASE || counterType == PERF_RAW_BASE || counterType == PERF_LARGE_RAW_BASE
|
||||
}
|
||||
|
||||
func IsElapsedTime(counterType uint32) bool {
|
||||
return counterType == PERF_ELAPSED_TIME
|
||||
}
|
||||
|
||||
func GetPrometheusValueType(counterType uint32) (prometheus.ValueType, error) {
|
||||
val, ok := supportedCounterTypes[counterType]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("counter type %#08x is not supported", counterType)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
38
vendor/github.com/leoluk/perflib_exporter/collector/merge.go
generated
vendored
Normal file
38
vendor/github.com/leoluk/perflib_exporter/collector/merge.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package collector
|
||||
|
||||
import "fmt"
|
||||
|
||||
var mergedDefinitions = map[uint]map[string]map[uint]string{
|
||||
230: {
|
||||
"processor_time_total": {
|
||||
0: "mode",
|
||||
6: "", // Processor Time (drop)
|
||||
142: "user", // User Time
|
||||
144: "privileged", // Privileged Time
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Return if a given object has merge definitions
|
||||
func HasMergedLabels(index uint) bool {
|
||||
_, ok := mergedDefinitions[index]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Return a list of merged label names for an instance
|
||||
func MergedLabelsForInstance(objIndex uint, def uint) (name string, labelName string) {
|
||||
return MergedMetricForInstance(objIndex, 0)
|
||||
}
|
||||
|
||||
// Return merged metric name and label value for an instance
|
||||
func MergedMetricForInstance(objIndex uint, def uint) (name string, label string) {
|
||||
for k, v := range mergedDefinitions[objIndex] {
|
||||
for n := range v {
|
||||
if def == n {
|
||||
return k, v[n]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("No merge definition for obj %d, inst %d", objIndex, def))
|
||||
}
|
||||
56
vendor/github.com/leoluk/perflib_exporter/collector/promote.go
generated
vendored
Normal file
56
vendor/github.com/leoluk/perflib_exporter/collector/promote.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/leoluk/perflib_exporter/perflib"
|
||||
)
|
||||
|
||||
var labelPromotionLabels = map[uint][]string{
|
||||
230: {
|
||||
"process_id",
|
||||
"creating_process_id",
|
||||
},
|
||||
}
|
||||
var labelPromotionValues = map[uint][]uint{
|
||||
230: {
|
||||
784, // process_id
|
||||
1410, // creating_process_id
|
||||
},
|
||||
}
|
||||
|
||||
// Get a list of promoted labels for an object
|
||||
func PromotedLabelsForObject(index uint) []string {
|
||||
return labelPromotionLabels[index]
|
||||
}
|
||||
|
||||
// Get a list of label values for a given object and instance
|
||||
func PromotedLabelValuesForInstance(index uint, instance *perflib.PerfInstance) []string {
|
||||
values := make([]string, len(labelPromotionValues[index]))
|
||||
|
||||
for _, c := range instance.Counters {
|
||||
for i, v := range labelPromotionValues[index] {
|
||||
if c.Def.NameIndex == v {
|
||||
values[i] = strconv.Itoa(int(c.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
// Return if a given object has label promotion definitions
|
||||
func HasPromotedLabels(index uint) bool {
|
||||
_, ok := labelPromotionLabels[index]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Return if a given definition is a promoted label for an object
|
||||
func IsDefPromotedLabel(objIndex uint, def uint) bool {
|
||||
for _, v := range labelPromotionValues[objIndex] {
|
||||
if v == def {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
75
vendor/github.com/leoluk/perflib_exporter/perflib/nametable.go
generated
vendored
Normal file
75
vendor/github.com/leoluk/perflib_exporter/perflib/nametable.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package perflib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type nameTableLookuper interface {
|
||||
LookupName() string
|
||||
LookupHelp() string
|
||||
}
|
||||
|
||||
func (p *perfObjectType) LookupName() string {
|
||||
return counterNameTable.LookupString(p.ObjectNameTitleIndex)
|
||||
}
|
||||
|
||||
func (p *perfObjectType) LookupHelp() string {
|
||||
return helpNameTable.LookupString(p.ObjectHelpTitleIndex)
|
||||
}
|
||||
|
||||
type NameTable struct {
|
||||
byIndex map[uint32]string
|
||||
byString map[string]uint32
|
||||
}
|
||||
|
||||
func (t *NameTable) LookupString(index uint32) string {
|
||||
return t.byIndex[index]
|
||||
}
|
||||
|
||||
func (t *NameTable) LookupIndex(str string) uint32 {
|
||||
return t.byString[str]
|
||||
}
|
||||
|
||||
// Query a perflib name table from the registry. Specify the type and the language
|
||||
// code (i.e. "Counter 009" or "Help 009") for English language.
|
||||
func QueryNameTable(tableName string) *NameTable {
|
||||
nameTable := new(NameTable)
|
||||
nameTable.byIndex = make(map[uint32]string)
|
||||
|
||||
buffer, err := queryRawData(tableName)
|
||||
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
|
||||
}
|
||||
|
||||
indexInt, _ := strconv.Atoi(index)
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprint("Invalid index ", index))
|
||||
}
|
||||
|
||||
nameTable.byIndex[uint32(indexInt)] = desc
|
||||
}
|
||||
|
||||
nameTable.byString = make(map[string]uint32)
|
||||
|
||||
for k, v := range nameTable.byIndex {
|
||||
nameTable.byString[v] = k
|
||||
}
|
||||
|
||||
return nameTable
|
||||
}
|
||||
460
vendor/github.com/leoluk/perflib_exporter/perflib/perflib.go
generated
vendored
Normal file
460
vendor/github.com/leoluk/perflib_exporter/perflib/perflib.go
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
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 registry 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
|
||||
|
||||
*/
|
||||
package perflib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO: There's a LittleEndian field in the PERF header - we ought to check it
|
||||
var bo = binary.LittleEndian
|
||||
|
||||
var counterNameTable NameTable
|
||||
var helpNameTable NameTable
|
||||
|
||||
// Top-level performance object (like "Process").
|
||||
type PerfObject struct {
|
||||
Name string
|
||||
// Same index you pass to QueryPerformanceData
|
||||
NameIndex uint
|
||||
HelpText string
|
||||
HelpTextIndex uint
|
||||
Instances []*PerfInstance
|
||||
CounterDefs []*PerfCounterDef
|
||||
|
||||
Frequency int64
|
||||
|
||||
rawData *perfObjectType
|
||||
}
|
||||
|
||||
// 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
|
||||
HelpText string
|
||||
HelpTextIndex 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
|
||||
|
||||
rawData *perfCounterDefinition
|
||||
}
|
||||
|
||||
type PerfCounter struct {
|
||||
Value int64
|
||||
Def *PerfCounterDef
|
||||
}
|
||||
|
||||
// Error value returned by RegQueryValueEx if the buffer isn't sufficiently large
|
||||
const errorMoreData = syscall.Errno(234)
|
||||
|
||||
var (
|
||||
bufLenGlobal = uint32(400000)
|
||||
bufLenCostly = uint32(2000000)
|
||||
)
|
||||
|
||||
// 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:
|
||||
// TODO: 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 := syscall.UTF16PtrFromString(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode query string: %v", err)
|
||||
}
|
||||
|
||||
for {
|
||||
bufLen := uint32(len(buffer))
|
||||
|
||||
err := syscall.RegQueryValueEx(
|
||||
syscall.HKEY_PERFORMANCE_DATA,
|
||||
name,
|
||||
nil,
|
||||
&valType,
|
||||
(*byte)(unsafe.Pointer(&buffer[0])),
|
||||
&bufLen)
|
||||
|
||||
if err == errorMoreData {
|
||||
newBuffer := make([]byte, len(buffer)+16384)
|
||||
copy(newBuffer, buffer)
|
||||
buffer = newBuffer
|
||||
continue
|
||||
} else if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
return nil, fmt.Errorf("ReqQueryValueEx failed: %v 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
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Initialize global name tables
|
||||
// TODO: 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)
|
||||
|
||||
counterNameTable = *QueryNameTable("Counter 009")
|
||||
helpNameTable = *QueryNameTable("Help 009")
|
||||
}
|
||||
|
||||
/*
|
||||
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) ([]*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, 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)
|
||||
objects := make([]*PerfObject, numObjects)
|
||||
|
||||
objOffset := int64(header.HeaderLength)
|
||||
|
||||
for i := 0; i < numObjects; i++ {
|
||||
r.Seek(objOffset, io.SeekStart)
|
||||
|
||||
obj := new(perfObjectType)
|
||||
obj.BinaryReadFrom(r)
|
||||
|
||||
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: obj.LookupName(),
|
||||
NameIndex: uint(obj.ObjectNameTitleIndex),
|
||||
HelpText: obj.LookupHelp(),
|
||||
HelpTextIndex: uint(obj.ObjectHelpTitleIndex),
|
||||
Instances: instances,
|
||||
CounterDefs: counterDefs,
|
||||
Frequency: obj.PerfFreq,
|
||||
rawData: obj,
|
||||
}
|
||||
|
||||
for i := 0; i < numCounterDefs; i++ {
|
||||
def := new(perfCounterDefinition)
|
||||
def.BinaryReadFrom(r)
|
||||
|
||||
counterDefs[i] = &PerfCounterDef{
|
||||
Name: def.LookupName(),
|
||||
NameIndex: uint(def.CounterNameTitleIndex),
|
||||
HelpText: def.LookupHelp(),
|
||||
HelpTextIndex: uint(def.CounterHelpTitleIndex),
|
||||
rawData: def,
|
||||
|
||||
CounterType: def.CounterType,
|
||||
|
||||
IsCounter: def.CounterType&0x400 == 0x400,
|
||||
IsBaseValue: def.CounterType&0x00030000 == 0x00030000,
|
||||
IsNanosecondCounter: def.CounterType&0x00100000 == 0x00100000,
|
||||
}
|
||||
}
|
||||
|
||||
if obj.NumInstances <= 0 {
|
||||
blockOffset := objOffset + int64(obj.DefinitionLength)
|
||||
r.Seek(blockOffset, io.SeekStart)
|
||||
|
||||
_, counters := parseCounterBlock(buffer, r, blockOffset, counterDefs)
|
||||
|
||||
instances[0] = &PerfInstance{
|
||||
Name: "",
|
||||
Counters: counters,
|
||||
rawData: nil,
|
||||
rawCounterBlock: nil,
|
||||
}
|
||||
} else {
|
||||
instOffset := objOffset + int64(obj.DefinitionLength)
|
||||
|
||||
for i := 0; i < numInstances; i++ {
|
||||
r.Seek(instOffset, io.SeekStart)
|
||||
|
||||
inst := new(perfInstanceDefinition)
|
||||
inst.BinaryReadFrom(r)
|
||||
|
||||
name, _ := readUTF16StringAtPos(r, instOffset+int64(inst.NameOffset), inst.NameLength)
|
||||
pos := instOffset + int64(inst.ByteLength)
|
||||
offset, counters := parseCounterBlock(buffer, r, pos, counterDefs)
|
||||
|
||||
instances[i] = &PerfInstance{
|
||||
Name: name,
|
||||
Counters: counters,
|
||||
rawData: inst,
|
||||
}
|
||||
|
||||
instOffset = pos + offset
|
||||
}
|
||||
}
|
||||
|
||||
// Next perfObjectType
|
||||
objOffset += int64(obj.TotalByteLength)
|
||||
}
|
||||
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
func parseCounterBlock(b []byte, r io.ReadSeeker, pos int64, defs []*PerfCounterDef) (int64, []*PerfCounter) {
|
||||
r.Seek(pos, io.SeekStart)
|
||||
block := new(perfCounterBlock)
|
||||
block.BinaryReadFrom(r)
|
||||
|
||||
counters := make([]*PerfCounter, len(defs))
|
||||
|
||||
for i, def := range defs {
|
||||
valueOffset := pos + int64(def.rawData.CounterOffset)
|
||||
value := convertCounterValue(def.rawData, b, valueOffset)
|
||||
|
||||
counters[i] = &PerfCounter{
|
||||
Value: value,
|
||||
Def: def,
|
||||
}
|
||||
}
|
||||
|
||||
return int64(block.ByteLength), counters
|
||||
}
|
||||
|
||||
func convertCounterValue(counterDef *perfCounterDefinition, buffer []byte, valueOffset int64) (value 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:
|
||||
value = int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)]))
|
||||
case 8:
|
||||
value = int64(bo.Uint64(buffer[valueOffset:(valueOffset + 8)]))
|
||||
default:
|
||||
value = int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)]))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Sort slice of objects by index. This is useful for displaying
|
||||
// a human-readable list or dump, but unnecessary otherwise.
|
||||
func SortObjects(p []*PerfObject) {
|
||||
sort.Slice(p, func(i, j int) bool {
|
||||
return p[i].NameIndex < p[j].NameIndex
|
||||
})
|
||||
|
||||
}
|
||||
180
vendor/github.com/leoluk/perflib_exporter/perflib/raw_types.go
generated
vendored
Normal file
180
vendor/github.com/leoluk/perflib_exporter/perflib/raw_types.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
package perflib
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type binaryReaderFrom interface {
|
||||
BinaryReadFrom(r io.Reader) error
|
||||
}
|
||||
|
||||
/*
|
||||
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 syscall.Systemtime
|
||||
_ uint32 // TODO
|
||||
PerfTime int64
|
||||
PerfFreq int64
|
||||
PerfTime100nSec int64
|
||||
SystemNameLength uint32
|
||||
SystemNameOffset uint32
|
||||
}
|
||||
|
||||
func (p *perfDataBlock) BinaryReadFrom(r io.Reader) error {
|
||||
return binary.Read(r, bo, p)
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
|
||||
func (p *perfCounterDefinition) LookupHelp() string {
|
||||
return helpNameTable.LookupString(p.CounterHelpTitleIndex)
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
49
vendor/github.com/leoluk/perflib_exporter/perflib/utf16.go
generated
vendored
Normal file
49
vendor/github.com/leoluk/perflib_exporter/perflib/utf16.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package perflib
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// 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 syscall.UTF16ToString(value), nil
|
||||
}
|
||||
|
||||
// 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 syscall.UTF16ToString(out), nil
|
||||
}
|
||||
Reference in New Issue
Block a user