runsc: Add metric counting calls of unimplemented system calls.

This is useful to determine which syscalls users want but are unimplemented.

PiperOrigin-RevId: 518962607
This commit is contained in:
Etienne Perot
2023-03-23 14:24:56 -07:00
committed by gVisor bot
parent bd30e8ba68
commit 68267dccc8
3 changed files with 46 additions and 0 deletions
+1
View File
@@ -1540,6 +1540,7 @@ func (k *Kernel) EmitUnimplementedEvent(ctx context.Context, sysno uintptr) {
})
t := TaskFromContext(ctx)
IncrementUnimplementedSyscallCounter(sysno)
_, _ = k.unimplementedSyscallEmitter.Emit(&uspb.UnimplementedSyscall{
Tid: int32(t.ThreadID()),
Registers: t.Arch().StateData().Proto(),
+44
View File
@@ -16,12 +16,14 @@ package kernel
import (
"fmt"
"strconv"
"google.golang.org/protobuf/proto"
"gvisor.dev/gvisor/pkg/abi"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/bits"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
@@ -37,6 +39,10 @@ const (
// LINT.IfChange
maxSyscallNum = 2000
// LINT.ThenChange(../seccheck/syscall.go)
// outOfRangeSyscallNumber is used to represent a syscall number that is out of the
// range [0, maxSyscallNum] in monitoring.
outOfRangeSyscallNumber = "-1"
)
// SyscallSupportLevel is a syscall support levels.
@@ -347,6 +353,19 @@ func (s *SyscallTable) MaxSysno() (max uintptr) {
// allSyscallTables contains all known tables.
var allSyscallTables []*SyscallTable
var (
// unimplementedSyscallCounterInit ensures the following fields are only initialized once.
unimplementedSyscallCounterInit sync.Once
// unimplementedSyscallNumbers maps syscall numbers to their string representation.
// Used such that incrementing unimplementedSyscallCounter does not require allocating memory.
unimplementedSyscallNumbers map[uintptr]string
// unimplementedSyscallCounter tracks the number of times each unimplemented syscall has been
// called by the sandboxed application.
unimplementedSyscallCounter *metric.Uint64Metric
)
// SyscallTables returns a read-only slice of registered SyscallTables.
func SyscallTables() []*SyscallTable {
return allSyscallTables
@@ -371,6 +390,17 @@ func RegisterSyscallTable(s *SyscallTable) {
panic(fmt.Sprintf("Duplicate SyscallTable registered for OS %v Arch %v", s.OS, s.Arch))
}
allSyscallTables = append(allSyscallTables, s)
unimplementedSyscallCounterInit.Do(func() {
allowedValues := make([]string, maxSyscallNum+2)
unimplementedSyscallNumbers = make(map[uintptr]string, len(allowedValues))
for i := uintptr(0); i <= maxSyscallNum; i++ {
s := strconv.Itoa(int(i))
allowedValues[i] = s
unimplementedSyscallNumbers[i] = s
}
allowedValues[len(allowedValues)-1] = outOfRangeSyscallNumber
unimplementedSyscallCounter = metric.MustCreateNewUint64Metric("unimplemented_syscalls", true, "Number of times the application tried to call an unimplemented syscall, broken down by syscall number", metric.NewField("sysno", allowedValues))
})
s.Init()
}
@@ -461,3 +491,17 @@ type SyscallInfo struct {
Rval uintptr
Errno int
}
// IncrementUnimplementedSyscallCounter increments the "unimplemented syscall" metric for the given
// syscall number.
// A syscall table must have been initialized prior to calling this function.
// +checkescape:all
//
//go:nosplit
func IncrementUnimplementedSyscallCounter(sysno uintptr) {
s, found := unimplementedSyscallNumbers[sysno]
if !found {
s = outOfRangeSyscallNumber
}
unimplementedSyscallCounter.Increment(s)
}
+1
View File
@@ -78,6 +78,7 @@ func Error(name string, err error, note string, urls []string) kernel.Syscall {
return kernel.Syscall{
Name: name,
Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
kernel.IncrementUnimplementedSyscallCounter(sysno)
return 0, nil, err
},
SupportLevel: kernel.SupportUnimplemented,