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.
25 "github.com/ftrvxmtrx/fd"
27 "git.fd.io/govpp.git/adapter"
31 maxWaitInProgress = time.Second * 1
34 type statDirectoryType int32
36 func (t statDirectoryType) String() string {
37 return adapter.StatType(t).String()
40 type statSegDirectoryEntry struct {
41 directoryType statDirectoryType
42 // unionData can represent: offset, index or value
48 type statSegment struct {
52 // oldHeader defines version 0 for stat segment
53 // and is used for VPP 19.04
57 func (c *statSegment) connect(sockName string) error {
58 addr := &net.UnixAddr{
63 Log.Debugf("connecting to: %v", addr)
65 conn, err := net.DialUnix(addr.Net, nil, addr)
67 Log.Warnf("connecting to socket %s failed: %s", addr, err)
71 if err := conn.Close(); err != nil {
72 Log.Warnf("closing socket failed: %v", err)
76 Log.Debugf("connected to socket")
78 files, err := fd.Get(conn, 1, nil)
80 return fmt.Errorf("getting file descriptor over socket failed: %v", err)
83 return fmt.Errorf("no files received over socket")
86 for _, f := range files {
87 if err := f.Close(); err != nil {
88 Log.Warnf("closing file %s failed: %v", f.Name(), err)
93 Log.Debugf("received %d files over socket", len(files))
104 Log.Debugf("fd: name=%v size=%v", info.Name(), size)
106 data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
108 Log.Warnf("mapping shared memory failed: %v", err)
109 return fmt.Errorf("mapping shared memory failed: %v", err)
112 Log.Debugf("successfuly mapped shared memory")
114 c.sharedHeader = data
117 header := c.readHeader()
118 Log.Debugf("stat segment header: %+v", header)
120 // older VPP (<=19.04) did not have version in stat segment header
121 // we try to provide fallback support by skipping it in header
122 if header.version > MaxVersion && header.inProgress > 1 && header.epoch == 0 {
123 h := c.readHeaderOld()
124 Log.Debugf("statsclient: falling back to old stat segment version (VPP <=19.04): %+v", h)
131 func (c *statSegment) disconnect() error {
132 if err := syscall.Munmap(c.sharedHeader); err != nil {
133 Log.Warnf("unmapping shared memory failed: %v", err)
134 return fmt.Errorf("unmapping shared memory failed: %v", err)
137 Log.Debugf("successfuly unmapped shared memory")
142 func (c *statSegment) copyData(dirEntry *statSegDirectoryEntry) adapter.Stat {
143 switch typ := adapter.StatType(dirEntry.directoryType); typ {
144 case adapter.ScalarIndex:
145 return adapter.ScalarStat(dirEntry.unionData)
147 case adapter.ErrorIndex:
148 _, errOffset, _ := c.readOffsets()
149 offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
151 var errData adapter.Counter
153 // error were not vector (per-worker) in VPP 19.04
154 offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(uint64(0))
155 val := *(*adapter.Counter)(add(offsetVector, offset))
158 vecLen := vectorLen(offsetVector)
159 for i := uint64(0); i < vecLen; i++ {
160 cb := *(*uint64)(add(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
161 offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
162 val := *(*adapter.Counter)(add(unsafe.Pointer(&c.sharedHeader[0]), offset))
166 return adapter.ErrorStat(errData)
168 case adapter.SimpleCounterVector:
169 if dirEntry.unionData == 0 {
170 Log.Debugf("\toffset is not valid")
172 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
173 Log.Debugf("\toffset out of range")
177 simpleCounter := unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]) // offset
178 vecLen := vectorLen(simpleCounter)
179 offsetVector := add(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
181 data := make([][]adapter.Counter, vecLen)
182 for i := uint64(0); i < vecLen; i++ {
183 cb := *(*uint64)(add(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
184 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
185 vecLen2 := vectorLen(counterVec)
186 for j := uint64(0); j < vecLen2; j++ {
187 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
188 val := *(*adapter.Counter)(add(counterVec, offset))
189 data[i] = append(data[i], val)
192 return adapter.SimpleCounterStat(data)
194 case adapter.CombinedCounterVector:
195 if dirEntry.unionData == 0 {
196 Log.Debugf("\toffset is not valid")
198 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
199 Log.Debugf("\toffset out of range")
203 combinedCounter := unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]) // offset
204 vecLen := vectorLen(combinedCounter)
205 offsetVector := add(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
207 data := make([][]adapter.CombinedCounter, vecLen)
208 for i := uint64(0); i < vecLen; i++ {
209 cb := *(*uint64)(add(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
210 counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
211 vecLen2 := vectorLen(counterVec)
212 for j := uint64(0); j < vecLen2; j++ {
213 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
214 val := *(*adapter.CombinedCounter)(add(counterVec, offset))
215 data[i] = append(data[i], val)
218 return adapter.CombinedCounterStat(data)
220 case adapter.NameVector:
221 if dirEntry.unionData == 0 {
222 Log.Debugf("\toffset is not valid")
224 } else if dirEntry.unionData >= uint64(len(c.sharedHeader)) {
225 Log.Debugf("\toffset out of range")
229 nameVector := unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]) // offset
230 vecLen := vectorLen(nameVector)
231 offsetVector := add(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
233 data := make([]adapter.Name, vecLen)
234 for i := uint64(0); i < vecLen; i++ {
235 cb := *(*uint64)(add(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
237 Log.Debugf("\tname vector cb out of range")
240 nameVec := unsafe.Pointer(&c.sharedHeader[cb])
241 vecLen2 := vectorLen(nameVec)
244 for j := uint64(0); j < vecLen2; j++ {
245 offset := uintptr(j) * unsafe.Sizeof(byte(0))
246 val := *(*byte)(add(nameVec, offset))
248 nameStr = append(nameStr, val)
251 data[i] = adapter.Name(nameStr)
253 return adapter.NameStat(data)
256 Log.Warnf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
262 type sharedHeaderBase struct {
265 directoryOffset int64
270 type statSegSharedHeader struct {
275 func (c *statSegment) readHeaderOld() (header statSegSharedHeader) {
276 h := (*sharedHeaderBase)(unsafe.Pointer(&c.sharedHeader[0]))
278 header.epoch = atomic.LoadInt64(&h.epoch)
279 header.inProgress = atomic.LoadInt64(&h.inProgress)
280 header.directoryOffset = atomic.LoadInt64(&h.directoryOffset)
281 header.errorOffset = atomic.LoadInt64(&h.errorOffset)
282 header.statsOffset = atomic.LoadInt64(&h.statsOffset)
286 func (c *statSegment) readHeader() (header statSegSharedHeader) {
287 h := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
288 header.version = atomic.LoadUint64(&h.version)
289 header.epoch = atomic.LoadInt64(&h.epoch)
290 header.inProgress = atomic.LoadInt64(&h.inProgress)
291 header.directoryOffset = atomic.LoadInt64(&h.directoryOffset)
292 header.errorOffset = atomic.LoadInt64(&h.errorOffset)
293 header.statsOffset = atomic.LoadInt64(&h.statsOffset)
297 func (c *statSegment) readVersion() uint64 {
301 header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
302 version := atomic.LoadUint64(&header.version)
306 func (c *statSegment) readEpoch() (int64, bool) {
308 h := c.readHeaderOld()
309 return h.epoch, h.inProgress != 0
311 header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
312 epoch := atomic.LoadInt64(&header.epoch)
313 inprog := atomic.LoadInt64(&header.inProgress)
314 return epoch, inprog != 0
317 func (c *statSegment) readOffsets() (dir, err, stat int64) {
319 h := c.readHeaderOld()
320 return h.directoryOffset, h.errorOffset, h.statsOffset
322 header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
323 dirOffset := atomic.LoadInt64(&header.directoryOffset)
324 errOffset := atomic.LoadInt64(&header.errorOffset)
325 statOffset := atomic.LoadInt64(&header.statsOffset)
326 return dirOffset, errOffset, statOffset
329 type statSegAccess struct {
333 func (c *statSegment) accessStart() *statSegAccess {
334 epoch, inprog := c.readEpoch()
337 if time.Since(t) > maxWaitInProgress {
340 epoch, inprog = c.readEpoch()
342 return &statSegAccess{
347 func (c *statSegment) accessEnd(acc *statSegAccess) bool {
348 epoch, inprog := c.readEpoch()
349 if acc.epoch != epoch || inprog {
355 type vecHeader struct {
360 func vectorLen(v unsafe.Pointer) uint64 {
361 vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uintptr(0))))
366 func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
367 return unsafe.Pointer(uintptr(p) + x)