10bc5f5df646e30fb4fcb7486f0ca3c82e7fd5ed
[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         "sync/atomic"
19         "unsafe"
20
21         "git.fd.io/govpp.git/adapter"
22 )
23
24 type statSegmentV2 struct {
25         sharedHeader []byte
26         memorySize   int64
27 }
28
29 type sharedHeaderV2 struct {
30         version     uint64
31         base        unsafe.Pointer
32         epoch       int64
33         inProgress  int64
34         dirVector   unsafe.Pointer
35         errorVector unsafe.Pointer
36 }
37
38 type statSegDirectoryEntryV2 struct {
39         directoryType dirType
40         // unionData can represent:
41         // - index
42         // - value
43         // - pointer to data
44         unionData uint64
45         name      [128]byte
46 }
47
48 func newStatSegmentV2(data []byte, size int64) *statSegmentV2 {
49         return &statSegmentV2{
50                 sharedHeader: data,
51                 memorySize:   size,
52         }
53 }
54
55 func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) {
56         h := (*sharedHeaderV2)(unsafe.Pointer(&b[0]))
57         return sharedHeaderV2{
58                 version:     atomic.LoadUint64(&h.version),
59                 base:        atomic.LoadPointer(&h.base),
60                 epoch:       atomic.LoadInt64(&h.epoch),
61                 inProgress:  atomic.LoadInt64(&h.inProgress),
62                 dirVector:   atomic.LoadPointer(&h.dirVector),
63                 errorVector: atomic.LoadPointer(&h.errorVector),
64         }
65 }
66
67 func (ss *statSegmentV2) GetDirectoryVector() dirVector {
68         header := ss.loadSharedHeader(ss.sharedHeader)
69         return ss.adjust(dirVector(&header.dirVector))
70 }
71
72 func (ss *statSegmentV2) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) {
73         statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
74         dir := (*statSegDirectoryEntryV2)(statSegDir)
75         var name []byte
76         for n := 0; n < len(dir.name); n++ {
77                 if dir.name[n] == 0 {
78                         name = dir.name[:n]
79                         break
80                 }
81         }
82         return statSegDir, name, dir.directoryType
83 }
84
85 func (ss *statSegmentV2) GetEpoch() (int64, bool) {
86         sh := ss.loadSharedHeader(ss.sharedHeader)
87         return sh.epoch, sh.inProgress != 0
88 }
89
90 func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat {
91         dirEntry := (*statSegDirectoryEntryV2)(segment)
92         if dirEntry.unionData == 0 {
93                 debugf("data value or pointer not defined for %s", dirEntry.name)
94                 return nil
95         }
96
97         switch adapter.StatType(dirEntry.directoryType) {
98         case statDirScalarIndex:
99                 return adapter.ScalarStat(dirEntry.unionData)
100
101         case statDirErrorIndex:
102                 dirVector := ss.getErrorVector()
103                 if dirVector == nil {
104                         debugf("error vector pointer is out of range for %s", dirEntry.name)
105                         return nil
106                 }
107                 vecLen := *(*uint32)(vectorLen(dirVector))
108                 var errData []adapter.Counter
109                 for i := uint32(0); i < vecLen; i++ {
110                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
111                         cbVal := ss.adjust(vectorLen(cb))
112                         if cbVal == nil {
113                                 debugf("error counter pointer out of range")
114                                 continue
115                         }
116                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
117                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
118                         errData = append(errData, val)
119                 }
120                 return adapter.ErrorStat(errData)
121
122         case statDirCounterVectorSimple:
123                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
124                 if dirVector == nil {
125                         debugf("data vector pointer is out of range for %s", dirEntry.name)
126                         return nil
127                 }
128                 vecLen := *(*uint32)(vectorLen(dirVector))
129                 data := make([][]adapter.Counter, vecLen)
130                 for i := uint32(0); i < vecLen; i++ {
131                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
132                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
133                         if counterVector == nil {
134                                 debugf("counter (vector simple) pointer out of range")
135                                 continue
136                         }
137                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
138                         data[i] = make([]adapter.Counter, counterVectorLength)
139                         for j := uint32(0); j < counterVectorLength; j++ {
140                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
141                                 val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
142                                 data[i][j] = val
143                         }
144                 }
145                 return adapter.SimpleCounterStat(data)
146
147         case statDirCounterVectorCombined:
148                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
149                 if dirVector == nil {
150                         debugf("data vector pointer is out of range for %s", dirEntry.name)
151                         return nil
152                 }
153                 vecLen := *(*uint32)(vectorLen(dirVector))
154                 data := make([][]adapter.CombinedCounter, vecLen)
155                 for i := uint32(0); i < vecLen; i++ {
156                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
157                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
158                         if counterVector == nil {
159                                 debugf("counter (vector combined) pointer out of range")
160                                 continue
161                         }
162                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
163                         data[i] = make([]adapter.CombinedCounter, counterVectorLength)
164                         for j := uint32(0); j < counterVectorLength; j++ {
165                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
166                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
167                                 data[i][j] = val
168                         }
169                 }
170                 return adapter.CombinedCounterStat(data)
171
172         case statDirNameVector:
173                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
174                 if dirVector == nil {
175                         debugf("data vector pointer is out of range for %s", dirEntry.name)
176                         return nil
177                 }
178                 vecLen := *(*uint32)(vectorLen(dirVector))
179                 data := make([]adapter.Name, vecLen)
180                 for i := uint32(0); i < vecLen; i++ {
181                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
182                         if uintptr(nameVectorOffset) == 0 {
183                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
184                                 continue
185                         }
186                         nameVector := ss.adjust(vectorLen(nameVectorOffset))
187                         if nameVector == nil {
188                                 debugf("name data pointer out of range")
189                                 continue
190                         }
191                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
192                         name := make([]byte, 0, nameVectorLen)
193                         for j := uint32(0); j < nameVectorLen; j++ {
194                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
195                                 value := *(*byte)(statSegPointer(nameVector, offset))
196                                 if value > 0 {
197                                         name = append(name, value)
198                                 }
199                         }
200                         data[i] = name
201                 }
202                 return adapter.NameStat(data)
203
204         case statDirEmpty:
205                 return adapter.EmptyStat("<none>")
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(segment dirSegment, stat *adapter.Stat) error {
216         dirEntry := (*statSegDirectoryEntryV2)(segment)
217         switch (*stat).(type) {
218         case adapter.ScalarStat:
219                 *stat = adapter.ScalarStat(dirEntry.unionData)
220
221         case adapter.ErrorStat:
222                 dirVector := ss.getErrorVector()
223                 if dirVector == 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 := ss.adjust(vectorLen(cb))
232                         if cbVal == 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 = append(errData, val)
239                 }
240                 *stat = adapter.ErrorStat(errData)
241
242         case adapter.SimpleCounterStat:
243                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
244                 if dirVector == 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 := ss.adjust(vectorLen(counterVectorOffset))
256                         if counterVector == 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 := ss.adjust(dirVector(&dirEntry.unionData))
271                 if dirVector == 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 := ss.adjust(vectorLen(counterVectorOffset))
280                         if counterVector == 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 := ss.adjust(dirVector(&dirEntry.unionData))
295                 if dirVector == 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 := ss.adjust(vectorLen(nameVectorOffset))
308                         if nameVector == 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 dirVector) dirVector {
338         header := ss.loadSharedHeader(ss.sharedHeader)
339         adjusted := dirVector(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
344         }
345         return adjusted
346 }
347
348 func (ss *statSegmentV2) getErrorVector() dirVector {
349         header := ss.loadSharedHeader(ss.sharedHeader)
350         return ss.adjust(dirVector(&header.errorVector))
351 }