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) getStatDirVector() unsafe.Pointer {
56 dirOffset, _, _ := c.getOffsets()
57 return unsafe.Pointer(&c.sharedHeader[dirOffset])
60 func (c *statSegment) getStatDirIndex(p unsafe.Pointer, index uint32) *statSegDirectoryEntry {
61 return (*statSegDirectoryEntry)(unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntry{})))
64 func (c *statSegment) getHeader() (header statSegSharedHeader) {
66 return statSegHeaderLegacy(c.sharedHeader)
68 return statSegHeader(c.sharedHeader)
71 func (c *statSegment) getEpoch() (int64, bool) {
73 return h.epoch, h.inProgress != 0
76 func (c *statSegment) getOffsets() (dir, err, stat int64) {
78 return h.directoryOffset, h.errorOffset, h.statsOffset
81 func (c *statSegment) connect(sockName string) error {
82 if c.sharedHeader != nil {
83 return fmt.Errorf("already connected")
90 Log.Debugf("connecting to: %v", addr)
92 conn, err := net.DialUnix(addr.Net, nil, &addr)
94 Log.Warnf("connecting to socket %s failed: %s", addr, err)
98 if err := conn.Close(); err != nil {
99 Log.Warnf("closing socket failed: %v", err)
103 Log.Debugf("connected to socket")
105 files, err := fd.Get(conn, 1, nil)
107 return fmt.Errorf("getting file descriptor over socket failed: %v", err)
110 return fmt.Errorf("no files received over socket")
115 if err := file.Close(); err != nil {
116 Log.Warnf("closing file failed: %v", err)
120 info, err := file.Stat()
126 data, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
128 Log.Debugf("mapping shared memory failed: %v", err)
129 return fmt.Errorf("mapping shared memory failed: %v", err)
132 c.sharedHeader = data
135 Log.Debugf("successfuly mmapped shared memory segment (size: %v)", size)
137 hdr := statSegHeader(c.sharedHeader)
138 Log.Debugf("stat segment header: %+v", hdr)
140 if hdr.legacyVersion() {
141 c.legacyVersion = true
142 hdr = statSegHeaderLegacy(c.sharedHeader)
143 Log.Debugf("falling back to legacy version (VPP <=19.04) of stat segment (header: %+v)", hdr)
146 if err := checkVersion(hdr.version); err != nil {
153 func (c *statSegment) disconnect() error {
154 if c.sharedHeader == nil {
158 if err := syscall.Munmap(c.sharedHeader); err != nil {
159 Log.Debugf("unmapping shared memory failed: %v", err)
160 return fmt.Errorf("unmapping shared memory failed: %v", err)
164 Log.Debugf("successfuly unmapped shared memory")
168 type statDirectoryType int32
170 func (t statDirectoryType) String() string {
171 return adapter.StatType(t).String()
174 type statSegDirectoryEntry struct {
175 directoryType statDirectoryType
176 // unionData can represent:
185 func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Stat {
186 dirType := adapter.StatType(dirEntry.directoryType)
189 case adapter.ScalarIndex:
190 return adapter.ScalarStat(dirEntry.unionData)
192 case adapter.ErrorIndex:
193 _, errOffset, _ := c.getOffsets()
194 offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
196 var errData adapter.Counter
198 // error were not vector (per-worker) in VPP 19.04
199 offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(uint64(0))
200 val := *(*adapter.Counter)(statSegPointer(offsetVector, offset))
203 vecLen := vectorLen(offsetVector)
204 for i := uint64(0); i < vecLen; i++ {
205 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
206 offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
207 val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), offset))
211 return adapter.ErrorStat(errData)
213 case adapter.SimpleCounterVector:
214 if dirEntry.unionData == 0 {
215 debugf("offset invalid for %s", dirEntry.name)
217 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
218 debugf("offset out of range for %s", dirEntry.name)
222 vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
223 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
225 data := make([][]adapter.Counter, vecLen)
226 for i := uint64(0); i < vecLen; i++ {
227 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
228 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
229 vecLen2 := vectorLen(counterVec)
230 data[i] = make([]adapter.Counter, vecLen2)
231 for j := uint64(0); j < vecLen2; j++ {
232 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
233 val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
237 return adapter.SimpleCounterStat(data)
239 case adapter.CombinedCounterVector:
240 if dirEntry.unionData == 0 {
241 debugf("offset invalid for %s", dirEntry.name)
243 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
244 debugf("offset out of range for %s", dirEntry.name)
248 vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
249 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
251 data := make([][]adapter.CombinedCounter, vecLen)
252 for i := uint64(0); i < vecLen; i++ {
253 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
254 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
255 vecLen2 := vectorLen(counterVec)
256 data[i] = make([]adapter.CombinedCounter, vecLen2)
257 for j := uint64(0); j < vecLen2; j++ {
258 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
259 val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
263 return adapter.CombinedCounterStat(data)
265 case adapter.NameVector:
266 if dirEntry.unionData == 0 {
267 debugf("offset invalid for %s", dirEntry.name)
269 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
270 debugf("offset out of range for %s", dirEntry.name)
274 vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
275 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
277 data := make([]adapter.Name, vecLen)
278 for i := uint64(0); i < vecLen; i++ {
279 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
281 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
284 nameVec := unsafe.Pointer(&c.sharedHeader[cb])
285 vecLen2 := vectorLen(nameVec)
287 nameStr := make([]byte, 0, vecLen2)
288 for j := uint64(0); j < vecLen2; j++ {
289 offset := uintptr(j) * unsafe.Sizeof(byte(0))
290 val := *(*byte)(statSegPointer(nameVec, offset))
292 nameStr = append(nameStr, val)
295 data[i] = adapter.Name(nameStr)
297 return adapter.NameStat(data)
300 // TODO: monitor occurrences with metrics
301 debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
307 func (c *statSegment) updateEntryData(dirEntry *statSegDirectoryEntry, stat *adapter.Stat) error {
308 switch (*stat).(type) {
309 case adapter.ScalarStat:
310 *stat = adapter.ScalarStat(dirEntry.unionData)
312 case adapter.ErrorStat:
313 _, errOffset, _ := c.getOffsets()
314 offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
316 var errData adapter.Counter
318 // error were not vector (per-worker) in VPP 19.04
319 offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(uint64(0))
320 val := *(*adapter.Counter)(statSegPointer(offsetVector, offset))
323 vecLen := vectorLen(offsetVector)
324 for i := uint64(0); i < vecLen; i++ {
325 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
326 offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
327 val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), offset))
331 *stat = adapter.ErrorStat(errData)
333 case adapter.SimpleCounterStat:
334 if dirEntry.unionData == 0 {
335 debugf("offset invalid for %s", dirEntry.name)
337 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
338 debugf("offset out of range for %s", dirEntry.name)
342 vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
343 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
345 data := (*stat).(adapter.SimpleCounterStat)
346 if uint64(len(data)) != vecLen {
347 return ErrStatDataLenIncorrect
349 for i := uint64(0); i < vecLen; i++ {
350 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
351 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
352 vecLen2 := vectorLen(counterVec)
354 if uint64(len(simpData)) != vecLen2 {
355 return ErrStatDataLenIncorrect
357 for j := uint64(0); j < vecLen2; j++ {
358 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
359 val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
364 case adapter.CombinedCounterStat:
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 := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
374 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
376 data := (*stat).(adapter.CombinedCounterStat)
377 if uint64(len(data)) != vecLen {
378 return ErrStatDataLenIncorrect
380 for i := uint64(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 := vectorLen(counterVec)
385 if uint64(len(combData)) != vecLen2 {
386 return ErrStatDataLenIncorrect
388 for j := uint64(0); j < vecLen2; j++ {
389 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
390 val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
395 case adapter.NameStat:
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 := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
405 offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
407 data := (*stat).(adapter.NameStat)
408 if uint64(len(data)) != vecLen {
409 return ErrStatDataLenIncorrect
411 for i := uint64(0); i < vecLen; i++ {
412 cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
416 nameVec := unsafe.Pointer(&c.sharedHeader[cb])
417 vecLen2 := vectorLen(nameVec)
420 if uint64(len(nameData))+1 != vecLen2 {
421 return ErrStatDataLenIncorrect
423 for j := uint64(0); j < vecLen2; j++ {
424 offset := uintptr(j) * unsafe.Sizeof(byte(0))
425 val := *(*byte)(statSegPointer(nameVec, offset))
435 Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)