diff options
Diffstat (limited to 'pkg/metric')
-rw-r--r-- | pkg/metric/metric.go | 250 | ||||
-rwxr-xr-x | pkg/metric/metric_go_proto/metric.pb.go | 297 | ||||
-rwxr-xr-x | pkg/metric/metric_state_autogen.go | 4 |
3 files changed, 551 insertions, 0 deletions
diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go new file mode 100644 index 000000000..803709cc4 --- /dev/null +++ b/pkg/metric/metric.go @@ -0,0 +1,250 @@ +// Copyright 2018 The gVisor Authors. +// +// 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 metric provides primitives for collecting metrics. +package metric + +import ( + "errors" + "fmt" + "sync" + "sync/atomic" + + "gvisor.googlesource.com/gvisor/pkg/eventchannel" + "gvisor.googlesource.com/gvisor/pkg/log" + pb "gvisor.googlesource.com/gvisor/pkg/metric/metric_go_proto" +) + +var ( + // ErrNameInUse indicates that another metric is already defined for + // the given name. + ErrNameInUse = errors.New("metric name already in use") + + // ErrInitializationDone indicates that the caller tried to create a + // new metric after initialization. + ErrInitializationDone = errors.New("metric cannot be created after initialization is complete") +) + +// Uint64Metric encapsulates a uint64 that represents some kind of metric to be +// monitored. +// +// All metrics must be cumulative, meaning that their values will only increase +// over time. +// +// Metrics are not saved across save/restore and thus reset to zero on restore. +// +// TODO(b/67298402): Support non-cumulative metrics. +// TODO(b/67298427): Support metric fields. +// +type Uint64Metric struct { + // value is the actual value of the metric. It must be accessed + // atomically. + value uint64 +} + +var ( + // initialized indicates that all metrics are registered. allMetrics is + // immutable once initialized is true. + initialized bool + + // allMetrics are the registered metrics. + allMetrics = makeMetricSet() +) + +// Initialize sends a metric registration event over the event channel. +// +// Precondition: +// * All metrics are registered. +// * Initialize/Disable has not been called. +func Initialize() { + if initialized { + panic("Initialize/Disable called more than once") + } + initialized = true + + m := pb.MetricRegistration{} + for _, v := range allMetrics.m { + m.Metrics = append(m.Metrics, v.metadata) + } + eventchannel.Emit(&m) +} + +// Disable sends an empty metric registration event over the event channel, +// disabling metric collection. +// +// Precondition: +// * All metrics are registered. +// * Initialize/Disable has not been called. +func Disable() { + if initialized { + panic("Initialize/Disable called more than once") + } + initialized = true + + m := pb.MetricRegistration{} + if err := eventchannel.Emit(&m); err != nil { + panic("unable to emit metric disable event: " + err.Error()) + } +} + +type customUint64Metric struct { + // metadata describes the metric. It is immutable. + metadata *pb.MetricMetadata + + // value returns the current value of the metric. + value func() uint64 +} + +// RegisterCustomUint64Metric registers a metric with the given name. +// +// Register must only be called at init and will return and error if called +// after Initialized. +// +// All metrics must be cumulative, meaning that the return values of value must +// only increase over time. +// +// Preconditions: +// * name must be globally unique. +// * Initialize/Disable have not been called. +func RegisterCustomUint64Metric(name string, sync bool, description string, value func() uint64) error { + if initialized { + return ErrInitializationDone + } + + if _, ok := allMetrics.m[name]; ok { + return ErrNameInUse + } + + allMetrics.m[name] = customUint64Metric{ + metadata: &pb.MetricMetadata{ + Name: name, + Description: description, + Cumulative: true, + Sync: sync, + Type: pb.MetricMetadata_UINT64, + }, + value: value, + } + return nil +} + +// MustRegisterCustomUint64Metric calls RegisterCustomUint64Metric and panics +// if it returns an error. +func MustRegisterCustomUint64Metric(name string, sync bool, description string, value func() uint64) { + if err := RegisterCustomUint64Metric(name, sync, description, value); err != nil { + panic(fmt.Sprintf("Unable to register metric %q: %v", name, err)) + } +} + +// NewUint64Metric creates and registers a new metric with the given name. +// +// Metrics must be statically defined (i.e., at init). +func NewUint64Metric(name string, sync bool, description string) (*Uint64Metric, error) { + var m Uint64Metric + return &m, RegisterCustomUint64Metric(name, sync, description, m.Value) +} + +// MustCreateNewUint64Metric calls NewUint64Metric and panics if it returns an +// error. +func MustCreateNewUint64Metric(name string, sync bool, description string) *Uint64Metric { + m, err := NewUint64Metric(name, sync, description) + if err != nil { + panic(fmt.Sprintf("Unable to create metric %q: %v", name, err)) + } + return m +} + +// Value returns the current value of the metric. +func (m *Uint64Metric) Value() uint64 { + return atomic.LoadUint64(&m.value) +} + +// Increment increments the metric by 1. +func (m *Uint64Metric) Increment() { + atomic.AddUint64(&m.value, 1) +} + +// IncrementBy increments the metric by v. +func (m *Uint64Metric) IncrementBy(v uint64) { + atomic.AddUint64(&m.value, v) +} + +// metricSet holds named metrics. +type metricSet struct { + m map[string]customUint64Metric +} + +// makeMetricSet returns a new metricSet. +func makeMetricSet() metricSet { + return metricSet{ + m: make(map[string]customUint64Metric), + } +} + +// Values returns a snapshot of all values in m. +func (m *metricSet) Values() metricValues { + vals := make(metricValues) + for k, v := range m.m { + vals[k] = v.value() + } + return vals +} + +// metricValues contains a copy of the values of all metrics. +type metricValues map[string]uint64 + +var ( + // emitMu protects metricsAtLastEmit and ensures that all emitted + // metrics are strongly ordered (older metrics are never emitted after + // newer metrics). + emitMu sync.Mutex + + // metricsAtLastEmit contains the state of the metrics at the last emit event. + metricsAtLastEmit metricValues +) + +// EmitMetricUpdate emits a MetricUpdate over the event channel. +// +// Only metrics that have changed since the last call are emitted. +// +// EmitMetricUpdate is thread-safe. +// +// Preconditions: +// * Initialize has been called. +func EmitMetricUpdate() { + emitMu.Lock() + defer emitMu.Unlock() + + snapshot := allMetrics.Values() + + m := pb.MetricUpdate{} + for k, v := range snapshot { + // On the first call metricsAtLastEmit will be empty. Include + // all metrics then. + if prev, ok := metricsAtLastEmit[k]; !ok || prev != v { + m.Metrics = append(m.Metrics, &pb.MetricValue{ + Name: k, + Value: &pb.MetricValue_Uint64Value{v}, + }) + } + } + + metricsAtLastEmit = snapshot + if len(m.Metrics) == 0 { + return + } + + log.Debugf("Emitting metrics: %v", m) + eventchannel.Emit(&m) +} diff --git a/pkg/metric/metric_go_proto/metric.pb.go b/pkg/metric/metric_go_proto/metric.pb.go new file mode 100755 index 000000000..553236535 --- /dev/null +++ b/pkg/metric/metric_go_proto/metric.pb.go @@ -0,0 +1,297 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pkg/metric/metric.proto + +package gvisor + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type MetricMetadata_Type int32 + +const ( + MetricMetadata_UINT64 MetricMetadata_Type = 0 +) + +var MetricMetadata_Type_name = map[int32]string{ + 0: "UINT64", +} + +var MetricMetadata_Type_value = map[string]int32{ + "UINT64": 0, +} + +func (x MetricMetadata_Type) String() string { + return proto.EnumName(MetricMetadata_Type_name, int32(x)) +} + +func (MetricMetadata_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_87b8778a4ff2ab5c, []int{0, 0} +} + +type MetricMetadata struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Cumulative bool `protobuf:"varint,3,opt,name=cumulative,proto3" json:"cumulative,omitempty"` + Sync bool `protobuf:"varint,4,opt,name=sync,proto3" json:"sync,omitempty"` + Type MetricMetadata_Type `protobuf:"varint,5,opt,name=type,proto3,enum=gvisor.MetricMetadata_Type" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricMetadata) Reset() { *m = MetricMetadata{} } +func (m *MetricMetadata) String() string { return proto.CompactTextString(m) } +func (*MetricMetadata) ProtoMessage() {} +func (*MetricMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_87b8778a4ff2ab5c, []int{0} +} + +func (m *MetricMetadata) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MetricMetadata.Unmarshal(m, b) +} +func (m *MetricMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MetricMetadata.Marshal(b, m, deterministic) +} +func (m *MetricMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricMetadata.Merge(m, src) +} +func (m *MetricMetadata) XXX_Size() int { + return xxx_messageInfo_MetricMetadata.Size(m) +} +func (m *MetricMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_MetricMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_MetricMetadata proto.InternalMessageInfo + +func (m *MetricMetadata) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MetricMetadata) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *MetricMetadata) GetCumulative() bool { + if m != nil { + return m.Cumulative + } + return false +} + +func (m *MetricMetadata) GetSync() bool { + if m != nil { + return m.Sync + } + return false +} + +func (m *MetricMetadata) GetType() MetricMetadata_Type { + if m != nil { + return m.Type + } + return MetricMetadata_UINT64 +} + +type MetricRegistration struct { + Metrics []*MetricMetadata `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricRegistration) Reset() { *m = MetricRegistration{} } +func (m *MetricRegistration) String() string { return proto.CompactTextString(m) } +func (*MetricRegistration) ProtoMessage() {} +func (*MetricRegistration) Descriptor() ([]byte, []int) { + return fileDescriptor_87b8778a4ff2ab5c, []int{1} +} + +func (m *MetricRegistration) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MetricRegistration.Unmarshal(m, b) +} +func (m *MetricRegistration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MetricRegistration.Marshal(b, m, deterministic) +} +func (m *MetricRegistration) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricRegistration.Merge(m, src) +} +func (m *MetricRegistration) XXX_Size() int { + return xxx_messageInfo_MetricRegistration.Size(m) +} +func (m *MetricRegistration) XXX_DiscardUnknown() { + xxx_messageInfo_MetricRegistration.DiscardUnknown(m) +} + +var xxx_messageInfo_MetricRegistration proto.InternalMessageInfo + +func (m *MetricRegistration) GetMetrics() []*MetricMetadata { + if m != nil { + return m.Metrics + } + return nil +} + +type MetricValue struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Types that are valid to be assigned to Value: + // *MetricValue_Uint64Value + Value isMetricValue_Value `protobuf_oneof:"value"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricValue) Reset() { *m = MetricValue{} } +func (m *MetricValue) String() string { return proto.CompactTextString(m) } +func (*MetricValue) ProtoMessage() {} +func (*MetricValue) Descriptor() ([]byte, []int) { + return fileDescriptor_87b8778a4ff2ab5c, []int{2} +} + +func (m *MetricValue) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MetricValue.Unmarshal(m, b) +} +func (m *MetricValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MetricValue.Marshal(b, m, deterministic) +} +func (m *MetricValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricValue.Merge(m, src) +} +func (m *MetricValue) XXX_Size() int { + return xxx_messageInfo_MetricValue.Size(m) +} +func (m *MetricValue) XXX_DiscardUnknown() { + xxx_messageInfo_MetricValue.DiscardUnknown(m) +} + +var xxx_messageInfo_MetricValue proto.InternalMessageInfo + +func (m *MetricValue) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type isMetricValue_Value interface { + isMetricValue_Value() +} + +type MetricValue_Uint64Value struct { + Uint64Value uint64 `protobuf:"varint,2,opt,name=uint64_value,json=uint64Value,proto3,oneof"` +} + +func (*MetricValue_Uint64Value) isMetricValue_Value() {} + +func (m *MetricValue) GetValue() isMetricValue_Value { + if m != nil { + return m.Value + } + return nil +} + +func (m *MetricValue) GetUint64Value() uint64 { + if x, ok := m.GetValue().(*MetricValue_Uint64Value); ok { + return x.Uint64Value + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*MetricValue) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*MetricValue_Uint64Value)(nil), + } +} + +type MetricUpdate struct { + Metrics []*MetricValue `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricUpdate) Reset() { *m = MetricUpdate{} } +func (m *MetricUpdate) String() string { return proto.CompactTextString(m) } +func (*MetricUpdate) ProtoMessage() {} +func (*MetricUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_87b8778a4ff2ab5c, []int{3} +} + +func (m *MetricUpdate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MetricUpdate.Unmarshal(m, b) +} +func (m *MetricUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MetricUpdate.Marshal(b, m, deterministic) +} +func (m *MetricUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricUpdate.Merge(m, src) +} +func (m *MetricUpdate) XXX_Size() int { + return xxx_messageInfo_MetricUpdate.Size(m) +} +func (m *MetricUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_MetricUpdate.DiscardUnknown(m) +} + +var xxx_messageInfo_MetricUpdate proto.InternalMessageInfo + +func (m *MetricUpdate) GetMetrics() []*MetricValue { + if m != nil { + return m.Metrics + } + return nil +} + +func init() { + proto.RegisterEnum("gvisor.MetricMetadata_Type", MetricMetadata_Type_name, MetricMetadata_Type_value) + proto.RegisterType((*MetricMetadata)(nil), "gvisor.MetricMetadata") + proto.RegisterType((*MetricRegistration)(nil), "gvisor.MetricRegistration") + proto.RegisterType((*MetricValue)(nil), "gvisor.MetricValue") + proto.RegisterType((*MetricUpdate)(nil), "gvisor.MetricUpdate") +} + +func init() { proto.RegisterFile("pkg/metric/metric.proto", fileDescriptor_87b8778a4ff2ab5c) } + +var fileDescriptor_87b8778a4ff2ab5c = []byte{ + // 288 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xc1, 0x4b, 0xc3, 0x30, + 0x14, 0xc6, 0x17, 0xd7, 0x75, 0xfa, 0x3a, 0x86, 0x44, 0xd0, 0x80, 0x20, 0xa5, 0x5e, 0x7a, 0xb1, + 0x93, 0x39, 0x76, 0xf3, 0xe2, 0x41, 0xf4, 0x30, 0x85, 0xb0, 0x79, 0x95, 0xd8, 0x86, 0x12, 0x5c, + 0xdb, 0xd0, 0xa4, 0x85, 0xfe, 0x75, 0xfe, 0x6b, 0xd2, 0x17, 0x95, 0x4d, 0x76, 0xca, 0xcb, 0xfb, + 0xde, 0xf7, 0xf1, 0xcb, 0x0b, 0x5c, 0xe8, 0xcf, 0x7c, 0x56, 0x48, 0x5b, 0xab, 0xf4, 0xe7, 0x48, + 0x74, 0x5d, 0xd9, 0x8a, 0xfa, 0x79, 0xab, 0x4c, 0x55, 0x47, 0x5f, 0x04, 0xa6, 0x2b, 0x14, 0x56, + 0xd2, 0x8a, 0x4c, 0x58, 0x41, 0x29, 0x78, 0xa5, 0x28, 0x24, 0x23, 0x21, 0x89, 0x4f, 0x38, 0xd6, + 0x34, 0x84, 0x20, 0x93, 0x26, 0xad, 0x95, 0xb6, 0xaa, 0x2a, 0xd9, 0x11, 0x4a, 0xbb, 0x2d, 0x7a, + 0x05, 0x90, 0x36, 0x45, 0xb3, 0x15, 0x56, 0xb5, 0x92, 0x0d, 0x43, 0x12, 0x1f, 0xf3, 0x9d, 0x4e, + 0x9f, 0x6a, 0xba, 0x32, 0x65, 0x1e, 0x2a, 0x58, 0xd3, 0x19, 0x78, 0xb6, 0xd3, 0x92, 0x8d, 0x42, + 0x12, 0x4f, 0xe7, 0x97, 0x89, 0x63, 0x4a, 0xf6, 0x79, 0x92, 0x75, 0xa7, 0x25, 0xc7, 0xc1, 0x88, + 0x82, 0xd7, 0xdf, 0x28, 0x80, 0xbf, 0x79, 0x7e, 0x59, 0x2f, 0x17, 0xa7, 0x83, 0xe8, 0x11, 0xa8, + 0x33, 0x70, 0x99, 0x2b, 0x63, 0x6b, 0x81, 0x38, 0xb7, 0x30, 0x76, 0xef, 0x35, 0x8c, 0x84, 0xc3, + 0x38, 0x98, 0x9f, 0x1f, 0x4e, 0xe7, 0xbf, 0x63, 0xd1, 0x2b, 0x04, 0x4e, 0x7a, 0x13, 0xdb, 0x46, + 0x1e, 0xdc, 0xc2, 0x35, 0x4c, 0x1a, 0x55, 0xda, 0xe5, 0xe2, 0xbd, 0xed, 0x67, 0x70, 0x0d, 0xde, + 0xd3, 0x80, 0x07, 0xae, 0x8b, 0xc6, 0x87, 0x31, 0x8c, 0x50, 0x8d, 0xee, 0x61, 0xe2, 0x02, 0x37, + 0x3a, 0x13, 0x56, 0xd2, 0x9b, 0xff, 0x48, 0x67, 0xfb, 0x48, 0x68, 0xff, 0xe3, 0xf9, 0xf0, 0xf1, + 0xa3, 0xee, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x7f, 0xcb, 0x46, 0xc3, 0x01, 0x00, 0x00, +} diff --git a/pkg/metric/metric_state_autogen.go b/pkg/metric/metric_state_autogen.go new file mode 100755 index 000000000..985c28832 --- /dev/null +++ b/pkg/metric/metric_state_autogen.go @@ -0,0 +1,4 @@ +// automatically generated by stateify. + +package metric + |