Improvements for binapi-generator and support VPP 19.04 in statsclient
[govpp.git] / adapter / statsclient / stat_segment.go
1 //  Copyright (c) 2019 Cisco and/or its affiliates.
2 //
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:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 package statsclient
16
17 import (
18         "fmt"
19         "net"
20         "sync/atomic"
21         "syscall"
22         "time"
23         "unsafe"
24
25         "github.com/ftrvxmtrx/fd"
26
27         "git.fd.io/govpp.git/adapter"
28 )
29
30 var (
31         maxWaitInProgress = time.Second * 1
32 )
33
34 type statSegDirectoryEntry struct {
35         directoryType statDirectoryType
36         // unionData can represent: offset, index or value
37         unionData    uint64
38         offsetVector uint64
39         name         [128]byte
40 }
41
42 type statDirectoryType int32
43
44 func (t statDirectoryType) String() string {
45         return adapter.StatType(t).String()
46 }
47
48 type statSegment struct {
49         sharedHeader []byte
50         memorySize   int64
51
52         oldHeader bool
53 }
54
55 func (c *statSegment) connect(sockName string) error {
56         addr := &net.UnixAddr{
57                 Net:  "unixpacket",
58                 Name: sockName,
59         }
60
61         Log.Debugf("connecting to: %v", addr)
62
63         conn, err := net.DialUnix(addr.Net, nil, addr)
64         if err != nil {
65                 Log.Warnf("connecting to socket %s failed: %s", addr, err)
66                 return err
67         }
68         defer func() {
69                 if err := conn.Close(); err != nil {
70                         Log.Warnf("closing socket failed: %v", err)
71                 }
72         }()
73
74         Log.Debugf("connected to socket")
75
76         files, err := fd.Get(conn, 1, nil)
77         if err != nil {
78                 return fmt.Errorf("getting file descriptor over socket failed: %v", err)
79         }
80         if len(files) == 0 {
81                 return fmt.Errorf("no files received over socket")
82         }
83         defer func() {
84                 for _, f := range files {
85                         if err := f.Close(); err != nil {
86                                 Log.Warnf("closing file %s failed: %v", f.Name(), err)
87                         }
88                 }
89         }()
90
91         Log.Debugf("received %d files over socket", len(files))
92
93         f := files[0]
94
95         info, err := f.Stat()
96         if err != nil {
97                 return err
98         }
99
100         size := info.Size()
101
102         Log.Debugf("fd: name=%v size=%v", info.Name(), size)
103
104         data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
105         if err != nil {
106                 Log.Warnf("mapping shared memory failed: %v", err)
107                 return fmt.Errorf("mapping shared memory failed: %v", err)
108         }
109
110         Log.Debugf("successfuly mapped shared memory")
111
112         c.sharedHeader = data
113         c.memorySize = size
114
115         header := c.readHeader()
116         Log.Debugf("stat segment header: %+v", header)
117
118         // older VPP (19.04) did not have version in stat segment header
119         // we try to provide fallback support by skipping it in header
120         if header.version > MaxVersion && header.inProgress > 1 && header.epoch == 0 {
121                 h := c.readHeaderOld()
122                 Log.Warnf("falling back to old stat segment header version: %+v", h)
123                 c.oldHeader = true
124         }
125
126         return nil
127 }
128
129 func (c *statSegment) disconnect() error {
130         if err := syscall.Munmap(c.sharedHeader); err != nil {
131                 Log.Warnf("unmapping shared memory failed: %v", err)
132                 return fmt.Errorf("unmapping shared memory failed: %v", err)
133         }
134
135         Log.Debugf("successfuly unmapped shared memory")
136
137         return nil
138 }
139
140 type sharedHeaderBase struct {
141         epoch           int64
142         inProgress      int64
143         directoryOffset int64
144         errorOffset     int64
145         statsOffset     int64
146 }
147
148 type statSegSharedHeader struct {
149         version uint64
150         sharedHeaderBase
151 }
152
153 func (c *statSegment) readHeaderOld() (header statSegSharedHeader) {
154         h := (*sharedHeaderBase)(unsafe.Pointer(&c.sharedHeader[0]))
155         header.version = 0
156         header.epoch = atomic.LoadInt64(&h.epoch)
157         header.inProgress = atomic.LoadInt64(&h.inProgress)
158         header.directoryOffset = atomic.LoadInt64(&h.directoryOffset)
159         header.errorOffset = atomic.LoadInt64(&h.errorOffset)
160         header.statsOffset = atomic.LoadInt64(&h.statsOffset)
161         return
162 }
163
164 func (c *statSegment) readHeader() (header statSegSharedHeader) {
165         h := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
166         header.version = atomic.LoadUint64(&h.version)
167         header.epoch = atomic.LoadInt64(&h.epoch)
168         header.inProgress = atomic.LoadInt64(&h.inProgress)
169         header.directoryOffset = atomic.LoadInt64(&h.directoryOffset)
170         header.errorOffset = atomic.LoadInt64(&h.errorOffset)
171         header.statsOffset = atomic.LoadInt64(&h.statsOffset)
172         return
173 }
174
175 func (c *statSegment) readVersion() uint64 {
176         if c.oldHeader {
177                 return 0
178         }
179         header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
180         version := atomic.LoadUint64(&header.version)
181         return version
182 }
183
184 func (c *statSegment) readEpoch() (int64, bool) {
185         if c.oldHeader {
186                 h := c.readHeaderOld()
187                 return h.epoch, h.inProgress != 0
188         }
189         header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
190         epoch := atomic.LoadInt64(&header.epoch)
191         inprog := atomic.LoadInt64(&header.inProgress)
192         return epoch, inprog != 0
193 }
194
195 func (c *statSegment) readOffsets() (dir, err, stat int64) {
196         if c.oldHeader {
197                 h := c.readHeaderOld()
198                 return h.directoryOffset, h.errorOffset, h.statsOffset
199         }
200         header := (*statSegSharedHeader)(unsafe.Pointer(&c.sharedHeader[0]))
201         dirOffset := atomic.LoadInt64(&header.directoryOffset)
202         errOffset := atomic.LoadInt64(&header.errorOffset)
203         statOffset := atomic.LoadInt64(&header.statsOffset)
204         return dirOffset, errOffset, statOffset
205 }
206
207 type statSegAccess struct {
208         epoch int64
209 }
210
211 func (c *statSegment) accessStart() *statSegAccess {
212         epoch, inprog := c.readEpoch()
213         t := time.Now()
214         for inprog {
215                 if time.Since(t) > maxWaitInProgress {
216                         return nil
217                 }
218                 epoch, inprog = c.readEpoch()
219         }
220         return &statSegAccess{
221                 epoch: epoch,
222         }
223 }
224
225 func (c *statSegment) accessEnd(acc *statSegAccess) bool {
226         epoch, inprog := c.readEpoch()
227         if acc.epoch != epoch || inprog {
228                 return false
229         }
230         return true
231 }
232
233 type vecHeader struct {
234         length     uint64
235         vectorData [0]uint8
236 }
237
238 func vectorLen(v unsafe.Pointer) uint64 {
239         vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uintptr(0))))
240         return vec.length
241 }
242
243 //go:nosplit
244 func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
245         return unsafe.Pointer(uintptr(p) + x)
246 }