diff options
author | Zach Koopmans <zkoopmans@google.com> | 2021-06-16 14:06:01 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-06-16 14:08:52 -0700 |
commit | 63b4f6e296a8f131ec969a685f0e31663be58385 (patch) | |
tree | 2bf10bff66a654b1bca5d27598a142fc68ca9430 /pkg/errors | |
parent | 47149b7c4275ddd4404d86eddab6feab4f059ed3 (diff) |
[syserror] Refactor linuxerr and error package.
Move Error struct to pkg/errors package for use in multiple places.
Move linuxerr static definitions under pkg/errors/linuxerr.
Add a lookup list for quick lookup of *errors.Error by errno. This is useful
when converting syserror errors and unix.Errno/syscall.Errrno values to
*errors.Error.
Update benchmarks routines to include conversions.
The below benchmarks show *errors.Error usage to be comparable to using
unix.Errno.
BenchmarkAssignUnix
BenchmarkAssignUnix-32 787875022 1.284 ns/op
BenchmarkAssignLinuxerr
BenchmarkAssignLinuxerr-32 1000000000 1.209 ns/op
BenchmarkAssignSyserror
BenchmarkAssignSyserror-32 759269229 1.429 ns/op
BenchmarkCompareUnix
BenchmarkCompareUnix-32 1000000000 1.310 ns/op
BenchmarkCompareLinuxerr
BenchmarkCompareLinuxerr-32 1000000000 1.241 ns/op
BenchmarkCompareSyserror
BenchmarkCompareSyserror-32 147196165 8.248 ns/op
BenchmarkSwitchUnix
BenchmarkSwitchUnix-32 373233556 3.664 ns/op
BenchmarkSwitchLinuxerr
BenchmarkSwitchLinuxerr-32 476323929 3.294 ns/op
BenchmarkSwitchSyserror
BenchmarkSwitchSyserror-32 39293408 29.62 ns/op
BenchmarkReturnUnix
BenchmarkReturnUnix-32 1000000000 0.5042 ns/op
BenchmarkReturnLinuxerr
BenchmarkReturnLinuxerr-32 1000000000 0.8152 ns/op
BenchmarkConvertUnixLinuxerr
BenchmarkConvertUnixLinuxerr-32 739948875 1.547 ns/op
BenchmarkConvertUnixLinuxerrZero
BenchmarkConvertUnixLinuxerrZero-32 977733974 1.489 ns/op
PiperOrigin-RevId: 379806801
Diffstat (limited to 'pkg/errors')
-rw-r--r-- | pkg/errors/BUILD | 10 | ||||
-rw-r--r-- | pkg/errors/errors.go | 40 | ||||
-rw-r--r-- | pkg/errors/linuxerr/BUILD | 25 | ||||
-rw-r--r-- | pkg/errors/linuxerr/linuxerr.go | 326 | ||||
-rw-r--r-- | pkg/errors/linuxerr/linuxerr_test.go | 245 |
5 files changed, 646 insertions, 0 deletions
diff --git a/pkg/errors/BUILD b/pkg/errors/BUILD new file mode 100644 index 000000000..36ea5da0c --- /dev/null +++ b/pkg/errors/BUILD @@ -0,0 +1,10 @@ +load("//tools:defs.bzl", "go_library") + +package(licenses = ["notice"]) + +go_library( + name = "errors", + srcs = ["errors.go"], + visibility = ["//:sandbox"], + deps = ["//pkg/abi/linux/errno"], +) diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go new file mode 100644 index 000000000..7984a770e --- /dev/null +++ b/pkg/errors/errors.go @@ -0,0 +1,40 @@ +// Copyright 2021 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 errors holds the standardized error definition for gVisor. +package errors + +import ( + "gvisor.dev/gvisor/pkg/abi/linux/errno" +) + +// Error represents a syscall errno with a descriptive message. +type Error struct { + errno errno.Errno + message string +} + +// New creates a new *Error. +func New(err errno.Errno, message string) *Error { + return &Error{ + errno: err, + message: message, + } +} + +// Error implements error.Error. +func (e *Error) Error() string { return e.message } + +// Errno returns the underlying errno.Errno value. +func (e *Error) Errno() errno.Errno { return e.errno } diff --git a/pkg/errors/linuxerr/BUILD b/pkg/errors/linuxerr/BUILD new file mode 100644 index 000000000..8afc9688c --- /dev/null +++ b/pkg/errors/linuxerr/BUILD @@ -0,0 +1,25 @@ +load("//tools:defs.bzl", "go_library", "go_test") + +package(licenses = ["notice"]) + +go_library( + name = "linuxerr", + srcs = ["linuxerr.go"], + visibility = ["//visibility:public"], + deps = [ + "//pkg/abi/linux/errno", + "//pkg/errors", + ], +) + +go_test( + name = "linuxerr_test", + srcs = ["linuxerr_test.go"], + deps = [ + ":linuxerr", + "//pkg/abi/linux/errno", + "//pkg/errors", + "//pkg/syserror", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/pkg/errors/linuxerr/linuxerr.go b/pkg/errors/linuxerr/linuxerr.go new file mode 100644 index 000000000..23d9f9f75 --- /dev/null +++ b/pkg/errors/linuxerr/linuxerr.go @@ -0,0 +1,326 @@ +// Copyright 2021 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 linuxerr contains syscall error codes exported as an error interface +// pointers. This allows for fast comparison and return operations comperable +// to unix.Errno constants. +package linuxerr + +import ( + "fmt" + + "gvisor.dev/gvisor/pkg/abi/linux/errno" + "gvisor.dev/gvisor/pkg/errors" +) + +const maxErrno uint32 = errno.EHWPOISON + 1 + +var ( + EPERM = errors.New(errno.EPERM, "operation not permitted") + ENOENT = errors.New(errno.ENOENT, "no such file or directory") + ESRCH = errors.New(errno.ESRCH, "no such process") + EINTR = errors.New(errno.EINTR, "interrupted system call") + EIO = errors.New(errno.EIO, "I/O error") + ENXIO = errors.New(errno.ENXIO, "no such device or address") + E2BIG = errors.New(errno.E2BIG, "argument list too long") + ENOEXEC = errors.New(errno.ENOEXEC, "exec format error") + EBADF = errors.New(errno.EBADF, "bad file number") + ECHILD = errors.New(errno.ECHILD, "no child processes") + EAGAIN = errors.New(errno.EAGAIN, "try again") + ENOMEM = errors.New(errno.ENOMEM, "out of memory") + EACCES = errors.New(errno.EACCES, "permission denied") + EFAULT = errors.New(errno.EFAULT, "bad address") + ENOTBLK = errors.New(errno.ENOTBLK, "block device required") + EBUSY = errors.New(errno.EBUSY, "device or resource busy") + EEXIST = errors.New(errno.EEXIST, "file exists") + EXDEV = errors.New(errno.EXDEV, "cross-device link") + ENODEV = errors.New(errno.ENODEV, "no such device") + ENOTDIR = errors.New(errno.ENOTDIR, "not a directory") + EISDIR = errors.New(errno.EISDIR, "is a directory") + EINVAL = errors.New(errno.EINVAL, "invalid argument") + ENFILE = errors.New(errno.ENFILE, "file table overflow") + EMFILE = errors.New(errno.EMFILE, "too many open files") + ENOTTY = errors.New(errno.ENOTTY, "not a typewriter") + ETXTBSY = errors.New(errno.ETXTBSY, "text file busy") + EFBIG = errors.New(errno.EFBIG, "file too large") + ENOSPC = errors.New(errno.ENOSPC, "no space left on device") + ESPIPE = errors.New(errno.ESPIPE, "illegal seek") + EROFS = errors.New(errno.EROFS, "read-only file system") + EMLINK = errors.New(errno.EMLINK, "too many links") + EPIPE = errors.New(errno.EPIPE, "broken pipe") + EDOM = errors.New(errno.EDOM, "math argument out of domain of func") + ERANGE = errors.New(errno.ERANGE, "math result not representable") + + // Errno values from include/uapi/asm-generic/errno.h. + EDEADLK = errors.New(errno.EDEADLK, "resource deadlock would occur") + ENAMETOOLONG = errors.New(errno.ENAMETOOLONG, "file name too long") + ENOLCK = errors.New(errno.ENOLCK, "no record locks available") + ENOSYS = errors.New(errno.ENOSYS, "invalid system call number") + ENOTEMPTY = errors.New(errno.ENOTEMPTY, "directory not empty") + ELOOP = errors.New(errno.ELOOP, "too many symbolic links encountered") + ENOMSG = errors.New(errno.ENOMSG, "no message of desired type") + EIDRM = errors.New(errno.EIDRM, "identifier removed") + ECHRNG = errors.New(errno.ECHRNG, "channel number out of range") + EL2NSYNC = errors.New(errno.EL2NSYNC, "level 2 not synchronized") + EL3HLT = errors.New(errno.EL3HLT, "level 3 halted") + EL3RST = errors.New(errno.EL3RST, "level 3 reset") + ELNRNG = errors.New(errno.ELNRNG, "link number out of range") + EUNATCH = errors.New(errno.EUNATCH, "protocol driver not attached") + ENOCSI = errors.New(errno.ENOCSI, "no CSI structure available") + EL2HLT = errors.New(errno.EL2HLT, "level 2 halted") + EBADE = errors.New(errno.EBADE, "invalid exchange") + EBADR = errors.New(errno.EBADR, "invalid request descriptor") + EXFULL = errors.New(errno.EXFULL, "exchange full") + ENOANO = errors.New(errno.ENOANO, "no anode") + EBADRQC = errors.New(errno.EBADRQC, "invalid request code") + EBADSLT = errors.New(errno.EBADSLT, "invalid slot") + EBFONT = errors.New(errno.EBFONT, "bad font file format") + ENOSTR = errors.New(errno.ENOSTR, "device not a stream") + ENODATA = errors.New(errno.ENODATA, "no data available") + ETIME = errors.New(errno.ETIME, "timer expired") + ENOSR = errors.New(errno.ENOSR, "out of streams resources") + ENOPKG = errors.New(errno.ENOPKG, "package not installed") + EREMOTE = errors.New(errno.EREMOTE, "object is remote") + ENOLINK = errors.New(errno.ENOLINK, "link has been severed") + EADV = errors.New(errno.EADV, "advertise error") + ESRMNT = errors.New(errno.ESRMNT, "srmount error") + ECOMM = errors.New(errno.ECOMM, "communication error on send") + EPROTO = errors.New(errno.EPROTO, "protocol error") + EMULTIHOP = errors.New(errno.EMULTIHOP, "multihop attempted") + EDOTDOT = errors.New(errno.EDOTDOT, "RFS specific error") + EBADMSG = errors.New(errno.EBADMSG, "not a data message") + EOVERFLOW = errors.New(errno.EOVERFLOW, "value too large for defined data type") + ENOTUNIQ = errors.New(errno.ENOTUNIQ, "name not unique on network") + EBADFD = errors.New(errno.EBADFD, "file descriptor in bad state") + EREMCHG = errors.New(errno.EREMCHG, "remote address changed") + ELIBACC = errors.New(errno.ELIBACC, "can not access a needed shared library") + ELIBBAD = errors.New(errno.ELIBBAD, "accessing a corrupted shared library") + ELIBSCN = errors.New(errno.ELIBSCN, ".lib section in a.out corrupted") + ELIBMAX = errors.New(errno.ELIBMAX, "attempting to link in too many shared libraries") + ELIBEXEC = errors.New(errno.ELIBEXEC, "cannot exec a shared library directly") + EILSEQ = errors.New(errno.EILSEQ, "illegal byte sequence") + ERESTART = errors.New(errno.ERESTART, "interrupted system call should be restarted") + ESTRPIPE = errors.New(errno.ESTRPIPE, "streams pipe error") + EUSERS = errors.New(errno.EUSERS, "too many users") + ENOTSOCK = errors.New(errno.ENOTSOCK, "socket operation on non-socket") + EDESTADDRREQ = errors.New(errno.EDESTADDRREQ, "destination address required") + EMSGSIZE = errors.New(errno.EMSGSIZE, "message too long") + EPROTOTYPE = errors.New(errno.EPROTOTYPE, "protocol wrong type for socket") + ENOPROTOOPT = errors.New(errno.ENOPROTOOPT, "protocol not available") + EPROTONOSUPPORT = errors.New(errno.EPROTONOSUPPORT, "protocol not supported") + ESOCKTNOSUPPORT = errors.New(errno.ESOCKTNOSUPPORT, "socket type not supported") + EOPNOTSUPP = errors.New(errno.EOPNOTSUPP, "operation not supported on transport endpoint") + EPFNOSUPPORT = errors.New(errno.EPFNOSUPPORT, "protocol family not supported") + EAFNOSUPPORT = errors.New(errno.EAFNOSUPPORT, "address family not supported by protocol") + EADDRINUSE = errors.New(errno.EADDRINUSE, "address already in use") + EADDRNOTAVAIL = errors.New(errno.EADDRNOTAVAIL, "cannot assign requested address") + ENETDOWN = errors.New(errno.ENETDOWN, "network is down") + ENETUNREACH = errors.New(errno.ENETUNREACH, "network is unreachable") + ENETRESET = errors.New(errno.ENETRESET, "network dropped connection because of reset") + ECONNABORTED = errors.New(errno.ECONNABORTED, "software caused connection abort") + ECONNRESET = errors.New(errno.ECONNRESET, "connection reset by peer") + ENOBUFS = errors.New(errno.ENOBUFS, "no buffer space available") + EISCONN = errors.New(errno.EISCONN, "transport endpoint is already connected") + ENOTCONN = errors.New(errno.ENOTCONN, "transport endpoint is not connected") + ESHUTDOWN = errors.New(errno.ESHUTDOWN, "cannot send after transport endpoint shutdown") + ETOOMANYREFS = errors.New(errno.ETOOMANYREFS, "too many references: cannot splice") + ETIMEDOUT = errors.New(errno.ETIMEDOUT, "connection timed out") + ECONNREFUSED = errors.New(errno.ECONNREFUSED, "connection refused") + EHOSTDOWN = errors.New(errno.EHOSTDOWN, "host is down") + EHOSTUNREACH = errors.New(errno.EHOSTUNREACH, "no route to host") + EALREADY = errors.New(errno.EALREADY, "operation already in progress") + EINPROGRESS = errors.New(errno.EINPROGRESS, "operation now in progress") + ESTALE = errors.New(errno.ESTALE, "stale file handle") + EUCLEAN = errors.New(errno.EUCLEAN, "structure needs cleaning") + ENOTNAM = errors.New(errno.ENOTNAM, "not a XENIX named type file") + ENAVAIL = errors.New(errno.ENAVAIL, "no XENIX semaphores available") + EISNAM = errors.New(errno.EISNAM, "is a named type file") + EREMOTEIO = errors.New(errno.EREMOTEIO, "remote I/O error") + EDQUOT = errors.New(errno.EDQUOT, "quota exceeded") + ENOMEDIUM = errors.New(errno.ENOMEDIUM, "no medium found") + EMEDIUMTYPE = errors.New(errno.EMEDIUMTYPE, "wrong medium type") + ECANCELED = errors.New(errno.ECANCELED, "operation Canceled") + ENOKEY = errors.New(errno.ENOKEY, "required key not available") + EKEYEXPIRED = errors.New(errno.EKEYEXPIRED, "key has expired") + EKEYREVOKED = errors.New(errno.EKEYREVOKED, "key has been revoked") + EKEYREJECTED = errors.New(errno.EKEYREJECTED, "key was rejected by service") + EOWNERDEAD = errors.New(errno.EOWNERDEAD, "owner died") + ENOTRECOVERABLE = errors.New(errno.ENOTRECOVERABLE, "state not recoverable") + ERFKILL = errors.New(errno.ERFKILL, "operation not possible due to RF-kill") + EHWPOISON = errors.New(errno.EHWPOISON, "memory page has hardware error") + + // Errors equivalent to other errors. + EWOULDBLOCK = EAGAIN + EDEADLOCK = EDEADLK + ENONET = ENOENT +) + +// A nil *errors.Error denotes no error and is placed at the 0 index of +// errorSlice. Thus, any other empty index should not be nil or a valid error. +// This marks that index as an invalid error so any comparison to nil or a +// valid linuxerr fails. +var errNotValidError = errors.New(errno.Errno(maxErrno), "not a valid error") + +// The following errorSlice holds errors by errno for fast translation between +// errnos (especially uint32(sycall.Errno)) and *Error. +var errorSlice = []*errors.Error{ + // Errno values from include/uapi/asm-generic/errno-base.h. + errno.NOERRNO: nil, + errno.EPERM: EPERM, + errno.ENOENT: ENOENT, + errno.ESRCH: ESRCH, + errno.EINTR: EINTR, + errno.EIO: EIO, + errno.ENXIO: ENXIO, + errno.E2BIG: E2BIG, + errno.ENOEXEC: ENOEXEC, + errno.EBADF: EBADF, + errno.ECHILD: ECHILD, + errno.EAGAIN: EAGAIN, + errno.ENOMEM: ENOMEM, + errno.EACCES: EACCES, + errno.EFAULT: EFAULT, + errno.ENOTBLK: ENOTBLK, + errno.EBUSY: EBUSY, + errno.EEXIST: EEXIST, + errno.EXDEV: EXDEV, + errno.ENODEV: ENODEV, + errno.ENOTDIR: ENOTDIR, + errno.EISDIR: EISDIR, + errno.EINVAL: EINVAL, + errno.ENFILE: ENFILE, + errno.EMFILE: EMFILE, + errno.ENOTTY: ENOTTY, + errno.ETXTBSY: ETXTBSY, + errno.EFBIG: EFBIG, + errno.ENOSPC: ENOSPC, + errno.ESPIPE: ESPIPE, + errno.EROFS: EROFS, + errno.EMLINK: EMLINK, + errno.EPIPE: EPIPE, + errno.EDOM: EDOM, + errno.ERANGE: ERANGE, + + // Errno values from include/uapi/asm-generic/errno.h. + errno.EDEADLK: EDEADLK, + errno.ENAMETOOLONG: ENAMETOOLONG, + errno.ENOLCK: ENOLCK, + errno.ENOSYS: ENOSYS, + errno.ENOTEMPTY: ENOTEMPTY, + errno.ELOOP: ELOOP, + errno.ELOOP + 1: errNotValidError, // No valid errno between ELOOP and ENOMSG. + errno.ENOMSG: ENOMSG, + errno.EIDRM: EIDRM, + errno.ECHRNG: ECHRNG, + errno.EL2NSYNC: EL2NSYNC, + errno.EL3HLT: EL3HLT, + errno.EL3RST: EL3RST, + errno.ELNRNG: ELNRNG, + errno.EUNATCH: EUNATCH, + errno.ENOCSI: ENOCSI, + errno.EL2HLT: EL2HLT, + errno.EBADE: EBADE, + errno.EBADR: EBADR, + errno.EXFULL: EXFULL, + errno.ENOANO: ENOANO, + errno.EBADRQC: EBADRQC, + errno.EBADSLT: EBADSLT, + errno.EBADSLT + 1: errNotValidError, // No valid errno between EBADSLT and ENOPKG. + errno.EBFONT: EBFONT, + errno.ENOSTR: ENOSTR, + errno.ENODATA: ENODATA, + errno.ETIME: ETIME, + errno.ENOSR: ENOSR, + errno.ENOSR + 1: errNotValidError, // No valid errno betweeen ENOSR and ENOPKG. + errno.ENOPKG: ENOPKG, + errno.EREMOTE: EREMOTE, + errno.ENOLINK: ENOLINK, + errno.EADV: EADV, + errno.ESRMNT: ESRMNT, + errno.ECOMM: ECOMM, + errno.EPROTO: EPROTO, + errno.EMULTIHOP: EMULTIHOP, + errno.EDOTDOT: EDOTDOT, + errno.EBADMSG: EBADMSG, + errno.EOVERFLOW: EOVERFLOW, + errno.ENOTUNIQ: ENOTUNIQ, + errno.EBADFD: EBADFD, + errno.EREMCHG: EREMCHG, + errno.ELIBACC: ELIBACC, + errno.ELIBBAD: ELIBBAD, + errno.ELIBSCN: ELIBSCN, + errno.ELIBMAX: ELIBMAX, + errno.ELIBEXEC: ELIBEXEC, + errno.EILSEQ: EILSEQ, + errno.ERESTART: ERESTART, + errno.ESTRPIPE: ESTRPIPE, + errno.EUSERS: EUSERS, + errno.ENOTSOCK: ENOTSOCK, + errno.EDESTADDRREQ: EDESTADDRREQ, + errno.EMSGSIZE: EMSGSIZE, + errno.EPROTOTYPE: EPROTOTYPE, + errno.ENOPROTOOPT: ENOPROTOOPT, + errno.EPROTONOSUPPORT: EPROTONOSUPPORT, + errno.ESOCKTNOSUPPORT: ESOCKTNOSUPPORT, + errno.EOPNOTSUPP: EOPNOTSUPP, + errno.EPFNOSUPPORT: EPFNOSUPPORT, + errno.EAFNOSUPPORT: EAFNOSUPPORT, + errno.EADDRINUSE: EADDRINUSE, + errno.EADDRNOTAVAIL: EADDRNOTAVAIL, + errno.ENETDOWN: ENETDOWN, + errno.ENETUNREACH: ENETUNREACH, + errno.ENETRESET: ENETRESET, + errno.ECONNABORTED: ECONNABORTED, + errno.ECONNRESET: ECONNRESET, + errno.ENOBUFS: ENOBUFS, + errno.EISCONN: EISCONN, + errno.ENOTCONN: ENOTCONN, + errno.ESHUTDOWN: ESHUTDOWN, + errno.ETOOMANYREFS: ETOOMANYREFS, + errno.ETIMEDOUT: ETIMEDOUT, + errno.ECONNREFUSED: ECONNREFUSED, + errno.EHOSTDOWN: EHOSTDOWN, + errno.EHOSTUNREACH: EHOSTUNREACH, + errno.EALREADY: EALREADY, + errno.EINPROGRESS: EINPROGRESS, + errno.ESTALE: ESTALE, + errno.EUCLEAN: EUCLEAN, + errno.ENOTNAM: ENOTNAM, + errno.ENAVAIL: ENAVAIL, + errno.EISNAM: EISNAM, + errno.EREMOTEIO: EREMOTEIO, + errno.EDQUOT: EDQUOT, + errno.ENOMEDIUM: ENOMEDIUM, + errno.EMEDIUMTYPE: EMEDIUMTYPE, + errno.ECANCELED: ECANCELED, + errno.ENOKEY: ENOKEY, + errno.EKEYEXPIRED: EKEYEXPIRED, + errno.EKEYREVOKED: EKEYREVOKED, + errno.EKEYREJECTED: EKEYREJECTED, + errno.EOWNERDEAD: EOWNERDEAD, + errno.ENOTRECOVERABLE: ENOTRECOVERABLE, + errno.ERFKILL: ERFKILL, + errno.EHWPOISON: EHWPOISON, +} + +// ErrorFromErrno gets an error from the list and panics if an invalid entry is requested. +func ErrorFromErrno(e errno.Errno) *errors.Error { + err := errorSlice[e] + // Done this way because a single comparison in benchmarks is 2-3 faster + // than something like ( if err == nil && err > 0 ). + if err != errNotValidError { + return err + } + panic(fmt.Sprintf("invalid error requested with errno: %d", e)) +} diff --git a/pkg/errors/linuxerr/linuxerr_test.go b/pkg/errors/linuxerr/linuxerr_test.go new file mode 100644 index 000000000..a81dd9560 --- /dev/null +++ b/pkg/errors/linuxerr/linuxerr_test.go @@ -0,0 +1,245 @@ +// 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 syserror_test + +import ( + "errors" + "syscall" + "testing" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux/errno" + gErrors "gvisor.dev/gvisor/pkg/errors" + "gvisor.dev/gvisor/pkg/errors/linuxerr" + "gvisor.dev/gvisor/pkg/syserror" +) + +var globalError error + +func BenchmarkAssignUnix(b *testing.B) { + for i := b.N; i > 0; i-- { + globalError = unix.EINVAL + } +} + +func BenchmarkAssignLinuxerr(b *testing.B) { + for i := b.N; i > 0; i-- { + globalError = linuxerr.EINVAL + } +} + +func BenchmarkAssignSyserror(b *testing.B) { + for i := b.N; i > 0; i-- { + globalError = syserror.EINVAL + } +} + +func BenchmarkCompareUnix(b *testing.B) { + globalError = unix.EAGAIN + j := 0 + for i := b.N; i > 0; i-- { + if globalError == unix.EINVAL { + j++ + } + } +} + +func BenchmarkCompareLinuxerr(b *testing.B) { + globalError = linuxerr.E2BIG + j := 0 + for i := b.N; i > 0; i-- { + if globalError == linuxerr.EINVAL { + j++ + } + } +} + +func BenchmarkCompareSyserror(b *testing.B) { + globalError = syserror.EAGAIN + j := 0 + for i := b.N; i > 0; i-- { + if globalError == syserror.EINVAL { + j++ + } + } +} + +func BenchmarkSwitchUnix(b *testing.B) { + globalError = unix.EPERM + j := 0 + for i := b.N; i > 0; i-- { + switch globalError { + case unix.EINVAL: + j++ + case unix.EINTR: + j += 2 + case unix.EAGAIN: + j += 3 + } + } +} + +func BenchmarkSwitchLinuxerr(b *testing.B) { + globalError = linuxerr.EPERM + j := 0 + for i := b.N; i > 0; i-- { + switch globalError { + case linuxerr.EINVAL: + j++ + case linuxerr.EINTR: + j += 2 + case linuxerr.EAGAIN: + j += 3 + } + } +} + +func BenchmarkSwitchSyserror(b *testing.B) { + globalError = syserror.EPERM + j := 0 + for i := b.N; i > 0; i-- { + switch globalError { + case syserror.EINVAL: + j++ + case syserror.EINTR: + j += 2 + case syserror.EAGAIN: + j += 3 + } + } +} + +func BenchmarkReturnUnix(b *testing.B) { + var localError error + f := func() error { + return unix.EINVAL + } + for i := b.N; i > 0; i-- { + localError = f() + } + if localError != nil { + return + } +} + +func BenchmarkReturnLinuxerr(b *testing.B) { + var localError error + f := func() error { + return linuxerr.EINVAL + } + for i := b.N; i > 0; i-- { + localError = f() + } + if localError != nil { + return + } +} + +func BenchmarkConvertUnixLinuxerr(b *testing.B) { + var localError error + for i := b.N; i > 0; i-- { + localError = linuxerr.ErrorFromErrno(errno.Errno(unix.EINVAL)) + } + if localError != nil { + return + } +} + +func BenchmarkConvertUnixLinuxerrZero(b *testing.B) { + var localError error + for i := b.N; i > 0; i-- { + localError = linuxerr.ErrorFromErrno(errno.Errno(0)) + } + if localError != nil { + return + } +} + +type translationTestTable struct { + fn string + errIn error + syscallErrorIn unix.Errno + expectedBool bool + expectedTranslation unix.Errno +} + +func TestErrorTranslation(t *testing.T) { + myError := errors.New("My test error") + myError2 := errors.New("Another test error") + testTable := []translationTestTable{ + {"TranslateError", myError, 0, false, 0}, + {"TranslateError", myError2, 0, false, 0}, + {"AddErrorTranslation", myError, unix.EAGAIN, true, 0}, + {"AddErrorTranslation", myError, unix.EAGAIN, false, 0}, + {"AddErrorTranslation", myError, unix.EPERM, false, 0}, + {"TranslateError", myError, 0, true, unix.EAGAIN}, + {"TranslateError", myError2, 0, false, 0}, + {"AddErrorTranslation", myError2, unix.EPERM, true, 0}, + {"AddErrorTranslation", myError2, unix.EPERM, false, 0}, + {"AddErrorTranslation", myError2, unix.EAGAIN, false, 0}, + {"TranslateError", myError, 0, true, unix.EAGAIN}, + {"TranslateError", myError2, 0, true, unix.EPERM}, + } + for _, tt := range testTable { + switch tt.fn { + case "TranslateError": + err, ok := syserror.TranslateError(tt.errIn) + if ok != tt.expectedBool { + t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool) + } else if err != tt.expectedTranslation { + t.Fatalf("%v(%v) (error) => %v expected %v", tt.fn, tt.errIn, err, tt.expectedTranslation) + } + case "AddErrorTranslation": + ok := syserror.AddErrorTranslation(tt.errIn, tt.syscallErrorIn) + if ok != tt.expectedBool { + t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool) + } + default: + t.Fatalf("Unknown function %v", tt.fn) + } + } +} + +func TestSyscallErrnoToErrors(t *testing.T) { + for _, tc := range []struct { + errno syscall.Errno + err *gErrors.Error + }{ + {errno: syscall.EACCES, err: linuxerr.EACCES}, + {errno: syscall.EAGAIN, err: linuxerr.EAGAIN}, + {errno: syscall.EBADF, err: linuxerr.EBADF}, + {errno: syscall.EBUSY, err: linuxerr.EBUSY}, + {errno: syscall.EDOM, err: linuxerr.EDOM}, + {errno: syscall.EEXIST, err: linuxerr.EEXIST}, + {errno: syscall.EFAULT, err: linuxerr.EFAULT}, + {errno: syscall.EFBIG, err: linuxerr.EFBIG}, + {errno: syscall.EINTR, err: linuxerr.EINTR}, + {errno: syscall.EINVAL, err: linuxerr.EINVAL}, + {errno: syscall.EIO, err: linuxerr.EIO}, + {errno: syscall.ENOTDIR, err: linuxerr.ENOTDIR}, + {errno: syscall.ENOTTY, err: linuxerr.ENOTTY}, + {errno: syscall.EPERM, err: linuxerr.EPERM}, + {errno: syscall.EPIPE, err: linuxerr.EPIPE}, + {errno: syscall.ESPIPE, err: linuxerr.ESPIPE}, + {errno: syscall.EWOULDBLOCK, err: linuxerr.EAGAIN}, + } { + t.Run(tc.errno.Error(), func(t *testing.T) { + e := linuxerr.ErrorFromErrno(errno.Errno(tc.errno)) + if e != tc.err { + t.Fatalf("Mismatch errors: want: %+v (%d) got: %+v %d", tc.err, tc.err.Errno(), e, e.Errno()) + } + }) + } +} |