windows_exporter/collector/perflib.go

90 lines
2.3 KiB
Go

package collector
import (
"fmt"
"reflect"
perflibCollector "github.com/leoluk/perflib_exporter/collector"
"github.com/leoluk/perflib_exporter/perflib"
"github.com/prometheus/common/log"
)
func getPerflibSnapshot() (map[string]*perflib.PerfObject, error) {
objects, err := perflib.QueryPerformanceData("Global")
if err != nil {
return nil, err
}
indexed := make(map[string]*perflib.PerfObject)
for _, obj := range objects {
indexed[obj.Name] = obj
}
return indexed, nil
}
func unmarshalObject(obj *perflib.PerfObject, vs interface{}) error {
if obj == nil {
return fmt.Errorf("counter not found")
}
rv := reflect.ValueOf(vs)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return fmt.Errorf("%v is nil or not a pointer to slice", reflect.TypeOf(vs))
}
ev := rv.Elem()
if ev.Kind() != reflect.Slice {
return fmt.Errorf("%v is not slice", reflect.TypeOf(vs))
}
// Ensure sufficient length
if ev.Cap() < len(obj.Instances) {
nvs := reflect.MakeSlice(ev.Type(), len(obj.Instances), len(obj.Instances))
ev.Set(nvs)
}
for idx, instance := range obj.Instances {
target := ev.Index(idx)
rt := target.Type()
counters := make(map[string]*perflib.PerfCounter, len(instance.Counters))
for _, ctr := range instance.Counters {
if ctr.Def.IsBaseValue && !ctr.Def.IsNanosecondCounter {
counters[ctr.Def.Name+"_Base"] = ctr
} else {
counters[ctr.Def.Name] = ctr
}
}
for i := 0; i < target.NumField(); i++ {
f := rt.Field(i)
tag := f.Tag.Get("perflib")
if tag == "" {
continue
}
ctr, found := counters[tag]
if !found {
log.Debugf("missing counter %q, has %v", tag, counters)
return fmt.Errorf("could not find counter %q on instance", tag)
}
if !target.Field(i).CanSet() {
return fmt.Errorf("tagged field %v cannot be written to", f)
}
switch ctr.Def.CounterType {
case perflibCollector.PERF_ELAPSED_TIME:
target.Field(i).SetFloat(float64(ctr.Value-windowsEpoch) / float64(obj.Frequency))
case perflibCollector.PERF_100NSEC_TIMER, perflibCollector.PERF_PRECISION_100NS_TIMER:
target.Field(i).SetFloat(float64(ctr.Value) * ticksToSecondsScaleFactor)
default:
target.Field(i).SetFloat(float64(ctr.Value))
}
}
if instance.Name != "" && target.FieldByName("Name").CanSet() {
target.FieldByName("Name").SetString(instance.Name)
}
}
return nil
}