package statsclient
import (
- "fmt"
+ "bytes"
+ "encoding/binary"
"sync/atomic"
"unsafe"
}
type statSegDirectoryEntryV2 struct {
- directoryType statDirectoryType
+ directoryType dirType
// unionData can represent:
+ // - symlink indexes
// - index
// - value
// - pointer to data
}
}
-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++ {
break
}
}
- return statSegDir, name, dir.directoryType
+ return statSegDir, name, getStatType(dir.directoryType, ss.getErrorVector() != nil)
}
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
}
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
}
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
}
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
}
}
return adapter.NameStat(data)
- case statDirEmpty:
+ 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(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
}
}
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
}
}
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
}
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
}
}
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
}
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
}
// 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
}