Provide error counters per worker for statsclient
[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 statDirectoryType
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() unsafe.Pointer {
68         header := ss.loadSharedHeader(ss.sharedHeader)
69         return ss.adjust(unsafe.Pointer(&header.dirVector))
70 }
71
72 func (ss *statSegmentV2) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) {
73         statSegDir := unsafe.Pointer(uintptr(p) + 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(statSegDir unsafe.Pointer) adapter.Stat {
91         dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
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(unsafe.Pointer(&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(unsafe.Pointer(&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(unsafe.Pointer(&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                 // no-op
206
207         default:
208                 // TODO: monitor occurrences with metrics
209                 debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
210         }
211         return nil
212 }
213
214 func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapter.Stat) error {
215         dirEntry := (*statSegDirectoryEntryV2)(statSegDir)
216         switch (*stat).(type) {
217         case adapter.ScalarStat:
218                 *stat = adapter.ScalarStat(dirEntry.unionData)
219
220         case adapter.ErrorStat:
221                 dirVector := ss.getErrorVector()
222                 if dirVector == nil {
223                         debugf("error vector pointer is out of range for %s", dirEntry.name)
224                         return nil
225                 }
226                 vecLen := *(*uint32)(vectorLen(dirVector))
227                 var errData []adapter.Counter
228                 for i := uint32(0); i < vecLen; i++ {
229                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
230                         cbVal := ss.adjust(vectorLen(cb))
231                         if cbVal == nil {
232                                 debugf("error counter pointer out of range")
233                                 continue
234                         }
235                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
236                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
237                         errData = append(errData, val)
238                 }
239                 *stat = adapter.ErrorStat(errData)
240
241         case adapter.SimpleCounterStat:
242                 dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
243                 if dirVector == nil {
244                         debugf("data vector pointer is out of range for %s", dirEntry.name)
245                         return nil
246                 }
247                 vecLen := *(*uint32)(vectorLen(dirVector))
248                 data := (*stat).(adapter.SimpleCounterStat)
249                 if uint32(len(data)) != vecLen {
250                         return ErrStatDataLenIncorrect
251                 }
252                 for i := uint32(0); i < vecLen; i++ {
253                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
254                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
255                         if counterVector == nil {
256                                 debugf("counter (vector simple) pointer out of range")
257                                 continue
258                         }
259                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
260                         data[i] = make([]adapter.Counter, counterVectorLength)
261                         for j := uint32(0); j < counterVectorLength; j++ {
262                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
263                                 val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
264                                 data[i][j] = val
265                         }
266                 }
267
268         case adapter.CombinedCounterStat:
269                 dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
270                 if dirVector == nil {
271                         debugf("data vector pointer is out of range for %s", dirEntry.name)
272                         return nil
273                 }
274                 vecLen := *(*uint32)(vectorLen(dirVector))
275                 data := (*stat).(adapter.CombinedCounterStat)
276                 for i := uint32(0); i < vecLen; i++ {
277                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
278                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
279                         if counterVector == nil {
280                                 debugf("counter (vector combined) pointer out of range")
281                                 continue
282                         }
283                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
284                         data[i] = make([]adapter.CombinedCounter, counterVectorLength)
285                         for j := uint32(0); j < counterVectorLength; j++ {
286                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
287                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
288                                 data[i][j] = val
289                         }
290                 }
291
292         case adapter.NameStat:
293                 dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData))
294                 if dirVector == nil {
295                         debugf("data vector pointer is out of range for %s", dirEntry.name)
296                         return nil
297                 }
298                 vecLen := *(*uint32)(vectorLen(dirVector))
299                 data := (*stat).(adapter.NameStat)
300                 for i := uint32(0); i < vecLen; i++ {
301                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
302                         if uintptr(nameVectorOffset) == 0 {
303                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
304                                 continue
305                         }
306                         nameVector := ss.adjust(vectorLen(nameVectorOffset))
307                         if nameVector == nil {
308                                 debugf("name data pointer out of range")
309                                 continue
310                         }
311                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
312                         nameData := data[i]
313                         if uint32(len(nameData))+1 != nameVectorLen {
314                                 return ErrStatDataLenIncorrect
315                         }
316                         for j := uint32(0); j < nameVectorLen; j++ {
317                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
318                                 value := *(*byte)(statSegPointer(nameVector, offset))
319                                 if value == 0 {
320                                         break
321                                 }
322                                 nameData[j] = value
323                         }
324                 }
325
326         default:
327                 if Debug {
328                         Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)
329                 }
330         }
331         return nil
332 }
333
334 // Adjust data pointer using shared header and base and return
335 // the pointer to a data segment
336 func (ss *statSegmentV2) adjust(data unsafe.Pointer) unsafe.Pointer {
337         header := ss.loadSharedHeader(ss.sharedHeader)
338         adjusted := unsafe.Pointer(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) +
339                 uintptr(*(*uint64)(data)) - uintptr(*(*uint64)(unsafe.Pointer(&header.base))))
340         if uintptr(unsafe.Pointer(&ss.sharedHeader[len(ss.sharedHeader)-1])) <= uintptr(adjusted) ||
341                 uintptr(unsafe.Pointer(&ss.sharedHeader[0])) >= uintptr(adjusted) {
342                 return nil
343         }
344         return adjusted
345 }
346
347 func (ss *statSegmentV2) getErrorVector() unsafe.Pointer {
348         header := ss.loadSharedHeader(ss.sharedHeader)
349         return ss.adjust(unsafe.Pointer(&header.errorVector))
350 }