summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/pkg/stm/stm.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/pkg/stm/stm.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/pkg/stm/stm.go b/src/go/collectors/go.d.plugin/pkg/stm/stm.go
new file mode 100644
index 000000000..7d07ba9a4
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/pkg/stm/stm.go
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package stm
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+const (
+ fieldTagName = "stm"
+ structKey = "STMKey"
+)
+
+type (
+ Value interface {
+ WriteTo(rv map[string]int64, key string, mul, div int)
+ }
+)
+
+// ToMap converts struct to a map[string]int64 based on 'stm' tags
+func ToMap(s ...interface{}) map[string]int64 {
+ rv := map[string]int64{}
+ for _, v := range s {
+ value := reflect.Indirect(reflect.ValueOf(v))
+ toMap(value, rv, "", 1, 1)
+ }
+ return rv
+}
+
+func toMap(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ if !value.IsValid() {
+ log.Panicf("value is not valid key=%s", key)
+ }
+ if value.CanInterface() {
+ val, ok := value.Interface().(Value)
+ if ok {
+ val.WriteTo(rv, key, mul, div)
+ return
+ }
+ }
+ switch value.Kind() {
+ case reflect.Ptr:
+ convertPtr(value, rv, key, mul, div)
+ case reflect.Struct:
+ convertStruct(value, rv, key)
+ case reflect.Array, reflect.Slice:
+ convertArraySlice(value, rv, key, mul, div)
+ case reflect.Map:
+ convertMap(value, rv, key, mul, div)
+ case reflect.Bool:
+ convertBool(value, rv, key)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ convertInteger(value, rv, key, mul, div)
+ case reflect.Float32, reflect.Float64:
+ convertFloat(value, rv, key, mul, div)
+ case reflect.Interface:
+ convertInterface(value, rv, key, mul, div)
+ default:
+ log.Panicf("unsupported data type: %v", value.Kind())
+ }
+}
+
+func convertPtr(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ if !value.IsNil() {
+ toMap(value.Elem(), rv, key, mul, div)
+ }
+}
+
+func convertStruct(value reflect.Value, rv map[string]int64, key string) {
+ t := value.Type()
+ k := value.FieldByName(structKey)
+ if k.Kind() == reflect.String {
+ key = joinPrefix(key, k.String())
+ }
+ for i := 0; i < t.NumField(); i++ {
+ ft := t.Field(i)
+ tag, ok := ft.Tag.Lookup(fieldTagName)
+ if !ok || ft.Name == structKey {
+ continue
+ }
+ value := value.Field(i)
+ prefix, mul, div := parseTag(tag)
+ toMap(value, rv, joinPrefix(key, prefix), mul, div)
+ }
+}
+
+func convertMap(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ if value.IsNil() {
+ log.Panicf("value is nil key=%s", key)
+ }
+ for _, k := range value.MapKeys() {
+ toMap(value.MapIndex(k), rv, joinPrefix(key, k.String()), mul, div)
+ }
+}
+
+func convertArraySlice(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ for i := 0; i < value.Len(); i++ {
+ toMap(value.Index(i), rv, key, mul, div)
+ }
+}
+
+func convertBool(value reflect.Value, rv map[string]int64, key string) {
+ if _, ok := rv[key]; ok {
+ log.Panic("duplicate key: ", key)
+ }
+ if value.Bool() {
+ rv[key] = 1
+ } else {
+ rv[key] = 0
+ }
+}
+
+func convertInteger(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ if _, ok := rv[key]; ok {
+ log.Panic("duplicate key: ", key)
+ }
+ intVal := value.Int()
+ rv[key] = intVal * int64(mul) / int64(div)
+}
+
+func convertFloat(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ if _, ok := rv[key]; ok {
+ log.Panic("duplicate key: ", key)
+ }
+ floatVal := value.Float()
+ rv[key] = int64(floatVal * float64(mul) / float64(div))
+}
+
+func convertInterface(value reflect.Value, rv map[string]int64, key string, mul, div int) {
+ fv := reflect.ValueOf(value.Interface())
+ toMap(fv, rv, key, mul, div)
+}
+
+func joinPrefix(prefix, key string) string {
+ if prefix == "" {
+ return key
+ }
+ if key == "" {
+ return prefix
+ }
+ return prefix + "_" + key
+}
+
+func parseTag(tag string) (prefix string, mul int, div int) {
+ tokens := strings.Split(tag, ",")
+ mul = 1
+ div = 1
+ var err error
+ switch len(tokens) {
+ case 3:
+ div, err = strconv.Atoi(tokens[2])
+ if err != nil {
+ log.Panic(err)
+ }
+ fallthrough
+ case 2:
+ mul, err = strconv.Atoi(tokens[1])
+ if err != nil {
+ log.Panic(err)
+ }
+ fallthrough
+ case 1:
+ prefix = tokens[0]
+ default:
+ log.Panic(fmt.Errorf("invalid tag format: %s", tag))
+ }
+ return
+}