Documentation
¶
Overview ¶
Package metrics measures what a run cost: how busy the processors were and what the NIC's own counters say.
It is here because the interesting question about a packet path is not how fast it can go but what it spends to get there. A rate on its own says nothing without the processors that produced it, and the driver's own counters say nothing about what actually reached the wire.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CPUHz ¶
func CPUHz() float64
CPUHz is the processor's nominal speed in hertz, for turning busy time into an estimate of cycles per packet.
It is nominal on purpose. What is wanted is a figure comparable between two runs on the same machine, and a measured frequency would move with turbo and with how busy the machine is, which is exactly what is being compared.
Types ¶
type CPUSample ¶
type CPUSample struct {
When time.Time
// PerCPU is indexed the same way as the CPU list the sampler was given.
PerCPU []CPUTime
// All is the sum over those processors.
All CPUTime
// Process is how much processor time this process itself had used, in
// seconds, across all its threads.
Process float64
}
CPUSample is one reading of the processors a run is using and of the process itself.
func (CPUSample) Cores ¶
Cores is how many whole processors' worth of time was used: 2.0 means two processors were busy for the whole interval.
func (CPUSample) CyclesPerPacket ¶
CyclesPerPacket estimates what each packet cost, given the processor speed in hertz. It is an estimate: without a performance counter to read, the only thing available is time multiplied by a nominal frequency, which ignores turbo and frequency scaling.
type CPUSampler ¶
type CPUSampler struct {
// contains filtered or unexported fields
}
CPUSampler reads processor time for a fixed set of processors.
Which processors matters. A packet path that bypasses the kernel spends its time in this process, and one that goes through the kernel spends much of it in soft interrupt handlers that belong to no process at all. Counting only the process would flatter the second; counting whole processors counts both.
func NewCPUSampler ¶
func NewCPUSampler(cpus []int) *CPUSampler
NewCPUSampler watches the given processors. An empty list watches all of them.
func (*CPUSampler) Sample ¶
func (s *CPUSampler) Sample() (CPUSample, error)
Sample reads the counters now.
type CPUTime ¶
type CPUTime struct {
User float64
System float64
SoftIRQ float64 // where a kernel network stack does its work
IRQ float64
Idle float64
Steal float64
Total float64
}
CPUTime is how a processor spent an interval, in seconds.
func (CPUTime) Utilisation ¶
Utilisation is the fraction of the interval that was not idle.
type ENAAllowances ¶ added in v0.1.11
type ENAAllowances struct {
PPSExceeded uint64
BwInExceeded uint64
BwOutExceeded uint64
ConntrackExceeded uint64
LinklocalExceeded uint64
ConntrackAvailable uint64
// Present is false on anything that is not an ENA, which is every NIC
// outside EC2 and an ENA behind a driver too old to publish them.
Present bool
}
ENAAllowances are the counters EC2's Nitro card publishes to say which of an instance's network allowances it is shaping traffic against. They are the difference between "the rate flattened here" and "the fabric shaped us here, and this is which quota did it", which is the only way to tell an instance's real limit from the generator running out of steam.
Every one is cumulative since the driver last reset, so only deltas mean anything, and a counter that goes backwards means the driver reset underneath the measurement and the window is void.
PPSExceeded is the one the packet-per-second work is about, and AWS documents it as bidirectional and per instance: receive and transmit draw on one budget. Note that it counts packets "queued or dropped", so a nonzero value alone only proves a microburst grazed the shaper -- it takes a shortfall of delivered against offered to show a real limit.
func Allowances ¶ added in v0.1.11
func Allowances(raw map[string]uint64) ENAAllowances
Allowances picks the ENA allowance counters out of any set of named counters: NICCounters.Raw from ethtool, or the driver's own xstats read straight from a device this process owns.
func (ENAAllowances) String ¶ added in v0.1.11
func (a ENAAllowances) String() string
String renders a delta as one line for a periodic report, naming only the allowances that actually fired so that a quiet run stays quiet.
func (ENAAllowances) Sub ¶ added in v0.1.11
func (a ENAAllowances) Sub(o ENAAllowances) ENAAllowances
Sub returns the change between two readings. ConntrackAvailable is a level rather than a running total, so it is carried across as the later reading instead of a difference.
func (ENAAllowances) WentBackwards ¶ added in v0.1.11
func (a ENAAllowances) WentBackwards(o ENAAllowances) bool
WentBackwards reports whether any cumulative counter fell, which means the driver reset mid-measurement and the deltas are meaningless.
type NICCounters ¶
type NICCounters struct {
// TxPackets and TxBytes are what the port put on the wire, and RxPackets
// and RxBytes what it took off.
TxPackets uint64
TxBytes uint64
RxPackets uint64
RxBytes uint64
// TxErrors and TxDiscards are frames the port would not send, and
// RxDiscards frames it could not take. On the receiving side of a fast
// test this is usually where the missing packets are.
TxErrors uint64
TxDiscards uint64
RxDiscards uint64
// RxOutOfBuffer is receive work the NIC had nowhere to put, which means
// the receive queue was not refilled fast enough.
RxOutOfBuffer uint64
// Raw is every counter that was read, for the ones not named above.
Raw map[string]uint64
}
NICCounters are the NIC's own counts of what crossed the wire.
They are the only honest answer to "did the packets actually leave". A driver counts what it handed to the hardware, which is not the same thing: a frame the NIC rejects, or drops for want of buffer, is counted as sent by everyone except the NIC.
func ReadNIC ¶
func ReadNIC(iface string) (NICCounters, error)
ReadNIC reads a port's counters.
The physical counters this wants are not in sysfs, because sysfs reports what the kernel's own driver saw and this packet path does not go through it. They come from ethtool, which asks the hardware.
func (NICCounters) HasPhysicalCounters ¶
func (c NICCounters) HasPhysicalCounters() bool
HasPhysicalCounters reports whether the port exposed the counters that describe the wire rather than the kernel's view of it.
func (NICCounters) Sub ¶
func (c NICCounters) Sub(o NICCounters) NICCounters
Sub is the difference between two readings.