Fix statseg v1 error offset zero
[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 dirType
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() dirVector {
70         dirOffset, _, _ := ss.getOffsets()
71         return dirVector(&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(v dirVector, index uint32) (dirSegment, dirName, dirType) {
79         statSegDir := dirSegment(uintptr(v) + 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(segment dirSegment, _ uint32) adapter.Stat {
97         dirEntry := (*statSegDirectoryEntryV1)(segment)
98         typ := getStatType(dirEntry.directoryType, true)
99
100         switch typ {
101         case adapter.ScalarIndex:
102                 return adapter.ScalarStat(dirEntry.unionData)
103
104         case adapter.ErrorIndex:
105                 if dirEntry.unionData >= uint64(len(ss.sharedHeader)) {
106                         debugf("offset out of range for %s", dirEntry.name)
107                         break
108                 }
109
110                 _, errOffset, _ := ss.getOffsets()
111                 offsetVector := dirVector(&ss.sharedHeader[errOffset])
112
113                 var errData []adapter.Counter
114
115                 vecLen := *(*uint32)(vectorLen(offsetVector))
116                 for i := uint32(0); i < vecLen; i++ {
117                         cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
118                         offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
119                         debugf("error index, cb: %d, offset: %d", cb, offset)
120                         val := *(*adapter.Counter)(statSegPointer(dirVector(&ss.sharedHeader[0]), offset))
121                         errData = append(errData, val)
122                 }
123                 return adapter.ErrorStat(errData)
124
125         case adapter.SimpleCounterVector:
126                 if dirEntry.unionData == 0 {
127                         debugf("offset invalid for %s", dirEntry.name)
128                         break
129                 } else if dirEntry.unionData >= uint64(len(ss.sharedHeader)) {
130                         debugf("offset out of range for %s", dirEntry.name)
131                         break
132                 }
133
134                 vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData])))
135                 offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector))
136
137                 data := make([][]adapter.Counter, vecLen)
138                 for i := uint32(0); i < vecLen; i++ {
139                         cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
140                         counterVec := dirVector(&ss.sharedHeader[uintptr(cb)])
141                         vecLen2 := *(*uint32)(vectorLen(counterVec))
142                         data[i] = make([]adapter.Counter, vecLen2)
143                         for j := uint32(0); j < vecLen2; j++ {
144                                 offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
145                                 val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
146                                 data[i][j] = val
147                         }
148                 }
149                 return adapter.SimpleCounterStat(data)
150
151         case adapter.CombinedCounterVector:
152                 if dirEntry.unionData == 0 {
153                         debugf("offset invalid for %s", dirEntry.name)
154                         break
155                 } else if dirEntry.unionData >= uint64(len(ss.sharedHeader)) {
156                         debugf("offset out of range for %s", dirEntry.name)
157                         break
158                 }
159
160                 vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData])))
161                 offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector))
162
163                 data := make([][]adapter.CombinedCounter, vecLen)
164                 for i := uint32(0); i < vecLen; i++ {
165                         cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
166                         counterVec := dirVector(&ss.sharedHeader[uintptr(cb)])
167                         vecLen2 := *(*uint32)(vectorLen(counterVec))
168                         data[i] = make([]adapter.CombinedCounter, vecLen2)
169                         for j := uint32(0); j < vecLen2; j++ {
170                                 offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
171                                 val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
172                                 data[i][j] = val
173                         }
174                 }
175                 return adapter.CombinedCounterStat(data)
176
177         case adapter.NameVector:
178                 if dirEntry.unionData == 0 {
179                         debugf("offset invalid for %s", dirEntry.name)
180                         break
181                 } else if dirEntry.unionData >= uint64(len(ss.sharedHeader)) {
182                         debugf("offset out of range for %s", dirEntry.name)
183                         break
184                 }
185
186                 vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData])))
187                 offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector))
188
189                 data := make([]adapter.Name, vecLen)
190                 for i := uint32(0); i < vecLen; i++ {
191                         cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
192                         if cb == 0 {
193                                 debugf("name vector out of range for %s (%v)", dirEntry.name, i)
194                                 continue
195                         }
196                         nameVec := dirVector(&ss.sharedHeader[cb])
197                         vecLen2 := *(*uint32)(vectorLen(nameVec))
198
199                         nameStr := make([]byte, 0, vecLen2)
200                         for j := uint32(0); j < vecLen2; j++ {
201                                 offset := uintptr(j) * unsafe.Sizeof(byte(0))
202                                 val := *(*byte)(statSegPointer(nameVec, offset))
203                                 if val > 0 {
204                                         nameStr = append(nameStr, val)
205                                 }
206                         }
207                         data[i] = nameStr
208                 }
209                 return adapter.NameStat(data)
210
211         case adapter.Empty:
212                 // no-op
213
214         case adapter.Symlink:
215                 debugf("Symlinks are not supported for stats v1")
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(segment dirSegment, stat *adapter.Stat) error {
225         dirEntry := (*statSegDirectoryEntryV1)(segment)
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 := dirVector(&ss.sharedHeader[errOffset])
241
242                 var errData []adapter.Counter
243
244                 vecLen := *(*uint32)(vectorLen(dirVector(&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(dirVector(&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(dirVector(&ss.sharedHeader[dirEntry.unionData])))
263                 offsetVector := statSegPointer(dirVector(&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 := dirVector(&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(dirVector(&ss.sharedHeader[dirEntry.unionData])))
294                 offsetVector := statSegPointer(dirVector(&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 := dirVector(&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(dirVector(&ss.sharedHeader[dirEntry.unionData])))
325                 offsetVector := statSegPointer(dirVector(&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 := dirVector(&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 }