statsclient: fix retrieving stats type
[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         "bytes"
19         "encoding/binary"
20         "sync/atomic"
21         "unsafe"
22
23         "git.fd.io/govpp.git/adapter"
24 )
25
26 type statSegmentV2 struct {
27         sharedHeader []byte
28         memorySize   int64
29 }
30
31 type sharedHeaderV2 struct {
32         version     uint64
33         base        unsafe.Pointer
34         epoch       int64
35         inProgress  int64
36         dirVector   unsafe.Pointer
37         errorVector unsafe.Pointer
38 }
39
40 type statSegDirectoryEntryV2 struct {
41         directoryType dirType
42         // unionData can represent:
43         // - symlink indexes
44         // - index
45         // - value
46         // - pointer to data
47         unionData uint64
48         name      [128]byte
49 }
50
51 func newStatSegmentV2(data []byte, size int64) *statSegmentV2 {
52         return &statSegmentV2{
53                 sharedHeader: data,
54                 memorySize:   size,
55         }
56 }
57
58 func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) {
59         h := (*sharedHeaderV2)(unsafe.Pointer(&b[0]))
60         return sharedHeaderV2{
61                 version:     atomic.LoadUint64(&h.version),
62                 base:        atomic.LoadPointer(&h.base),
63                 epoch:       atomic.LoadInt64(&h.epoch),
64                 inProgress:  atomic.LoadInt64(&h.inProgress),
65                 dirVector:   atomic.LoadPointer(&h.dirVector),
66                 errorVector: atomic.LoadPointer(&h.errorVector),
67         }
68 }
69
70 func (ss *statSegmentV2) GetDirectoryVector() dirVector {
71         header := ss.loadSharedHeader(ss.sharedHeader)
72         return ss.adjust(dirVector(&header.dirVector))
73 }
74
75 func (ss *statSegmentV2) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) {
76         statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
77         dir := (*statSegDirectoryEntryV2)(statSegDir)
78         var name []byte
79         for n := 0; n < len(dir.name); n++ {
80                 if dir.name[n] == 0 {
81                         name = dir.name[:n]
82                         break
83                 }
84         }
85         return statSegDir, name, dir.directoryType
86 }
87
88 func (ss *statSegmentV2) GetEpoch() (int64, bool) {
89         sh := ss.loadSharedHeader(ss.sharedHeader)
90         return sh.epoch, sh.inProgress != 0
91 }
92
93 func (ss *statSegmentV2) CopyEntryData(segment dirSegment, index uint32) adapter.Stat {
94         dirEntry := (*statSegDirectoryEntryV2)(segment)
95         typ := adapter.StatType(dirEntry.directoryType)
96         // skip zero pointer value
97         if typ != statDirScalarIndex && typ != statDirEmpty && dirEntry.unionData == 0 {
98                 debugf("data pointer not defined for %s", dirEntry.name)
99                 return nil
100         }
101
102         switch typ {
103         case statDirScalarIndex:
104                 return adapter.ScalarStat(dirEntry.unionData)
105
106         case statDirErrorIndex:
107                 dirVector := ss.getErrorVector()
108                 if dirVector == nil {
109                         debugf("error vector pointer is out of range for %s", dirEntry.name)
110                         return nil
111                 }
112                 vecLen := *(*uint32)(vectorLen(dirVector))
113                 var errData []adapter.Counter
114                 for i := uint32(0); i < vecLen; i++ {
115                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
116                         cbVal := ss.adjust(vectorLen(cb))
117                         if cbVal == nil {
118                                 debugf("error counter pointer out of range")
119                                 continue
120                         }
121                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
122                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
123                         errData = append(errData, val)
124                 }
125                 return adapter.ErrorStat(errData)
126
127         case statDirCounterVectorSimple:
128                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
129                 if dirVector == nil {
130                         debugf("data vector pointer is out of range for %s", dirEntry.name)
131                         return nil
132                 }
133                 vecLen := *(*uint32)(vectorLen(dirVector))
134                 data := make([][]adapter.Counter, vecLen)
135                 for i := uint32(0); i < vecLen; i++ {
136                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
137                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
138                         if counterVector == nil {
139                                 debugf("counter (vector simple) pointer out of range")
140                                 continue
141                         }
142                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
143                         if index == ^uint32(0) {
144                                 data[i] = make([]adapter.Counter, counterVectorLength)
145                                 for j := uint32(0); j < counterVectorLength; j++ {
146                                         offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
147                                         data[i][j] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
148                                 }
149                         } else {
150                                 data[i] = make([]adapter.Counter, 1) // expect single value
151                                 for j := uint32(0); j < counterVectorLength; j++ {
152                                         offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
153                                         if index == j {
154                                                 data[i][0] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
155                                                 break
156                                         }
157                                 }
158                         }
159                 }
160                 return adapter.SimpleCounterStat(data)
161
162         case statDirCounterVectorCombined:
163                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
164                 if dirVector == nil {
165                         debugf("data vector pointer is out of range for %s", dirEntry.name)
166                         return nil
167                 }
168                 vecLen := *(*uint32)(vectorLen(dirVector))
169                 data := make([][]adapter.CombinedCounter, vecLen)
170                 for i := uint32(0); i < vecLen; i++ {
171                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
172                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
173                         if counterVector == nil {
174                                 debugf("counter (vector combined) pointer out of range")
175                                 continue
176                         }
177                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
178                         if index == ^uint32(0) {
179                                 data[i] = make([]adapter.CombinedCounter, counterVectorLength)
180                                 for j := uint32(0); j < counterVectorLength; j++ {
181                                         offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
182                                         data[i][j] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
183                                 }
184                         } else {
185                                 data[i] = make([]adapter.CombinedCounter, 1) // expect single value pair
186                                 for j := uint32(0); j < counterVectorLength; j++ {
187                                         offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
188                                         if index == j {
189                                                 data[i][0] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
190                                                 break
191                                         }
192                                 }
193                         }
194                 }
195                 return adapter.CombinedCounterStat(data)
196
197         case statDirNameVector:
198                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
199                 if dirVector == nil {
200                         debugf("data vector pointer is out of range for %s", dirEntry.name)
201                         return nil
202                 }
203                 vecLen := *(*uint32)(vectorLen(dirVector))
204                 data := make([]adapter.Name, vecLen)
205                 for i := uint32(0); i < vecLen; i++ {
206                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
207                         if uintptr(nameVectorOffset) == 0 {
208                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
209                                 continue
210                         }
211                         nameVector := ss.adjust(vectorLen(nameVectorOffset))
212                         if nameVector == nil {
213                                 debugf("name data pointer out of range")
214                                 continue
215                         }
216                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
217                         name := make([]byte, 0, nameVectorLen)
218                         for j := uint32(0); j < nameVectorLen; j++ {
219                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
220                                 value := *(*byte)(statSegPointer(nameVector, offset))
221                                 if value > 0 {
222                                         name = append(name, value)
223                                 }
224                         }
225                         data[i] = name
226                 }
227                 return adapter.NameStat(data)
228
229         case statDirEmpty:
230                 return adapter.EmptyStat("<none>")
231                 // no-op
232
233         case statDirSymlink:
234                 // prevent recursion loops
235                 if index != ^uint32(0) {
236                         debugf("received symlink with defined item index")
237                         return nil
238                 }
239                 i1, i2 := ss.getSymlinkIndexes(dirEntry)
240                 // use first index to get the stats directory the symlink points to
241                 header := ss.loadSharedHeader(ss.sharedHeader)
242                 dirVector := ss.adjust(dirVector(&header.dirVector))
243                 statSegDir2 := dirSegment(uintptr(dirVector) + uintptr(i1)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
244
245                 // retry with actual stats segment and use second index to get
246                 // stats for the required item
247                 return ss.CopyEntryData(statSegDir2, i2)
248
249         default:
250                 // TODO: monitor occurrences with metrics
251                 debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
252         }
253         return nil
254 }
255
256 func (ss *statSegmentV2) UpdateEntryData(segment dirSegment, stat *adapter.Stat) error {
257         dirEntry := (*statSegDirectoryEntryV2)(segment)
258         switch (*stat).(type) {
259         case adapter.ScalarStat:
260                 *stat = adapter.ScalarStat(dirEntry.unionData)
261
262         case adapter.ErrorStat:
263                 dirVector := ss.getErrorVector()
264                 if dirVector == nil {
265                         debugf("error vector pointer is out of range for %s", dirEntry.name)
266                         return nil
267                 }
268                 vecLen := *(*uint32)(vectorLen(dirVector))
269                 var errData []adapter.Counter
270                 for i := uint32(0); i < vecLen; i++ {
271                         cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
272                         cbVal := ss.adjust(vectorLen(cb))
273                         if cbVal == nil {
274                                 debugf("error counter pointer out of range")
275                                 continue
276                         }
277                         offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0))
278                         val := *(*adapter.Counter)(statSegPointer(cbVal, offset))
279                         errData = append(errData, val)
280                 }
281                 *stat = adapter.ErrorStat(errData)
282
283         case adapter.SimpleCounterStat:
284                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
285                 if dirVector == nil {
286                         debugf("data vector pointer is out of range for %s", dirEntry.name)
287                         return nil
288                 }
289                 vecLen := *(*uint32)(vectorLen(dirVector))
290                 data := (*stat).(adapter.SimpleCounterStat)
291                 if uint32(len(data)) != vecLen {
292                         return ErrStatDataLenIncorrect
293                 }
294                 for i := uint32(0); i < vecLen; i++ {
295                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
296                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
297                         if counterVector == nil {
298                                 debugf("counter (vector simple) pointer out of range")
299                                 continue
300                         }
301                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
302                         data[i] = make([]adapter.Counter, counterVectorLength)
303                         for j := uint32(0); j < counterVectorLength; j++ {
304                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
305                                 val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
306                                 data[i][j] = val
307                         }
308                 }
309
310         case adapter.CombinedCounterStat:
311                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
312                 if dirVector == nil {
313                         debugf("data vector pointer is out of range for %s", dirEntry.name)
314                         return nil
315                 }
316                 vecLen := *(*uint32)(vectorLen(dirVector))
317                 data := (*stat).(adapter.CombinedCounterStat)
318                 for i := uint32(0); i < vecLen; i++ {
319                         counterVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
320                         counterVector := ss.adjust(vectorLen(counterVectorOffset))
321                         if counterVector == nil {
322                                 debugf("counter (vector combined) pointer out of range")
323                                 continue
324                         }
325                         counterVectorLength := *(*uint32)(vectorLen(counterVector))
326                         data[i] = make([]adapter.CombinedCounter, counterVectorLength)
327                         for j := uint32(0); j < counterVectorLength; j++ {
328                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
329                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
330                                 data[i][j] = val
331                         }
332                 }
333
334         case adapter.NameStat:
335                 dirVector := ss.adjust(dirVector(&dirEntry.unionData))
336                 if dirVector == nil {
337                         debugf("data vector pointer is out of range for %s", dirEntry.name)
338                         return nil
339                 }
340                 vecLen := *(*uint32)(vectorLen(dirVector))
341                 data := (*stat).(adapter.NameStat)
342                 for i := uint32(0); i < vecLen; i++ {
343                         nameVectorOffset := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0)))
344                         if uintptr(nameVectorOffset) == 0 {
345                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
346                                 continue
347                         }
348                         nameVector := ss.adjust(vectorLen(nameVectorOffset))
349                         if nameVector == nil {
350                                 debugf("name data pointer out of range")
351                                 continue
352                         }
353                         nameVectorLen := *(*uint32)(vectorLen(nameVector))
354                         nameData := data[i]
355                         if uint32(len(nameData))+1 != nameVectorLen {
356                                 return ErrStatDataLenIncorrect
357                         }
358                         for j := uint32(0); j < nameVectorLen; j++ {
359                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
360                                 value := *(*byte)(statSegPointer(nameVector, offset))
361                                 if value == 0 {
362                                         break
363                                 }
364                                 nameData[j] = value
365                         }
366                 }
367
368         default:
369                 if Debug {
370                         Log.Debugf("Unrecognized stat type %T for stat entry: %v", stat, dirEntry.name)
371                 }
372         }
373         return nil
374 }
375
376 // Adjust data pointer using shared header and base and return
377 // the pointer to a data segment
378 func (ss *statSegmentV2) adjust(data dirVector) dirVector {
379         header := ss.loadSharedHeader(ss.sharedHeader)
380         adjusted := dirVector(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) +
381                 uintptr(*(*uint64)(data)) - uintptr(*(*uint64)(unsafe.Pointer(&header.base))))
382         if uintptr(unsafe.Pointer(&ss.sharedHeader[len(ss.sharedHeader)-1])) <= uintptr(adjusted) ||
383                 uintptr(unsafe.Pointer(&ss.sharedHeader[0])) >= uintptr(adjusted) {
384                 return nil
385         }
386         return adjusted
387 }
388
389 func (ss *statSegmentV2) getErrorVector() dirVector {
390         header := ss.loadSharedHeader(ss.sharedHeader)
391         return ss.adjust(dirVector(&header.errorVector))
392 }
393
394 func (ss *statSegmentV2) getSymlinkIndexes(dirEntry *statSegDirectoryEntryV2) (index1, index2 uint32) {
395         var b bytes.Buffer
396         if err := binary.Write(&b, binary.LittleEndian, dirEntry.unionData); err != nil {
397                 debugf("error getting symlink indexes for %s: %v", dirEntry.name, err)
398                 return
399         }
400         if len(b.Bytes()) != 8 {
401                 debugf("incorrect symlink union data length for %s: expected 8, got %d", dirEntry.name, len(b.Bytes()))
402                 return
403         }
404         for i := range b.Bytes()[:4] {
405                 index1 += uint32(b.Bytes()[i]) << (uint32(i) * 8)
406         }
407         for i := range b.Bytes()[4:] {
408                 index2 += uint32(b.Bytes()[i+4]) << (uint32(i) * 8)
409         }
410         return
411 }