Skip to content

Commit db87efa

Browse files
committed
Fix statsclient for VPP 20.05-rc0 (master)
- this change fixes panic that was occurring with recent VPP that was caused by incorrectly calculated vector length - converting returned vector length from uint64 to uint32 results in correct length value Change-Id: I76a4b9d147c3df3bea9d3e5ef5853e2809dc42e8 Signed-off-by: Ondrej Fabry <[email protected]>
1 parent 9f27eef commit db87efa

File tree

6 files changed

+102
-75
lines changed

6 files changed

+102
-75
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This file lists changes for the GoVPP releases.
1111
-
1212
-->
1313

14+
## 0.3.2
15+
> _20 March 2020_
16+
17+
### Fixes
18+
- statsclient: Fix panic occurring with VPP 20.05-rc0 (master)
19+
1420
## 0.3.0
1521
> _18 March 2020_
1622

adapter/stats_api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (n Name) String() string {
113113
return string(n)
114114
}
115115

116-
// Data represents some type of stat which is usually defined by StatType.
116+
// Stat represents some type of stat which is usually defined by StatType.
117117
type Stat interface {
118118
// IsZero returns true if all of its values equal to zero.
119119
IsZero() bool
@@ -205,7 +205,7 @@ func ReduceSimpleCounterStatIndex(s SimpleCounterStat, i int) uint64 {
205205
return val
206206
}
207207

208-
// ReduceSimpleCounterStatIndex returns reduced CombinedCounterStat s for index i.
208+
// ReduceCombinedCounterStatIndex returns reduced CombinedCounterStat s for index i.
209209
func ReduceCombinedCounterStatIndex(s CombinedCounterStat, i int) [2]uint64 {
210210
var val [2]uint64
211211
for _, w := range s {

adapter/statsclient/stat_segment.go

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,11 @@ type statSegment struct {
5252
legacyVersion bool
5353
}
5454

55-
func (c *statSegment) getStatDirVector() unsafe.Pointer {
56-
dirOffset, _, _ := c.getOffsets()
57-
return unsafe.Pointer(&c.sharedHeader[dirOffset])
58-
}
59-
60-
func (c *statSegment) getStatDirIndex(p unsafe.Pointer, index uint32) *statSegDirectoryEntry {
61-
return (*statSegDirectoryEntry)(unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntry{})))
62-
}
63-
64-
func (c *statSegment) getHeader() (header statSegSharedHeader) {
55+
func (c *statSegment) getHeader() (header sharedHeader) {
6556
if c.legacyVersion {
66-
return statSegHeaderLegacy(c.sharedHeader)
57+
return loadSharedHeaderLegacy(c.sharedHeader)
6758
}
68-
return statSegHeader(c.sharedHeader)
59+
return loadSharedHeader(c.sharedHeader)
6960
}
7061

7162
func (c *statSegment) getEpoch() (int64, bool) {
@@ -129,17 +120,17 @@ func (c *statSegment) connect(sockName string) error {
129120
return fmt.Errorf("mapping shared memory failed: %v", err)
130121
}
131122

123+
Log.Debugf("successfuly mmapped shared memory segment (size: %v) %v", size, len(data))
124+
132125
c.sharedHeader = data
133126
c.memorySize = size
134127

135-
Log.Debugf("successfuly mmapped shared memory segment (size: %v)", size)
136-
137-
hdr := statSegHeader(c.sharedHeader)
128+
hdr := loadSharedHeader(c.sharedHeader)
138129
Log.Debugf("stat segment header: %+v", hdr)
139130

140131
if hdr.legacyVersion() {
141132
c.legacyVersion = true
142-
hdr = statSegHeaderLegacy(c.sharedHeader)
133+
hdr = loadSharedHeaderLegacy(c.sharedHeader)
143134
Log.Debugf("falling back to legacy version (VPP <=19.04) of stat segment (header: %+v)", hdr)
144135
}
145136

@@ -167,6 +158,16 @@ func (c *statSegment) disconnect() error {
167158

168159
type statDirectoryType int32
169160

161+
const (
162+
statDirIllegal = 0
163+
statDirScalarIndex = 1
164+
statDirCounterVectorSimple = 2
165+
statDirCounterVectorCombined = 3
166+
statDirErrorIndex = 4
167+
statDirNameVector = 5
168+
statDirEmpty = 6
169+
)
170+
170171
func (t statDirectoryType) String() string {
171172
return adapter.StatType(t).String()
172173
}
@@ -182,14 +183,23 @@ type statSegDirectoryEntry struct {
182183
name [128]byte
183184
}
184185

186+
func (c *statSegment) getStatDirVector() unsafe.Pointer {
187+
dirOffset, _, _ := c.getOffsets()
188+
return unsafe.Pointer(&c.sharedHeader[dirOffset])
189+
}
190+
191+
func (c *statSegment) getStatDirIndex(p unsafe.Pointer, index uint32) *statSegDirectoryEntry {
192+
return (*statSegDirectoryEntry)(unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntry{})))
193+
}
194+
185195
func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Stat {
186196
dirType := adapter.StatType(dirEntry.directoryType)
187197

188198
switch dirType {
189-
case adapter.ScalarIndex:
199+
case statDirScalarIndex:
190200
return adapter.ScalarStat(dirEntry.unionData)
191201

192-
case adapter.ErrorIndex:
202+
case statDirErrorIndex:
193203
_, errOffset, _ := c.getOffsets()
194204
offsetVector := unsafe.Pointer(&c.sharedHeader[errOffset])
195205

@@ -200,17 +210,19 @@ func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Sta
200210
val := *(*adapter.Counter)(statSegPointer(offsetVector, offset))
201211
errData = val
202212
} else {
203-
vecLen := vectorLen(offsetVector)
204-
for i := uint64(0); i < vecLen; i++ {
213+
vecLen := uint32(vectorLen(offsetVector))
214+
215+
for i := uint32(0); i < vecLen; i++ {
205216
cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
206217
offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0))
218+
debugf("error index, cb: %d, offset: %d", cb, offset)
207219
val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), offset))
208220
errData += val
209221
}
210222
}
211223
return adapter.ErrorStat(errData)
212224

213-
case adapter.SimpleCounterVector:
225+
case statDirCounterVectorSimple:
214226
if dirEntry.unionData == 0 {
215227
debugf("offset invalid for %s", dirEntry.name)
216228
break
@@ -219,24 +231,24 @@ func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Sta
219231
break
220232
}
221233

222-
vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
234+
vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
223235
offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
224236

225237
data := make([][]adapter.Counter, vecLen)
226-
for i := uint64(0); i < vecLen; i++ {
238+
for i := uint32(0); i < vecLen; i++ {
227239
cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
228240
counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
229-
vecLen2 := vectorLen(counterVec)
241+
vecLen2 := uint32(vectorLen(counterVec))
230242
data[i] = make([]adapter.Counter, vecLen2)
231-
for j := uint64(0); j < vecLen2; j++ {
243+
for j := uint32(0); j < vecLen2; j++ {
232244
offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
233245
val := *(*adapter.Counter)(statSegPointer(counterVec, offset))
234246
data[i][j] = val
235247
}
236248
}
237249
return adapter.SimpleCounterStat(data)
238250

239-
case adapter.CombinedCounterVector:
251+
case statDirCounterVectorCombined:
240252
if dirEntry.unionData == 0 {
241253
debugf("offset invalid for %s", dirEntry.name)
242254
break
@@ -245,24 +257,24 @@ func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Sta
245257
break
246258
}
247259

248-
vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
260+
vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
249261
offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
250262

251263
data := make([][]adapter.CombinedCounter, vecLen)
252-
for i := uint64(0); i < vecLen; i++ {
264+
for i := uint32(0); i < vecLen; i++ {
253265
cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
254266
counterVec := unsafe.Pointer(&c.sharedHeader[uintptr(cb)])
255-
vecLen2 := vectorLen(counterVec)
267+
vecLen2 := uint32(vectorLen(counterVec))
256268
data[i] = make([]adapter.CombinedCounter, vecLen2)
257-
for j := uint64(0); j < vecLen2; j++ {
269+
for j := uint32(0); j < vecLen2; j++ {
258270
offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
259271
val := *(*adapter.CombinedCounter)(statSegPointer(counterVec, offset))
260272
data[i][j] = val
261273
}
262274
}
263275
return adapter.CombinedCounterStat(data)
264276

265-
case adapter.NameVector:
277+
case statDirNameVector:
266278
if dirEntry.unionData == 0 {
267279
debugf("offset invalid for %s", dirEntry.name)
268280
break
@@ -271,21 +283,21 @@ func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Sta
271283
break
272284
}
273285

274-
vecLen := vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData]))
286+
vecLen := uint32(vectorLen(unsafe.Pointer(&c.sharedHeader[dirEntry.unionData])))
275287
offsetVector := statSegPointer(unsafe.Pointer(&c.sharedHeader[0]), uintptr(dirEntry.offsetVector))
276288

277289
data := make([]adapter.Name, vecLen)
278-
for i := uint64(0); i < vecLen; i++ {
290+
for i := uint32(0); i < vecLen; i++ {
279291
cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0))))
280292
if cb == 0 {
281293
debugf("name vector out of range for %s (%v)", dirEntry.name, i)
282294
continue
283295
}
284296
nameVec := unsafe.Pointer(&c.sharedHeader[cb])
285-
vecLen2 := vectorLen(nameVec)
297+
vecLen2 := uint32(vectorLen(nameVec))
286298

287299
nameStr := make([]byte, 0, vecLen2)
288-
for j := uint64(0); j < vecLen2; j++ {
300+
for j := uint32(0); j < vecLen2; j++ {
289301
offset := uintptr(j) * unsafe.Sizeof(byte(0))
290302
val := *(*byte)(statSegPointer(nameVec, offset))
291303
if val > 0 {
@@ -296,11 +308,13 @@ func (c *statSegment) copyEntryData(dirEntry *statSegDirectoryEntry) adapter.Sta
296308
}
297309
return adapter.NameStat(data)
298310

311+
case statDirEmpty:
312+
// no-op
313+
299314
default:
300315
// TODO: monitor occurrences with metrics
301316
debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
302317
}
303-
304318
return nil
305319
}
306320

adapter/statsclient/statsclient.go

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,24 @@ func (c *StatsClient) ListStats(patterns ...string) (names []string, err error)
121121
if err != nil {
122122
return nil, err
123123
}
124+
125+
dirVector := c.getStatDirVector()
126+
vecLen := uint32(vectorLen(dirVector))
127+
124128
for _, index := range indexes {
125-
name, err := c.entryName(index)
126-
if err != nil {
127-
return nil, err
129+
if index >= vecLen {
130+
return nil, fmt.Errorf("stat entry index %d out of dir vector len (%d)", index, vecLen)
128131
}
129-
names = append(names, name)
132+
133+
dirEntry := c.getStatDirIndex(dirVector, index)
134+
var name []byte
135+
for n := 0; n < len(dirEntry.name); n++ {
136+
if dirEntry.name[n] == 0 {
137+
name = dirEntry.name[:n]
138+
break
139+
}
140+
}
141+
names = append(names, string(name))
130142
}
131143

132144
if !c.accessEnd(&sa) {
@@ -142,11 +154,11 @@ func (c *StatsClient) DumpStats(patterns ...string) (entries []adapter.StatEntry
142154
return nil, adapter.ErrStatsAccessFailed
143155
}
144156

145-
dir, err := c.listIndexes(patterns...)
157+
indexes, err := c.listIndexes(patterns...)
146158
if err != nil {
147159
return nil, err
148160
}
149-
if entries, err = c.dumpEntries(dir); err != nil {
161+
if entries, err = c.dumpEntries(indexes); err != nil {
150162
return nil, err
151163
}
152164

@@ -261,7 +273,7 @@ func (c *StatsClient) listIndexes(patterns ...string) (indexes []uint32, err err
261273

262274
func (c *StatsClient) listIndexesFunc(f func(name []byte) bool) (indexes []uint32, err error) {
263275
if f == nil {
264-
// there is around ~3150 stats, so to avoid too many allocations
276+
// there is around ~3157 stats, so to avoid too many allocations
265277
// we set capacity to 3200 when listing all stats
266278
indexes = make([]uint32, 0, 3200)
267279
}
@@ -290,33 +302,18 @@ func (c *StatsClient) listIndexesFunc(f func(name []byte) bool) (indexes []uint3
290302
return indexes, nil
291303
}
292304

293-
func (c *StatsClient) entryName(index uint32) (string, error) {
305+
func (c *StatsClient) dumpEntries(indexes []uint32) (entries []adapter.StatEntry, err error) {
294306
dirVector := c.getStatDirVector()
295-
vecLen := uint32(vectorLen(dirVector))
307+
dirLen := uint32(vectorLen(dirVector))
296308

297-
if index >= vecLen {
298-
return "", fmt.Errorf("stat entry index %d out of range (%d)", index, vecLen)
299-
}
300-
301-
dirEntry := c.getStatDirIndex(dirVector, index)
302-
303-
var name []byte
304-
for n := 0; n < len(dirEntry.name); n++ {
305-
if dirEntry.name[n] == 0 {
306-
name = dirEntry.name[:n]
307-
break
308-
}
309-
}
310-
311-
return string(name), nil
312-
}
309+
debugf("dumping entres for %d indexes", len(indexes))
313310

314-
func (c *StatsClient) dumpEntries(indexes []uint32) (entries []adapter.StatEntry, err error) {
315311
entries = make([]adapter.StatEntry, 0, len(indexes))
316-
317-
dirVector := c.getStatDirVector()
318-
319312
for _, index := range indexes {
313+
if index >= dirLen {
314+
return nil, fmt.Errorf("stat entry index %d out of dir vector length (%d)", index, dirLen)
315+
}
316+
320317
dirEntry := c.getStatDirIndex(dirVector, index)
321318

322319
var name []byte
@@ -326,6 +323,12 @@ func (c *StatsClient) dumpEntries(indexes []uint32) (entries []adapter.StatEntry
326323
break
327324
}
328325
}
326+
327+
if Debug {
328+
debugf(" - %3d. dir: %q type: %v offset: %d union: %d", index, name,
329+
adapter.StatType(dirEntry.directoryType), dirEntry.offsetVector, dirEntry.unionData)
330+
}
331+
329332
if len(name) == 0 {
330333
continue
331334
}

adapter/statsclient/statseg.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ type sharedHeaderBase struct {
1919
statsOffset int64
2020
}
2121

22-
type statSegSharedHeader struct {
22+
type sharedHeaderV0 struct {
23+
sharedHeaderBase
24+
}
25+
26+
type sharedHeader struct {
2327
version uint64
2428
sharedHeaderBase
2529
}
2630

27-
func (h *statSegSharedHeader) legacyVersion() bool {
31+
func (h *sharedHeader) legacyVersion() bool {
2832
// older VPP (<=19.04) did not have version in stat segment header
2933
// we try to provide fallback support by skipping it in header
3034
if h.version > maxVersion && h.inProgress > 1 && h.epoch == 0 {
@@ -33,8 +37,8 @@ func (h *statSegSharedHeader) legacyVersion() bool {
3337
return false
3438
}
3539

36-
func statSegHeader(b []byte) (header statSegSharedHeader) {
37-
h := (*statSegSharedHeader)(unsafe.Pointer(&b[0]))
40+
func loadSharedHeader(b []byte) (header sharedHeader) {
41+
h := (*sharedHeader)(unsafe.Pointer(&b[0]))
3842
header.version = atomic.LoadUint64(&h.version)
3943
header.epoch = atomic.LoadInt64(&h.epoch)
4044
header.inProgress = atomic.LoadInt64(&h.inProgress)
@@ -44,8 +48,8 @@ func statSegHeader(b []byte) (header statSegSharedHeader) {
4448
return
4549
}
4650

47-
func statSegHeaderLegacy(b []byte) (header statSegSharedHeader) {
48-
h := (*sharedHeaderBase)(unsafe.Pointer(&b[0]))
51+
func loadSharedHeaderLegacy(b []byte) (header sharedHeader) {
52+
h := (*sharedHeaderV0)(unsafe.Pointer(&b[0]))
4953
header.version = 0
5054
header.epoch = atomic.LoadInt64(&h.epoch)
5155
header.inProgress = atomic.LoadInt64(&h.inProgress)
@@ -90,7 +94,7 @@ type vecHeader struct {
9094
}
9195

9296
func vectorLen(v unsafe.Pointer) uint64 {
93-
vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uintptr(0))))
97+
vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uint64(0))))
9498
return vec.length
9599
}
96100

0 commit comments

Comments
 (0)