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