1 // Copyright (c) 2019 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
23 "github.com/ftrvxmtrx/fd"
25 "git.fd.io/govpp.git/adapter"
29 ErrStatDataLenIncorrect = fmt.Errorf("stat data length incorrect")
37 func checkVersion(ver uint64) error {
39 return fmt.Errorf("stat segment version is too old: %v (minimal version: %v)", ver, minVersion)
40 } else if ver > maxVersion {
41 return fmt.Errorf("stat segment version is not supported: %v (minimal version: %v)", ver, maxVersion)
46 type statSegment struct {
50 // legacyVersion represents stat segment version 0
51 // and is used as fallback for VPP 19.04
55 func (c *statSegment) getHeader() (header sharedHeader) {
57 return loadSharedHeaderLegacy(c.sharedHeader)
59 return loadSharedHeader(c.sharedHeader)
62 func (c *statSegment) getEpoch() (int64, bool) {
64 return h.epoch, h.inProgress != 0
67 func (c *statSegment) getOffsets() (dir, err, stat int64) {
69 return h.directoryOffset, h.errorOffset, h.statsOffset
72 func (c *statSegment) connect(sockName string) error {
73 if c.sharedHeader != nil {
74 return fmt.Errorf("already connected")
81 Log.Debugf("connecting to: %v", addr)
83 conn, err := net.DialUnix(addr.Net, nil, &addr)
85 Log.Warnf("connecting to socket %s failed: %s", addr, err)
89 if err := conn.Close(); err != nil {
90 Log.Warnf("closing socket failed: %v", err)
94 Log.Debugf("connected to socket")
96 files, err := fd.Get(conn, 1, nil)
98 return fmt.Errorf("getting file descriptor over socket failed: %v", err)
101 return fmt.Errorf("no files received over socket")
106 if err := file.Close(); err != nil {
107 Log.Warnf("closing file failed: %v", err)
111 info, err := file.Stat()
117 data, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
119 Log.Debugf("mapping shared memory failed: %v", err)
120 return fmt.Errorf("mapping shared memory failed: %v", err)
123 Log.Debugf("successfuly mmapped shared memory segment (size: %v) %v", size, len(data))
125 c.sharedHeader = data
128 hdr := loadSharedHeader(c.sharedHeader)
129 Log.Debugf("stat segment header: %+v", hdr)
131 if hdr.legacyVersion() {
132 c.legacyVersion = true
133 hdr = loadSharedHeaderLegacy(c.sharedHeader)
134 Log.Debugf("falling back to legacy version (VPP <=19.04) of stat segment (header: %+v)", hdr)
137 if err := checkVersion(hdr.version); err != nil {
144 func (c *statSegment) disconnect() error {
145 if c.sharedHeader == nil {
149 if err := syscall.Munmap(c.sharedHeader); err != nil {
150 Log.Debugf("unmapping shared memory failed: %v", err)
151 return fmt.Errorf("unmapping shared memory failed: %v", err)
155 Log.Debugf("successfuly unmapped shared memory")
159 type statDirectoryType int32
163 statDirScalarIndex = 1
164 statDirCounterVectorSimple = 2
165 statDirCounterVectorCombined = 3
166 statDirErrorIndex = 4
167 statDirNameVector = 5
171 func (t statDirectoryType) String() string {
172 return adapter.StatType(t).String()
175 type statSegDirectoryEntry struct {
176 directoryType statDirectoryType
177 // unionData can represent:
186 func (c *statSegment) getStatDirVector() unsafe.Pointer {
187 dirOffset, _, _ := c.getOffsets()
188 return unsafe.Pointer(&c.sharedHeader[dirOffset])
191 func (c *statSegment) getStatDirIndex(p unsafe.Pointer, index uint32) *statSegDirectoryEntry {
192 return (*statSegDirectoryEntry)(unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntry{})))
195 func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Stat {
196 dirType := adapter.StatType(dirEntry.directoryType)
199 case statDirScalarIndex:
200 return adapter.ScalarStat(dirEntry.unionData)
202 case statDirErrorIndex:
203 if dirEntry.unionData == 0 {
204 debugf("offset invalid for %s", dirEntry.name)
206 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
207 debugf("offset out of range for %s", dirEntry.name)
211 _, errOffset, _ := c.getOffsets()
212 offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
214 var errData adapter.Counter
216 // error were not vector (per-worker) in VPP 19.04
217 offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(uint64(0))
218 val := *(*adapter.Counter)(statSegPointer(offsetVector, offset))
221 vecLen := uint32(vectorLen(offsetVector))
223 for i := uint32(0); i < vecLen; i++ {
224 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
225 offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
226 debugf("error index, cb: %d, offset: %d", cb, offset)
227 val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), offset))
231 return adapter.ErrorStat(errData)
233 case statDirCounterVectorSimple:
234 if dirEntry.unionData == 0 {
235 debugf("offset invalid for %s", dirEntry.name)
237 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
238 debugf("offset out of range for %s", dirEntry.name)
242 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
243 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
245 data := make([][]adapter.Counter, vecLen)
246 for i := uint32(0); i < vecLen; i++ {
247 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
248 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
249 vecLen2 := uint32(vectorLen(counterVec))
250 data[i] = make([]adapter.Counter, vecLen2)
251 for j := uint32(0); j < vecLen2; j++ {
252 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
253 val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
257 return adapter.SimpleCounterStat(data)
259 case statDirCounterVectorCombined:
260 if dirEntry.unionData == 0 {
261 debugf("offset invalid for %s", dirEntry.name)
263 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
264 debugf("offset out of range for %s", dirEntry.name)
268 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
269 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
271 data := make([][]adapter.CombinedCounter, vecLen)
272 for i := uint32(0); i < vecLen; i++ {
273 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
274 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
275 vecLen2 := uint32(vectorLen(counterVec))
276 data[i] = make([]adapter.CombinedCounter, vecLen2)
277 for j := uint32(0); j < vecLen2; j++ {
278 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
279 val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
283 return adapter.CombinedCounterStat(data)
285 case statDirNameVector:
286 if dirEntry.unionData == 0 {
287 debugf("offset invalid for %s", dirEntry.name)
289 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
290 debugf("offset out of range for %s", dirEntry.name)
294 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
295 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
297 data := make([]adapter.Name, vecLen)
298 for i := uint32(0); i < vecLen; i++ {
299 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
301 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
304 nameVec := unsafe.Pointer(&c.sharedHeader[cb])
305 vecLen2 := uint32(vectorLen(nameVec))
307 nameStr := make([]byte, 0, vecLen2)
308 for j := uint32(0); j < vecLen2; j++ {
309 offset := uintptr(j) * unsafe.Sizeof(byte(0))
310 val := *(*byte)(statSegPointer(nameVec, offset))
312 nameStr = append(nameStr, val)
315 data[i] = adapter.Name(nameStr)
317 return adapter.NameStat(data)
323 // TODO: monitor occurrences with metrics
324 debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
329 func (c *statSegment) updateEntryData(dirEntry *statSegDirectoryEntry, stat *adapter.Stat) error {
330 switch (*stat).(type) {
331 case adapter.ScalarStat:
332 *stat = adapter.ScalarStat(dirEntry.unionData)
334 case adapter.ErrorStat:
335 if dirEntry.unionData == 0 {
336 debugf("offset invalid for %s", dirEntry.name)
338 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
339 debugf("offset out of range for %s", dirEntry.name)
343 _, errOffset, _ := c.getOffsets()
344 offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
346 var errData adapter.Counter
348 // error were not vector (per-worker) in VPP 19.04
349 offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(uint64(0))
350 val := *(*adapter.Counter)(statSegPointer(offsetVector, offset))
353 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[errOffset])))
355 for i := uint32(0); i < vecLen; i++ {
356 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
357 offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
358 val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), offset))
362 *stat = adapter.ErrorStat(errData)
364 case adapter.SimpleCounterStat:
365 if dirEntry.unionData == 0 {
366 debugf("offset invalid for %s", dirEntry.name)
368 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
369 debugf("offset out of range for %s", dirEntry.name)
373 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
374 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
376 data := (*stat).(adapter.SimpleCounterStat)
377 if uint32(len(data)) != vecLen {
378 return ErrStatDataLenIncorrect
380 for i := uint32(0); i < vecLen; i++ {
381 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
382 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
383 vecLen2 := uint32(vectorLen(counterVec))
385 if uint32(len(simpData)) != vecLen2 {
386 return ErrStatDataLenIncorrect
388 for j := uint32(0); j < vecLen2; j++ {
389 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
390 val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
395 case adapter.CombinedCounterStat:
396 if dirEntry.unionData == 0 {
397 debugf("offset invalid for %s", dirEntry.name)
399 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
400 debugf("offset out of range for %s", dirEntry.name)
404 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
405 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
407 data := (*stat).(adapter.CombinedCounterStat)
408 if uint32(len(data)) != vecLen {
409 return ErrStatDataLenIncorrect
411 for i := uint32(0); i < vecLen; i++ {
412 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
413 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
414 vecLen2 := uint32(vectorLen(counterVec))
416 if uint32(len(combData)) != vecLen2 {
417 return ErrStatDataLenIncorrect
419 for j := uint32(0); j < vecLen2; j++ {
420 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
421 val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
426 case adapter.NameStat:
427 if dirEntry.unionData == 0 {
428 debugf("offset invalid for %s", dirEntry.name)
430 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
431 debugf("offset out of range for %s", dirEntry.name)
435 vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
436 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
438 data := (*stat).(adapter.NameStat)
439 if uint32(len(data)) != vecLen {
440 return ErrStatDataLenIncorrect
442 for i := uint32(0); i < vecLen; i++ {
443 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
447 nameVec := unsafe.Pointer(&c.sharedHeader[cb])
448 vecLen2 := uint32(vectorLen(nameVec))
451 if uint32(len(nameData))+1 != vecLen2 {
452 return ErrStatDataLenIncorrect
454 for j := uint32(0); j < vecLen2; j++ {
455 offset := uintptr(j) * unsafe.Sizeof(byte(0))
456 val := *(*byte)(statSegPointer(nameVec, offset))
466 Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)