Add KVM specific metrics.

This change adds counter and timer metrics useful for analyzing the KVM
platform.

PiperOrigin-RevId: 447043888
This commit is contained in:
Konstantin Bogomolov
2022-05-06 12:21:49 -07:00
committed by gVisor bot
parent 368a4fe8b3
commit 7574e4f642
10 changed files with 72 additions and 3 deletions
+4 -3
View File
@@ -417,7 +417,7 @@ func MustCreateNewUint64NanosecondsMetric(name string, sync bool, description st
// This must be called with the correct number of field values or it will panic.
//go:nosplit
func (m *Uint64Metric) Value(fieldValues ...string) uint64 {
key := m.fieldMapper.lookup(fieldValues...)
key := m.fieldMapper.lookupConcat(fieldValues, nil)
return m.fields[key].Load()
}
@@ -425,14 +425,15 @@ func (m *Uint64Metric) Value(fieldValues ...string) uint64 {
// This must be called with the correct number of field values or it will panic.
//go:nosplit
func (m *Uint64Metric) Increment(fieldValues ...string) {
m.IncrementBy(1, fieldValues...)
key := m.fieldMapper.lookupConcat(fieldValues, nil)
m.fields[key].Add(1)
}
// IncrementBy increments the metric by v.
// This must be called with the correct number of field values or it will panic.
//go:nosplit
func (m *Uint64Metric) IncrementBy(v uint64, fieldValues ...string) {
key := m.fieldMapper.lookup(fieldValues...)
key := m.fieldMapper.lookupConcat(fieldValues, nil)
m.fields[key].Add(v)
}
+14
View File
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -595,6 +596,19 @@ type Task struct {
userCounters *userCounters
}
// Task related metrics
var (
// syscallCounter is a metric that tracks how many syscalls the sentry has
// executed.
syscallCounter = metric.MustCreateNewUint64Metric(
"/task/syscalls", false, "The number of syscalls the sentry has executed for the user.")
// faultCounter is a metric that tracks how many faults the sentry has had to
// handle.
faultCounter = metric.MustCreateNewUint64Metric(
"/task/faults", false, "The number of faults the sentry has handled.")
)
func (t *Task) savePtraceTracer() *Task {
return t.ptraceTracer.Load().(*Task)
}
+2
View File
@@ -261,6 +261,8 @@ func (app *runApp) execute(t *Task) taskRunState {
// an application-generated signal and we should continue execution
// normally.
if at.Any() {
faultCounter.Increment()
region := trace.StartRegion(t.traceContext, faultRegion)
addr := hostarch.Addr(info.Addr())
err := t.MemoryManager().HandleUserFault(t, addr, at, hostarch.Addr(t.Arch().Stack()))
+1
View File
@@ -253,6 +253,7 @@ func (t *Task) doSyscall() taskRunState {
}
}
syscallCounter.Increment()
return t.doSyscallEnter(sysno, args)
}
+1
View File
@@ -92,6 +92,7 @@ go_library(
"//pkg/cpuid",
"//pkg/hostarch",
"//pkg/log",
"//pkg/metric",
"//pkg/procid",
"//pkg/ring0",
"//pkg/ring0/pagetables",
@@ -16,9 +16,11 @@ package kvm
// invalidate is the implementation for Invalidate.
func (as *addressSpace) invalidate() {
timer := asInvalidateDuration.Start()
as.dirtySet.forEach(as.machine, func(c *vCPU) {
if c.active.get() == as { // If this happens to be active,
c.BounceToKernel() // ... force a kernel transition.
}
})
timer.Finish()
}
@@ -110,10 +110,12 @@ func bluepillHandler(context unsafe.Pointer) {
}
for {
hostExitCounter.Increment()
_, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(c.fd), _KVM_RUN, 0) // escapes: no.
switch errno {
case 0: // Expected case.
case unix.EINTR:
interruptCounter.Increment()
// First, we process whatever pending signal
// interrupted KVM. Since we're in a signal handler
// currently, all signals are masked and the signal
+1
View File
@@ -85,6 +85,7 @@ restart:
// Increment the number of user exits.
cpu.userExits.Add(1)
userExitCounter.Increment()
// Release resources.
c.machine.Put(cpu)
+43
View File
@@ -19,12 +19,14 @@ import (
"runtime"
gosync "sync"
"sync/atomic"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/procid"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
@@ -101,6 +103,40 @@ const (
vCPUWaiter uint32 = 1 << 2
)
var (
// hostExitCounter is a metric that tracks how many times the sentry
// performed a host to guest world switch.
hostExitCounter = metric.MustCreateNewUint64Metric(
"/kvm/host_exits", false, "The number of times the sentry performed a host to guest world switch.")
// userExitCounter is a metric that tracks how many times the sentry has
// had an exit from userspace. Analogous to vCPU.userExits.
userExitCounter = metric.MustCreateNewUint64Metric(
"/kvm/user_exits", false, "The number of times the sentry has had an exit from userspace.")
// interruptCounter is a metric that tracks how many times execution returned
// to the KVM host to handle a pending signal.
interruptCounter = metric.MustCreateNewUint64Metric(
"/kvm/interrupts", false, "The number of times the signal handler was invoked.")
// mmapCallCounter is a metric that tracks how many times the function
// seccompMmapSyscall has been called.
mmapCallCounter = metric.MustCreateNewUint64Metric(
"/kvm/mmap_calls", false, "The number of times seccompMmapSyscall has been called.")
// getVCPUFastPathDuration are durations of acquiring a VCPU
// (using machine.Get()).
getVCPUDuration = metric.MustRegisterTimerMetric("/kvm/get_vcpu",
metric.NewExponentialBucketer(20, uint64(time.Nanosecond*10), 1, 2),
"Duration of acquiring a VCPU, not including the fastest reuse path.",
metric.NewField("acquisition_type", []string{"fast_reused", "reused", "unused", "stolen"}))
// asInvalidateDuration are durations of calling addressSpace.invalidate().
asInvalidateDuration = metric.MustRegisterTimerMetric("/kvm/address_space_invalidate",
metric.NewExponentialBucketer(15, uint64(time.Nanosecond*100), 1, 2),
"Duration of calling addressSpace.invalidate().")
)
// vCPU is a single KVM vCPU.
type vCPU struct {
// CPU is the kernel CPU data.
@@ -405,6 +441,8 @@ func (m *machine) Destroy() {
// the corrent context in guest, the vCPU of it must be the same as what
// Get() returns.
func (m *machine) Get() *vCPU {
timer := getVCPUDuration.Start()
m.mu.RLock()
runtime.LockOSThread()
tid := procid.Current()
@@ -413,6 +451,7 @@ func (m *machine) Get() *vCPU {
if c := m.vCPUsByTID[tid]; c != nil {
c.lock()
m.mu.RUnlock()
timer.Finish("fast_reused")
return c
}
@@ -432,6 +471,7 @@ func (m *machine) Get() *vCPU {
if c := m.vCPUsByTID[tid]; c != nil {
c.lock()
m.mu.Unlock()
timer.Finish("reused")
return c
}
@@ -443,6 +483,7 @@ func (m *machine) Get() *vCPU {
m.vCPUsByTID[tid] = c
m.mu.Unlock()
c.loadSegments(tid)
timer.Finish("unused")
return c
}
}
@@ -455,6 +496,7 @@ func (m *machine) Get() *vCPU {
m.vCPUsByTID[tid] = c
m.mu.Unlock()
c.loadSegments(tid)
timer.Finish("unused")
return c
}
@@ -481,6 +523,7 @@ func (m *machine) Get() *vCPU {
m.vCPUsByTID[tid] = c
m.mu.Unlock()
c.loadSegments(tid)
timer.Finish("stolen")
return c
}
@@ -202,6 +202,8 @@ func seccompMmapSync() {
//
//go:nosplit
func seccompMmapHandler(context unsafe.Pointer) {
mmapCallCounter.Increment()
addr, length, errno := seccompMmapSyscall(context)
if errno != 0 {
return