aboutsummaryrefslogtreecommitdiffstats
path: root/adapter/statsclient/statseg_v2.go
diff options
context:
space:
mode:
Diffstat (limited to 'adapter/statsclient/statseg_v2.go')
-rw-r--r--adapter/statsclient/statseg_v2.go411
1 files changed, 411 insertions, 0 deletions
diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go
new file mode 100644
index 0000000..f808112
--- /dev/null
+++ b/adapter/statsclient/statseg_v2.go
@@ -0,0 +1,411 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package statsclient
+
+import (
+ "bytes"
+ "encoding/binary"
+ "sync/atomic"
+ "unsafe"
+
+ "go.fd.io/govpp/adapter"
+)
+
+type statSegmentV2 struct {
+ sharedHeader []byte
+ memorySize int64
+}
+
+type sharedHeaderV2 struct {
+ version uint64
+ base unsafe.Pointer
+ epoch int64
+ inProgress int64
+ dirVector unsafe.Pointer
+ errorVector unsafe.Pointer
+}
+
+type statSegDirectoryEntryV2 struct {
+ directoryType dirType
+ // unionData can represent:
+ // - symlink indexes
+ // - index
+ // - value
+ // - pointer to data
+ unionData uint64
+ name [128]byte
+}
+
+func newStatSegmentV2(data []byte, size int64) *statSegmentV2 {
+ return &statSegmentV2{
+ sharedHeader: data,
+ memorySize: size,
+ }
+}
+
+func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) {
+ h := (*sharedHeaderV2)(unsafe.Pointer(&b[0]))
+ return sharedHeaderV2{
+ version: atomic.LoadUint64(&h.version),
+ base: atomic.LoadPointer(&h.base),
+ epoch: atomic.LoadInt64(&h.epoch),
+ inProgress: atomic.LoadInt64(&h.inProgress),
+ dirVector: atomic.LoadPointer(&h.dirVector),
+ errorVector: atomic.LoadPointer(&h.errorVector),
+ }
+}
+
+func (ss *statSegmentV2) GetDirectoryVector() dirVector {
+ header := ss.loadSharedHeader(ss.sharedHeader)
+ return ss.adjust(dirVector(&header.dirVector))
+}
+
+func (ss *statSegmentV2) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, adapter.StatType) {
+ statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
+ dir := (*statSegDirectoryEntryV2)(statSegDir)
+ var name []byte
+ for n := 0; n < len(dir.name); n++ {
+ if dir.name[n] == 0 {
+ name = dir.name[:n]
+ break
+ }
+ }
+ return statSegDir, name, getStatType(dir.directoryType, ss.getErrorVector() != nil)
+}
+
+func (ss *statSegmentV2) GetEpoch() (int64, bool) {
+ sh := ss.loadSharedHeader(ss.sharedHeader)
+ return sh.epoch, sh.inProgress != 0
+}
+
+func (ss *statSegmentV2) CopyEntryData(segment dirSegment, index uint32) adapter.Stat {
+ dirEntry := (*statSegDirectoryEntryV2)(segment)
+ typ := getStatType(dirEntry.directoryType, ss.getErrorVector() != nil)
+ // skip zero pointer value
+ if typ != adapter.ScalarIndex && typ != adapter.Empty && typ != adapter.ErrorIndex && dirEntry.unionData == 0 {
+ debugf("data pointer not defined for %s", dirEntry.name)
+ return nil
+ }
+
+ switch typ {
+ case adapter.ScalarIndex:
+ return adapter.ScalarStat(dirEntry.unionData)
+
+ case adapter.ErrorIndex:
+ dirVector := ss.getErrorVector()
+ if dirVector == nil {
+ debugf("error vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ 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))
+ if cbVal == nil {
+ debugf("error counter pointer out of range")
+ continue
+ }
+ offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
+ val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
+ errData = append(errData, val)
+ }
+ return adapter.ErrorStat(errData)
+
+ case adapter.SimpleCounterVector:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := make([][]adapter.Counter, vecLen)
+ for i := uint32(0); i < vecLen; i++ {
+ counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ counterVector := ss.adjust(vectorLen(counterVectorOffset))
+ if counterVector == nil {
+ debugf("counter (vector simple) pointer out of range")
+ continue
+ }
+ counterVectorLength := *(*uint32)(vectorLen(counterVector))
+ 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 adapter.CombinedCounterVector:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := make([][]adapter.CombinedCounter, vecLen)
+ for i := uint32(0); i < vecLen; i++ {
+ counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ counterVector := ss.adjust(vectorLen(counterVectorOffset))
+ if counterVector == nil {
+ debugf("counter (vector combined) pointer out of range")
+ continue
+ }
+ counterVectorLength := *(*uint32)(vectorLen(counterVector))
+ 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 adapter.NameVector:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := make([]adapter.Name, vecLen)
+ for i := uint32(0); i < vecLen; i++ {
+ nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ if uintptr(nameVectorOffset) == 0 {
+ debugf("name vector out of range for %s (%v)", dirEntry.name, i)
+ continue
+ }
+ nameVector := ss.adjust(vectorLen(nameVectorOffset))
+ if nameVector == nil {
+ debugf("name data pointer out of range")
+ continue
+ }
+ nameVectorLen := *(*uint32)(vectorLen(nameVector))
+ name := make([]byte, 0, nameVectorLen)
+ for j := uint32(0); j < nameVectorLen; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(byte(0))
+ value := *(*byte)(statSegPointer(nameVector, offset))
+ if value > 0 {
+ name = append(name, value)
+ }
+ }
+ data[i] = name
+ }
+ return adapter.NameStat(data)
+
+ case adapter.Empty:
+ return adapter.EmptyStat("<none>")
+ // no-op
+
+ case adapter.Symlink:
+ // 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)
+ }
+ return nil
+}
+
+func (ss *statSegmentV2) UpdateEntryData(segment dirSegment, stat *adapter.Stat) error {
+ dirEntry := (*statSegDirectoryEntryV2)(segment)
+ switch (*stat).(type) {
+ case adapter.ScalarStat:
+ *stat = adapter.ScalarStat(dirEntry.unionData)
+
+ case adapter.ErrorStat:
+ dirVector := ss.getErrorVector()
+ if dirVector == nil {
+ debugf("error vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ 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))
+ if cbVal == nil {
+ debugf("error counter pointer out of range")
+ continue
+ }
+ offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
+ val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
+ errData = append(errData, val)
+ }
+ *stat = adapter.ErrorStat(errData)
+
+ case adapter.SimpleCounterStat:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := (*stat).(adapter.SimpleCounterStat)
+ if uint32(len(data)) != vecLen {
+ return ErrStatDataLenIncorrect
+ }
+ for i := uint32(0); i < vecLen; i++ {
+ counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ counterVector := ss.adjust(vectorLen(counterVectorOffset))
+ if counterVector == nil {
+ debugf("counter (vector simple) pointer out of range")
+ 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
+ }
+ }
+
+ case adapter.CombinedCounterStat:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := (*stat).(adapter.CombinedCounterStat)
+ for i := uint32(0); i < vecLen; i++ {
+ counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ counterVector := ss.adjust(vectorLen(counterVectorOffset))
+ if counterVector == nil {
+ debugf("counter (vector combined) pointer out of range")
+ 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
+ }
+ }
+
+ case adapter.NameStat:
+ dirVector := ss.adjust(dirVector(&dirEntry.unionData))
+ if dirVector == nil {
+ debugf("data vector pointer is out of range for %s", dirEntry.name)
+ return nil
+ }
+ vecLen := *(*uint32)(vectorLen(dirVector))
+ data := (*stat).(adapter.NameStat)
+ for i := uint32(0); i < vecLen; i++ {
+ nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
+ if uintptr(nameVectorOffset) == 0 {
+ debugf("name vector out of range for %s (%v)", dirEntry.name, i)
+ continue
+ }
+ nameVector := ss.adjust(vectorLen(nameVectorOffset))
+ if nameVector == nil {
+ debugf("name data pointer out of range")
+ continue
+ }
+ nameVectorLen := *(*uint32)(vectorLen(nameVector))
+ nameData := data[i]
+ if uint32(len(nameData))+1 != nameVectorLen {
+ return ErrStatDataLenIncorrect
+ }
+ for j := uint32(0); j < nameVectorLen; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(byte(0))
+ value := *(*byte)(statSegPointer(nameVector, offset))
+ if value == 0 {
+ break
+ }
+ nameData[j] = value
+ }
+ }
+
+ default:
+ if Debug {
+ Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)
+ }
+ }
+ return nil
+}
+
+// Adjust data pointer using shared header and base and return
+// the pointer to a data segment
+func (ss *statSegmentV2) adjust(data dirVector) dirVector {
+ header := ss.loadSharedHeader(ss.sharedHeader)
+ 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) {
+ return nil
+ }
+ return adjusted
+}
+
+func (ss *statSegmentV2) getErrorVector() dirVector {
+ header := ss.loadSharedHeader(ss.sharedHeader)
+ 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
+}