statsclient: fix retrieving stats type
[govpp.git] / adapter / statsclient / statseg_v2.go
index d52077f..0780248 100644 (file)
@@ -15,6 +15,8 @@
 package statsclient
 
 import (
+       "bytes"
+       "encoding/binary"
        "sync/atomic"
        "unsafe"
 
@@ -36,8 +38,9 @@ type sharedHeaderV2 struct {
 }
 
 type statSegDirectoryEntryV2 struct {
-       directoryType statDirectoryType
+       directoryType dirType
        // unionData can represent:
+       // - symlink indexes
        // - index
        // - value
        // - pointer to data
@@ -64,13 +67,13 @@ func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) {
        }
 }
 
-func (ss *statSegmentV2) GetDirectoryVector() unsafe.Pointer {
+func (ss *statSegmentV2) GetDirectoryVector() dirVector {
        header := ss.loadSharedHeader(ss.sharedHeader)
-       return ss.adjust(unsafe.Pointer(&header.dirVector))
+       return ss.adjust(dirVector(&header.dirVector))
 }
 
-func (ss *statSegmentV2) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) {
-       statSegDir := unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
+func (ss *statSegmentV2) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) {
+       statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
        dir := (*statSegDirectoryEntryV2)(statSegDir)
        var name []byte
        for n := 0; n < len(dir.name); n++ {
@@ -87,14 +90,16 @@ func (ss *statSegmentV2) GetEpoch() (int64, bool) {
        return sh.epoch, sh.inProgress != 0
 }
 
-func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
-       dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
-       if dirEntry.unionData == 0 {
-               debugf("data value or pointer not defined for %s", dirEntry.name)
+func (ss *statSegmentV2) CopyEntryData(segment dirSegment, index uint32) adapter.Stat {
+       dirEntry := (*statSegDirectoryEntryV2)(segment)
+       typ := adapter.StatType(dirEntry.directoryType)
+       // skip zero pointer value
+       if typ != statDirScalarIndex && typ != statDirEmpty && dirEntry.unionData == 0 {
+               debugf("data pointer not defined for %s", dirEntry.name)
                return nil
        }
 
-       switch adapter.StatType(dirEntry.directoryType) {
+       switch typ {
        case statDirScalarIndex:
                return adapter.ScalarStat(dirEntry.unionData)
 
@@ -105,7 +110,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
                        return nil
                }
                vecLen := *(*uint32)(vectorLen(dirVector))
-               var errData adapter.Counter
+               var errData []adapter.Counter
                for i := uint32(0); i < vecLen; i++ {
                        cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
                        cbVal := ss.adjust(vectorLen(cb))
@@ -115,12 +120,12 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
                        }
                        offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
                        val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
-                       errData += val
+                       errData = append(errData, val)
                }
                return adapter.ErrorStat(errData)
 
        case statDirCounterVectorSimple:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -135,17 +140,27 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
                                continue
                        }
                        counterVectorLength := *(*uint32)(vectorLen(counterVector))
-                       data[i] = make([]adapter.Counter, counterVectorLength)
-                       for j := uint32(0); j < counterVectorLength; j++ {
-                               offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
-                               val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
-                               data[i][j] = val
+                       if index == ^uint32(0) {
+                               data[i] = make([]adapter.Counter, counterVectorLength)
+                               for j := uint32(0); j < counterVectorLength; j++ {
+                                       offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
+                                       data[i][j] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
+                               }
+                       } else {
+                               data[i] = make([]adapter.Counter, 1) // expect single value
+                               for j := uint32(0); j < counterVectorLength; j++ {
+                                       offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
+                                       if index == j {
+                                               data[i][0] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
+                                               break
+                                       }
+                               }
                        }
                }
                return adapter.SimpleCounterStat(data)
 
        case statDirCounterVectorCombined:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -160,17 +175,27 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
                                continue
                        }
                        counterVectorLength := *(*uint32)(vectorLen(counterVector))
-                       data[i] = make([]adapter.CombinedCounter, counterVectorLength)
-                       for j := uint32(0); j < counterVectorLength; j++ {
-                               offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
-                               val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
-                               data[i][j] = val
+                       if index == ^uint32(0) {
+                               data[i] = make([]adapter.CombinedCounter, counterVectorLength)
+                               for j := uint32(0); j < counterVectorLength; j++ {
+                                       offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
+                                       data[i][j] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
+                               }
+                       } else {
+                               data[i] = make([]adapter.CombinedCounter, 1) // expect single value pair
+                               for j := uint32(0); j < counterVectorLength; j++ {
+                                       offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
+                                       if index == j {
+                                               data[i][0] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
+                                               break
+                                       }
+                               }
                        }
                }
                return adapter.CombinedCounterStat(data)
 
        case statDirNameVector:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -202,8 +227,25 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
                return adapter.NameStat(data)
 
        case statDirEmpty:
+               return adapter.EmptyStat("<none>")
                // no-op
 
+       case statDirSymlink:
+               // prevent recursion loops
+               if index != ^uint32(0) {
+                       debugf("received symlink with defined item index")
+                       return nil
+               }
+               i1, i2 := ss.getSymlinkIndexes(dirEntry)
+               // use first index to get the stats directory the symlink points to
+               header := ss.loadSharedHeader(ss.sharedHeader)
+               dirVector := ss.adjust(dirVector(&header.dirVector))
+               statSegDir2 := dirSegment(uintptr(dirVector) + uintptr(i1)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
+
+               // retry with actual stats segment and use second index to get
+               // stats for the required item
+               return ss.CopyEntryData(statSegDir2, i2)
+
        default:
                // TODO: monitor occurrences with metrics
                debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
@@ -211,8 +253,8 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
        return nil
 }
 
-func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapter.Stat) error {
-       dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
+func (ss *statSegmentV2) UpdateEntryData(segment dirSegment, stat *adapter.Stat) error {
+       dirEntry := (*statSegDirectoryEntryV2)(segment)
        switch (*stat).(type) {
        case adapter.ScalarStat:
                *stat = adapter.ScalarStat(dirEntry.unionData)
@@ -224,7 +266,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte
                        return nil
                }
                vecLen := *(*uint32)(vectorLen(dirVector))
-               var errData adapter.Counter
+               var errData []adapter.Counter
                for i := uint32(0); i < vecLen; i++ {
                        cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
                        cbVal := ss.adjust(vectorLen(cb))
@@ -234,12 +276,12 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte
                        }
                        offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
                        val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
-                       errData += val
+                       errData = append(errData, val)
                }
                *stat = adapter.ErrorStat(errData)
 
        case adapter.SimpleCounterStat:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -266,7 +308,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte
                }
 
        case adapter.CombinedCounterStat:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -290,7 +332,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte
                }
 
        case adapter.NameStat:
-               dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
+               dirVector := ss.adjust(dirVector(&dirEntry.unionData))
                if dirVector == nil {
                        debugf("data vector pointer is out of range for %s", dirEntry.name)
                        return nil
@@ -333,9 +375,9 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte
 
 // Adjust data pointer using shared header and base and return
 // the pointer to a data segment
-func (ss *statSegmentV2) adjust(data unsafe.Pointer) unsafe.Pointer {
+func (ss *statSegmentV2) adjust(data dirVector) dirVector {
        header := ss.loadSharedHeader(ss.sharedHeader)
-       adjusted := unsafe.Pointer(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) +
+       adjusted := dirVector(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) +
                uintptr(*(*uint64)(data)) - uintptr(*(*uint64)(unsafe.Pointer(&header.base))))
        if uintptr(unsafe.Pointer(&ss.sharedHeader[len(ss.sharedHeader)-1])) <= uintptr(adjusted) ||
                uintptr(unsafe.Pointer(&ss.sharedHeader[0])) >= uintptr(adjusted) {
@@ -344,7 +386,26 @@ func (ss *statSegmentV2) adjust(data unsafe.Pointer) unsafe.Pointer {
        return adjusted
 }
 
-func (ss *statSegmentV2) getErrorVector() unsafe.Pointer {
+func (ss *statSegmentV2) getErrorVector() dirVector {
        header := ss.loadSharedHeader(ss.sharedHeader)
-       return ss.adjust(unsafe.Pointer(&header.errorVector))
+       return ss.adjust(dirVector(&header.errorVector))
+}
+
+func (ss *statSegmentV2) getSymlinkIndexes(dirEntry *statSegDirectoryEntryV2) (index1, index2 uint32) {
+       var b bytes.Buffer
+       if err := binary.Write(&b, binary.LittleEndian, dirEntry.unionData); err != nil {
+               debugf("error getting symlink indexes for %s: %v", dirEntry.name, err)
+               return
+       }
+       if len(b.Bytes()) != 8 {
+               debugf("incorrect symlink union data length for %s: expected 8, got %d", dirEntry.name, len(b.Bytes()))
+               return
+       }
+       for i := range b.Bytes()[:4] {
+               index1 += uint32(b.Bytes()[i]) << (uint32(i) * 8)
+       }
+       for i := range b.Bytes()[4:] {
+               index2 += uint32(b.Bytes()[i+4]) << (uint32(i) * 8)
+       }
+       return
 }