aboutsummaryrefslogtreecommitdiffstats
path: root/adapter/statsclient/stat_segment_api.go
blob: 2c2950fdb9266ff9ba71c0128fd18c58046086f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//  Copyright (c) 2020 Cisco and/or its affiliates.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at:
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package statsclient

import (
	"fmt"
	"sync/atomic"
	"time"
	"unsafe"

	"go.fd.io/govpp/adapter"
)

var (
	// ErrStatDataLenIncorrect is returned when stat data does not match vector
	// length of a respective data directory
	ErrStatDataLenIncorrect = fmt.Errorf("stat data length incorrect")
)

var (
	MaxWaitInProgress    = time.Millisecond * 100
	CheckDelayInProgress = time.Microsecond * 10
)

const (
	minVersion = 1
	maxVersion = 2
)

var dirTypeMapping = map[dirType]adapter.StatType{
	1: adapter.ScalarIndex,
	2: adapter.SimpleCounterVector,
	3: adapter.CombinedCounterVector,
	4: adapter.NameVector,
	5: adapter.Empty,
	6: adapter.Symlink,
}

var dirTypeMappingLegacy = map[dirType]adapter.StatType{
	1: adapter.ScalarIndex,
	2: adapter.SimpleCounterVector,
	3: adapter.CombinedCounterVector,
	4: adapter.ErrorIndex,
	5: adapter.NameVector,
	6: adapter.Empty,
	7: adapter.Symlink,
}

type (
	dirVector  unsafe.Pointer
	dirSegment unsafe.Pointer
	dirName    []byte
	dirType    int32
)

// statSegment represents common API for every stats API version
type statSegment interface {
	// GetDirectoryVector returns pointer to memory where the beginning
	// of the data directory is located.
	GetDirectoryVector() dirVector

	// GetStatDirOnIndex accepts directory vector and particular index.
	// Returns pointer to the beginning of the segment. Also the directory
	// name as [128]byte and the directory type is returned for easy use
	// without needing to know the exact segment version.
	//
	// Note that if the index is equal to 0, the result pointer points to
	// the same memory address as the argument.
	GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, adapter.StatType)

	// GetEpoch re-loads stats header and returns current epoch
	//and 'inProgress' value
	GetEpoch() (int64, bool)

	// CopyEntryData accepts pointer to a directory segment and returns adapter.Stat
	// based on directory type populated with data. The index is an optional parameter
	// (used by symlinks) returning stats for item on the given index only.
	// Use ^uint32(0) as an empty index (since 0 is a valid value).
	CopyEntryData(segment dirSegment, index uint32) adapter.Stat

	// UpdateEntryData accepts pointer to a directory segment with data, and stat
	// segment to update
	UpdateEntryData(segment dirSegment, s *adapter.Stat) error
}

// vecHeader represents a vector header
type vecHeader struct {
	length     uint64
	vectorData [0]uint8
}

func getVersion(data []byte) uint64 {
	type apiVersion struct {
		value uint64
	}
	header := (*apiVersion)(unsafe.Pointer(&data[0]))
	version := &apiVersion{
		value: atomic.LoadUint64(&header.value),
	}
	debugf("stats API version loaded: %d", version.value)
	return version.value
}

func vectorLen(v dirVector) dirVector {
	vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uint64(0))))
	return dirVector(&vec.length)
}

func getStatType(dirTypeNum dirType, useLegacyMapping bool) (dirTyp adapter.StatType) {
	var exists bool
	if useLegacyMapping {
		dirTyp, exists = dirTypeMappingLegacy[dirTypeNum]
	} else {
		dirTyp, exists = dirTypeMapping[dirTypeNum]
	}
	if exists {
		return dirTyp
	}
	return adapter.Unknown
}

//go:nosplit
func statSegPointer(v dirVector, offset uintptr) dirVector {
	return dirVector(uintptr(v) + offset)
}