7091ff9cccd13873eeadbfbeca498bfe6bcf80f0
[govpp.git] / adapter / statsclient / statseg_v2.go
1 //  Copyright (c) 2020 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         "sync/atomic"
20         "unsafe"
21
22         "git.fd.io/govpp.git/adapter"
23 )
24
25 type statSegmentV2 struct {
26         sharedHeader []byte
27         memorySize   int64
28 }
29
30 type sharedHeaderV2 struct {
31         version     uint64
32         base        unsafe.Pointer
33         epoch       int64
34         inProgress  int64
35         dirVector   unsafe.Pointer
36         errorVector unsafe.Pointer
37 }
38
39 type statSegDirectoryEntryV2 struct {
40         directoryType statDirectoryType
41         // unionData can represent:
42         // - index
43         // - value
44         // - pointer to data
45         unionData uint64
46         name      [128]byte
47 }
48
49 func newStatSegmentV2(data []byte, size int64) *statSegmentV2 {
50         return &statSegmentV2{
51                 sharedHeader: data,
52                 memorySize:   size,
53         }
54 }
55
56 func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) {
57         h := (*sharedHeaderV2)(unsafe.Pointer(&b[0]))
58         return sharedHeaderV2{
59                 version:     atomic.LoadUint64(&h.version),
60                 base:        atomic.LoadPointer(&h.base),
61                 epoch:       atomic.LoadInt64(&h.epoch),
62                 inProgress:  atomic.LoadInt64(&h.inProgress),
63                 dirVector:   atomic.LoadPointer(&h.dirVector),
64                 errorVector: atomic.LoadPointer(&h.errorVector),
65         }
66 }
67
68 func (ss *statSegmentV2) GetDirectoryVector() (unsafe.Pointer, error) {
69         header := ss.loadSharedHeader(ss.sharedHeader)
70         return ss.adjust(unsafe.Pointer(&header.dirVector))
71 }
72
73 func (ss *statSegmentV2) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) {
74         statSegDir := unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
75         dir := (*statSegDirectoryEntryV2)(statSegDir)
76         var name []byte
77         for n := 0; n < len(dir.name); n++ {
78                 if dir.name[n] == 0 {
79                         name = dir.name[:n]
80                         break
81                 }
82         }
83         return statSegDir, name, dir.directoryType
84 }
85
86 func (ss *statSegmentV2) GetEpoch() (int64, bool) {
87         sh := ss.loadSharedHeader(ss.sharedHeader)
88         return sh.epoch, sh.inProgress != 0
89 }
90
91 func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat {
92         dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
93         if dirEntry.unionData == 0 {
94                 debugf("data value or pointer not defined for %s", dirEntry.name)
95                 return nil
96         }
97
98         switch adapter.StatType(dirEntry.directoryType) {
99         case statDirScalarIndex:
100                 return adapter.ScalarStat(dirEntry.unionData)
101
102         case statDirErrorIndex:
103                 dirVector, err := ss.getErrorVector()
104                 if err != nil {
105                         debugf("error vector pointer is out of range for %s", dirEntry.name)
106                         return nil
107                 }
108                 vecLen := *(*uint32)(vectorLen(dirVector))
109                 var errData adapter.Counter
110                 for i := uint32(0); i < vecLen; i++ {
111                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
112                         cbVal, err := ss.adjust(vectorLen(cb))
113                         if err != nil {
114                                 debugf("error counter pointer out of range")
115                                 continue
116                         }
117                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
118                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
119                         errData += val
120                 }
121                 return adapter.ErrorStat(errData)
122
123         case statDirCounterVectorSimple:
124                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
125                 if err != nil {
126                         debugf("data vector pointer is out of range for %s", dirEntry.name)
127                         return nil
128                 }
129                 vecLen := *(*uint32)(vectorLen(dirVector))
130                 data := make([][]adapter.Counter, vecLen)
131                 for i := uint32(0); i < vecLen; i++ {
132                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
133                         counterVector, err := ss.adjust(vectorLen(counterVectorOffset))
134                         if err != nil {
135                                 debugf("counter (vector simple) pointer out of range")
136                                 continue
137                         }
138                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
139                         data[i] = make([]adapter.Counter, counterVectorLength)
140                         for j := uint32(0); j < counterVectorLength; j++ {
141                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
142                                 val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
143                                 data[i][j] = val
144                         }
145                 }
146                 return adapter.SimpleCounterStat(data)
147
148         case statDirCounterVectorCombined:
149                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
150                 if err != nil {
151                         debugf("data vector pointer is out of range for %s", dirEntry.name)
152                         return nil
153                 }
154                 vecLen := *(*uint32)(vectorLen(dirVector))
155                 data := make([][]adapter.CombinedCounter, vecLen)
156                 for i := uint32(0); i < vecLen; i++ {
157                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
158                         counterVector, err := ss.adjust(vectorLen(counterVectorOffset))
159                         if err != nil {
160                                 debugf("counter (vector combined) pointer out of range")
161                                 continue
162                         }
163                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
164                         data[i] = make([]adapter.CombinedCounter, counterVectorLength)
165                         for j := uint32(0); j < counterVectorLength; j++ {
166                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
167                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
168                                 data[i][j] = val
169                         }
170                 }
171                 return adapter.CombinedCounterStat(data)
172
173         case statDirNameVector:
174                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
175                 if err != nil {
176                         debugf("data vector pointer is out of range for %s", dirEntry.name)
177                         return nil
178                 }
179                 vecLen := *(*uint32)(vectorLen(dirVector))
180                 data := make([]adapter.Name, vecLen)
181                 for i := uint32(0); i < vecLen; i++ {
182                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
183                         if uintptr(nameVectorOffset) == 0 {
184                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
185                                 continue
186                         }
187                         nameVector, err := ss.adjust(vectorLen(nameVectorOffset))
188                         if err != nil {
189                                 debugf("name data pointer out of range")
190                                 continue
191                         }
192                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
193                         name := make([]byte, 0, nameVectorLen)
194                         for j := uint32(0); j < nameVectorLen; j++ {
195                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
196                                 value := *(*byte)(statSegPointer(nameVector, offset))
197                                 if value > 0 {
198                                         name = append(name, value)
199                                 }
200                         }
201                         data[i] = name
202                 }
203                 return adapter.NameStat(data)
204
205         case statDirEmpty:
206                 // no-op
207
208         default:
209                 // TODO: monitor occurrences with metrics
210                 debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
211         }
212         return nil
213 }
214
215 func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapter.Stat) error {
216         dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
217         switch (*stat).(type) {
218         case adapter.ScalarStat:
219                 *stat = adapter.ScalarStat(dirEntry.unionData)
220
221         case adapter.ErrorStat:
222                 dirVector, err := ss.getErrorVector()
223                 if err != nil {
224                         debugf("error vector pointer is out of range for %s", dirEntry.name)
225                         return nil
226                 }
227                 vecLen := *(*uint32)(vectorLen(dirVector))
228                 var errData adapter.Counter
229                 for i := uint32(0); i < vecLen; i++ {
230                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
231                         cbVal, err := ss.adjust(vectorLen(cb))
232                         if err != nil {
233                                 debugf("error counter pointer out of range")
234                                 continue
235                         }
236                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
237                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
238                         errData += val
239                 }
240                 *stat = adapter.ErrorStat(errData)
241
242         case adapter.SimpleCounterStat:
243                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
244                 if err != nil {
245                         debugf("data vector pointer is out of range for %s", dirEntry.name)
246                         return nil
247                 }
248                 vecLen := *(*uint32)(vectorLen(dirVector))
249                 data := (*stat).(adapter.SimpleCounterStat)
250                 if uint32(len(data)) != vecLen {
251                         return ErrStatDataLenIncorrect
252                 }
253                 for i := uint32(0); i < vecLen; i++ {
254                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
255                         counterVector, err := ss.adjust(vectorLen(counterVectorOffset))
256                         if err != nil {
257                                 debugf("counter (vector simple) pointer out of range")
258                                 continue
259                         }
260                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
261                         data[i] = make([]adapter.Counter, counterVectorLength)
262                         for j := uint32(0); j < counterVectorLength; j++ {
263                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
264                                 val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
265                                 data[i][j] = val
266                         }
267                 }
268
269         case adapter.CombinedCounterStat:
270                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
271                 if err != nil {
272                         debugf("data vector pointer is out of range for %s", dirEntry.name)
273                         return nil
274                 }
275                 vecLen := *(*uint32)(vectorLen(dirVector))
276                 data := (*stat).(adapter.CombinedCounterStat)
277                 for i := uint32(0); i < vecLen; i++ {
278                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
279                         counterVector, err := ss.adjust(vectorLen(counterVectorOffset))
280                         if err != nil {
281                                 debugf("counter (vector combined) pointer out of range")
282                                 continue
283                         }
284                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
285                         data[i] = make([]adapter.CombinedCounter, counterVectorLength)
286                         for j := uint32(0); j < counterVectorLength; j++ {
287                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
288                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
289                                 data[i][j] = val
290                         }
291                 }
292
293         case adapter.NameStat:
294                 dirVector, err := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
295                 if err != nil {
296                         debugf("data vector pointer is out of range for %s", dirEntry.name)
297                         return nil
298                 }
299                 vecLen := *(*uint32)(vectorLen(dirVector))
300                 data := (*stat).(adapter.NameStat)
301                 for i := uint32(0); i < vecLen; i++ {
302                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
303                         if uintptr(nameVectorOffset) == 0 {
304                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
305                                 continue
306                         }
307                         nameVector, err := ss.adjust(vectorLen(nameVectorOffset))
308                         if err != nil {
309                                 debugf("name data pointer out of range")
310                                 continue
311                         }
312                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
313                         nameData := data[i]
314                         if uint32(len(nameData))+1 != nameVectorLen {
315                                 return ErrStatDataLenIncorrect
316                         }
317                         for j := uint32(0); j < nameVectorLen; j++ {
318                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
319                                 value := *(*byte)(statSegPointer(nameVector, offset))
320                                 if value == 0 {
321                                         break
322                                 }
323                                 nameData[j] = value
324                         }
325                 }
326
327         default:
328                 if Debug {
329                         Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)
330                 }
331         }
332         return nil
333 }
334
335 // Adjust data pointer using shared header and base and return
336 // the pointer to a data segment
337 func (ss *statSegmentV2) adjust(data unsafe.Pointer) (unsafe.Pointer, error) {
338         header := ss.loadSharedHeader(ss.sharedHeader)
339         adjusted := unsafe.Pointer(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) +
340                 uintptr(*(*uint64)(data)) - uintptr(*(*uint64)(unsafe.Pointer(&header.base))))
341         if uintptr(unsafe.Pointer(&ss.sharedHeader[len(ss.sharedHeader)-1])) <= uintptr(adjusted) ||
342                 uintptr(unsafe.Pointer(&ss.sharedHeader[0])) >= uintptr(adjusted) {
343                 return nil, fmt.Errorf("adjusted data out of range for %v", data)
344         }
345         return adjusted, nil
346 }
347
348 func (ss *statSegmentV2) getErrorVector() (unsafe.Pointer, error) {
349         header := ss.loadSharedHeader(ss.sharedHeader)
350         return ss.adjust(unsafe.Pointer(&header.errorVector))
351 }