From c36d2ef3733a0619b992f8ddc23b072474b04044 Mon Sep 17 00:00:00 2001 From: Ian Gudger Date: Tue, 9 Oct 2018 15:11:46 -0700 Subject: [PATCH] Add new netstack metrics to the sentry PiperOrigin-RevId: 216431260 Change-Id: Ia6e5c8d506940148d10ff2884cf4440f470e5820 --- pkg/metric/metric.go | 54 +++++++++++++++++++------- pkg/sentry/socket/epsocket/BUILD | 1 + pkg/sentry/socket/epsocket/epsocket.go | 38 ++++++++++++++++++ runsc/boot/loader.go | 7 +++- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go index 0743612f0..763cd6bc2 100644 --- a/pkg/metric/metric.go +++ b/pkg/metric/metric.go @@ -48,9 +48,6 @@ var ( // TODO: Support metric fields. // type Uint64Metric struct { - // metadata describes the metric. It is immutable. - metadata *pb.MetricMetadata - // value is the actual value of the metric. It must be accessed // atomically. value uint64 @@ -101,24 +98,35 @@ func Disable() { } } -// NewUint64Metric creates a new metric with the given name. +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. // -// Metrics must be statically defined (i.e., at startup). NewUint64Metric will -// return an error if called after Initialized. +// 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 NewUint64Metric(name string, sync bool, description string) (*Uint64Metric, error) { +func RegisterCustomUint64Metric(name string, sync bool, description string, value func() uint64) error { if initialized { - return nil, ErrInitializationDone + return ErrInitializationDone } if _, ok := allMetrics.m[name]; ok { - return nil, ErrNameInUse + return ErrNameInUse } - m := &Uint64Metric{ + allMetrics.m[name] = customUint64Metric{ metadata: &pb.MetricMetadata{ Name: name, Description: description, @@ -126,9 +134,25 @@ func NewUint64Metric(name string, sync bool, description string) (*Uint64Metric, Sync: sync, Type: pb.MetricMetadata_UINT64, }, + value: value, } - allMetrics.m[name] = m - return m, nil + 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 @@ -158,13 +182,13 @@ func (m *Uint64Metric) IncrementBy(v uint64) { // metricSet holds named metrics. type metricSet struct { - m map[string]*Uint64Metric + m map[string]customUint64Metric } // makeMetricSet returns a new metricSet. func makeMetricSet() metricSet { return metricSet{ - m: make(map[string]*Uint64Metric), + m: make(map[string]customUint64Metric), } } @@ -172,7 +196,7 @@ func makeMetricSet() metricSet { func (m *metricSet) Values() metricValues { vals := make(metricValues) for k, v := range m.m { - vals[k] = v.Value() + vals[k] = v.value() } return vals } diff --git a/pkg/sentry/socket/epsocket/BUILD b/pkg/sentry/socket/epsocket/BUILD index 49af8db85..7f9ea9edc 100644 --- a/pkg/sentry/socket/epsocket/BUILD +++ b/pkg/sentry/socket/epsocket/BUILD @@ -19,6 +19,7 @@ go_library( "//pkg/abi/linux", "//pkg/binary", "//pkg/log", + "//pkg/metric", "//pkg/sentry/arch", "//pkg/sentry/context", "//pkg/sentry/device", diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 550569b4c..c5da18b0e 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -33,6 +33,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/metric" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" @@ -53,6 +54,43 @@ import ( "gvisor.googlesource.com/gvisor/pkg/waiter" ) +func mustCreateMetric(name, description string) *tcpip.StatCounter { + var cm tcpip.StatCounter + metric.MustRegisterCustomUint64Metric(name, false /* sync */, description, cm.Value) + return &cm +} + +// Metrics contains metrics exported by netstack. +var Metrics = tcpip.Stats{ + UnknownProtocolRcvdPackets: mustCreateMetric("/netstack/unknown_protocol_received_packets", "Number of packets received by netstack that were for an unknown or unsupported protocol."), + MalformedRcvdPackets: mustCreateMetric("/netstack/malformed_received_packets", "Number of packets received by netstack that were deemed malformed."), + DroppedPackets: mustCreateMetric("/netstack/dropped_packets", "Number of packets dropped by netstack due to full queues."), + IP: tcpip.IPStats{ + PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."), + InvalidAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."), + PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."), + PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."), + OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."), + }, + TCP: tcpip.TCPStats{ + ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."), + PassiveConnectionOpenings: mustCreateMetric("/netstack/tcp/passive_connection_openings", "Number of connections opened successfully via Listen."), + FailedConnectionAttempts: mustCreateMetric("/netstack/tcp/failed_connection_attempts", "Number of calls to Connect or Listen (active and passive openings, respectively) that end in an error."), + ValidSegmentsReceived: mustCreateMetric("/netstack/tcp/valid_segments_received", "Number of TCP segments received that the transport layer successfully parsed."), + InvalidSegmentsReceived: mustCreateMetric("/netstack/tcp/invalid_segments_received", "Number of TCP segments received that the transport layer could not parse."), + SegmentsSent: mustCreateMetric("/netstack/tcp/segments_sent", "Number of TCP segments sent."), + ResetsSent: mustCreateMetric("/netstack/tcp/resets_sent", "Number of TCP resets sent."), + ResetsReceived: mustCreateMetric("/netstack/tcp/resets_received", "Number of TCP resets received."), + }, + UDP: tcpip.UDPStats{ + PacketsReceived: mustCreateMetric("/netstack/udp/packets_received", "Number of UDP datagrams received via HandlePacket."), + UnknownPortErrors: mustCreateMetric("/netstack/udp/unknown_port_errors", "Number of incoming UDP datagrams dropped because they did not have a known destination port."), + ReceiveBufferErrors: mustCreateMetric("/netstack/udp/receive_buffer_errors", "Number of incoming UDP datagrams dropped due to the receiving buffer being in an invalid state."), + MalformedPacketsReceived: mustCreateMetric("/netstack/udp/malformed_packets_received", "Number of incoming UDP datagrams dropped due to the UDP header being in a malformed state."), + PacketsSent: mustCreateMetric("/netstack/udp/packets_sent", "Number of UDP datagrams sent via sendUDP."), + }, +} + const sizeOfInt32 int = 4 var errStackType = syserr.New("expected but did not receive an epsocket.Stack", linux.EINVAL) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 5716ef217..1ad6b09f4 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -683,11 +683,14 @@ func newEmptyNetworkStack(conf *Config, clock tcpip.Clock) (inet.Stack, error) { // NetworkNone sets up loopback using netstack. netProtos := []string{ipv4.ProtocolName, ipv6.ProtocolName, arp.ProtocolName} protoNames := []string{tcp.ProtocolName, udp.ProtocolName, ping.ProtocolName4} - s := &epsocket.Stack{stack.New(netProtos, protoNames, stack.Options{Clock: clock})} + s := epsocket.Stack{stack.New(netProtos, protoNames, stack.Options{ + Clock: clock, + Stats: epsocket.Metrics, + })} if err := s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.SACKEnabled(true)); err != nil { return nil, fmt.Errorf("failed to enable SACK: %v", err) } - return s, nil + return &s, nil default: panic(fmt.Sprintf("invalid network configuration: %v", conf.Network))