X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=adapter%2Fstatsclient%2Fstatseg_v2.go;h=f8081124184516d167a8e01ed9a0f2642fb6ec52;hb=67cea0df30f7ef348e265c9326f7a9f15ba26240;hp=7091ff9cccd13873eeadbfbeca498bfe6bcf80f0;hpb=1a1f4043dfae2e77de70d6adfbb8d84cdeed658d;p=govpp.git diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go index 7091ff9..f808112 100644 --- a/adapter/statsclient/statseg_v2.go +++ b/adapter/statsclient/statseg_v2.go @@ -15,11 +15,12 @@ package statsclient import ( - "fmt" + "bytes" + "encoding/binary" "sync/atomic" "unsafe" - "git.fd.io/govpp.git/adapter" + "go.fd.io/govpp/adapter" ) type statSegmentV2 struct { @@ -37,8 +38,9 @@ type sharedHeaderV2 struct { } type statSegDirectoryEntryV2 struct { - directoryType statDirectoryType + directoryType dirType // unionData can represent: + // - symlink indexes // - index // - value // - pointer to data @@ -65,13 +67,13 @@ func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) { } } -func (ss *statSegmentV2) GetDirectoryVector() (unsafe.Pointer, error) { +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, 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++ { @@ -80,7 +82,7 @@ func (ss *statSegmentV2) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsa break } } - return statSegDir, name, dir.directoryType + return statSegDir, name, getStatType(dir.directoryType, ss.getErrorVector() != nil) } func (ss *statSegmentV2) GetEpoch() (int64, bool) { @@ -88,41 +90,43 @@ 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 := 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 adapter.StatType(dirEntry.directoryType) { - case statDirScalarIndex: + switch typ { + case adapter.ScalarIndex: return adapter.ScalarStat(dirEntry.unionData) - case statDirErrorIndex: - dirVector, err := ss.getErrorVector() - if err != nil { + 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 + var errData []adapter.Counter for i := uint32(0); i < vecLen; i++ { cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - cbVal, err := ss.adjust(vectorLen(cb)) - if err != nil { + 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 += val + errData = append(errData, val) } return adapter.ErrorStat(errData) - case statDirCounterVectorSimple: - dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + 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 } @@ -130,24 +134,34 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { data := make([][]adapter.Counter, vecLen) for i := uint32(0); i < vecLen; i++ { counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - counterVector, err := ss.adjust(vectorLen(counterVectorOffset)) - if err != nil { + 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 + 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, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + 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 } @@ -155,24 +169,34 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { data := make([][]adapter.CombinedCounter, vecLen) for i := uint32(0); i < vecLen; i++ { counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - counterVector, err := ss.adjust(vectorLen(counterVectorOffset)) - if err != nil { + 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 + 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, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + 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 } @@ -184,8 +208,8 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { debugf("name vector out of range for %s (%v)", dirEntry.name, i) continue } - nameVector, err := ss.adjust(vectorLen(nameVectorOffset)) - if err != nil { + nameVector := ss.adjust(vectorLen(nameVectorOffset)) + if nameVector == nil { debugf("name data pointer out of range") continue } @@ -202,9 +226,26 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { } return adapter.NameStat(data) - case statDirEmpty: + case adapter.Empty: + return adapter.EmptyStat("") // 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) @@ -212,36 +253,36 @@ 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) case adapter.ErrorStat: - dirVector, err := ss.getErrorVector() - if err != nil { + 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 + var errData []adapter.Counter for i := uint32(0); i < vecLen; i++ { cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - cbVal, err := ss.adjust(vectorLen(cb)) - if err != nil { + 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 += val + errData = append(errData, val) } *stat = adapter.ErrorStat(errData) case adapter.SimpleCounterStat: - dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) + if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil } @@ -252,8 +293,8 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } for i := uint32(0); i < vecLen; i++ { counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - counterVector, err := ss.adjust(vectorLen(counterVectorOffset)) - if err != nil { + counterVector := ss.adjust(vectorLen(counterVectorOffset)) + if counterVector == nil { debugf("counter (vector simple) pointer out of range") continue } @@ -267,8 +308,8 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } case adapter.CombinedCounterStat: - dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) + if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil } @@ -276,8 +317,8 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte data := (*stat).(adapter.CombinedCounterStat) for i := uint32(0); i < vecLen; i++ { counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) - counterVector, err := ss.adjust(vectorLen(counterVectorOffset)) - if err != nil { + counterVector := ss.adjust(vectorLen(counterVectorOffset)) + if counterVector == nil { debugf("counter (vector combined) pointer out of range") continue } @@ -291,8 +332,8 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } case adapter.NameStat: - dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) - if err != nil { + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) + if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil } @@ -304,8 +345,8 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte debugf("name vector out of range for %s (%v)", dirEntry.name, i) continue } - nameVector, err := ss.adjust(vectorLen(nameVectorOffset)) - if err != nil { + nameVector := ss.adjust(vectorLen(nameVectorOffset)) + if nameVector == nil { debugf("name data pointer out of range") continue } @@ -334,18 +375,37 @@ 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, error) { +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) { - return nil, fmt.Errorf("adjusted data out of range for %v", data) + return nil } - return adjusted, nil + return adjusted } -func (ss *statSegmentV2) getErrorVector() (unsafe.Pointer, error) { +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 }