diff options
Diffstat (limited to 'adapter/statsclient/statseg_v2.go')
-rw-r--r-- | adapter/statsclient/statseg_v2.go | 411 |
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 +} |