summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/runner/defs.bzl12
-rw-r--r--test/runtimes/exclude/nodejs12.4.0.csv41
-rw-r--r--test/runtimes/exclude/python3.7.3.csv1
-rw-r--r--test/runtimes/proctor/main.go28
-rw-r--r--test/syscalls/linux/BUILD1
-rw-r--r--test/syscalls/linux/flock.cc46
-rw-r--r--test/syscalls/linux/mknod.cc16
-rw-r--r--test/syscalls/linux/pipe.cc30
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc51
-rw-r--r--test/syscalls/linux/socket_netlink_route.cc44
-rw-r--r--test/syscalls/linux/socket_netlink_util.cc11
-rw-r--r--test/syscalls/linux/socket_netlink_util.h8
-rw-r--r--test/syscalls/linux/timers.cc94
-rw-r--r--test/util/save_util_linux.cc2
-rw-r--r--test/util/timer_util.cc18
-rw-r--r--test/util/timer_util.h94
16 files changed, 344 insertions, 153 deletions
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index 232fdd4d4..9b5994d59 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -57,13 +57,13 @@ def _syscall_test(
platform,
use_tmpfs,
tags,
+ debug,
network = "none",
file_access = "exclusive",
overlay = False,
add_uds_tree = False,
vfs2 = False,
- fuse = False,
- debug = True):
+ fuse = False):
# Prepend "runsc" to non-native platform names.
full_platform = platform if platform == "native" else "runsc_" + platform
@@ -179,6 +179,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + vfs2_tags,
+ debug = debug,
vfs2 = True,
fuse = fuse,
)
@@ -194,6 +195,7 @@ def syscall_test(
use_tmpfs = False,
add_uds_tree = add_uds_tree,
tags = list(tags),
+ debug = debug,
)
for (platform, platform_tags) in platforms.items():
@@ -205,6 +207,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platform_tags + tags,
+ debug = debug,
)
if add_overlay:
@@ -216,6 +219,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + tags,
+ debug = debug,
overlay = True,
)
@@ -232,6 +236,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + overlay_vfs2_tags,
+ debug = debug,
overlay = True,
vfs2 = True,
)
@@ -246,6 +251,7 @@ def syscall_test(
network = "host",
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + tags,
+ debug = debug,
)
if not use_tmpfs:
@@ -258,6 +264,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + tags,
+ debug = debug,
file_access = "shared",
)
_syscall_test(
@@ -268,6 +275,7 @@ def syscall_test(
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + vfs2_tags,
+ debug = debug,
file_access = "shared",
vfs2 = True,
)
diff --git a/test/runtimes/exclude/nodejs12.4.0.csv b/test/runtimes/exclude/nodejs12.4.0.csv
index ba993814f..c4e7917ec 100644
--- a/test/runtimes/exclude/nodejs12.4.0.csv
+++ b/test/runtimes/exclude/nodejs12.4.0.csv
@@ -1,31 +1,22 @@
test name,bug id,comment
async-hooks/test-statwatcher.js,https://github.com/nodejs/node/issues/21425,Check for fix inclusion in nodejs releases after 2020-03-29
-benchmark/test-benchmark-fs.js,,
-benchmark/test-benchmark-napi.js,,
+benchmark/test-benchmark-fs.js,,Broken test
+benchmark/test-benchmark-napi.js,,Broken test
doctool/test-make-doc.js,b/68848110,Expected to fail.
internet/test-dgram-multicast-set-interface-lo.js,b/162798882,
-internet/test-doctool-versions.js,,
-internet/test-uv-threadpool-schedule.js,,
-parallel/test-cluster-dgram-reuse.js,b/64024294,
+internet/test-doctool-versions.js,,Broken test
+internet/test-uv-threadpool-schedule.js,,Broken test
parallel/test-dgram-bind-fd.js,b/132447356,
parallel/test-dgram-socket-buffer-size.js,b/68847921,
parallel/test-dns-channel-timeout.js,b/161893056,
-parallel/test-fs-access.js,,
-parallel/test-fs-watchfile.js,,Flaky - File already exists error
-parallel/test-fs-write-stream.js,b/166819807,Flaky
-parallel/test-fs-write-stream-double-close.js,b/166819807,Flaky
-parallel/test-fs-write-stream-throw-type-error.js,b/166819807,Flaky
-parallel/test-http-writable-true-after-close.js,,Flaky - Mismatched <anonymous> function calls. Expected exactly 1 actual 2
+parallel/test-fs-access.js,,Broken test
+parallel/test-fs-watchfile.js,b/166819807,Flaky - VFS1 only
+parallel/test-fs-write-stream.js,b/166819807,Flaky - VFS1 only
+parallel/test-fs-write-stream-double-close.js,b/166819807,Flaky - VFS1 only
+parallel/test-fs-write-stream-throw-type-error.js,b/166819807,Flaky - VFS1 only
+parallel/test-http-writable-true-after-close.js,b/171301436,Flaky - Mismatched <anonymous> function calls. Expected exactly 1 actual 2
parallel/test-os.js,b/63997097,
-parallel/test-net-server-listen-options.js,,Flaky - EADDRINUSE
-parallel/test-process-uid-gid.js,,
-parallel/test-tls-cli-min-version-1.0.js,,Flaky - EADDRINUSE
-parallel/test-tls-cli-min-version-1.1.js,,Flaky - EADDRINUSE
-parallel/test-tls-cli-min-version-1.2.js,,Flaky - EADDRINUSE
-parallel/test-tls-cli-min-version-1.3.js,,Flaky - EADDRINUSE
-parallel/test-tls-cli-max-version-1.2.js,,Flaky - EADDRINUSE
-parallel/test-tls-cli-max-version-1.3.js,,Flaky - EADDRINUSE
-parallel/test-tls-min-max-version.js,,Flaky - EADDRINUSE
+parallel/test-process-uid-gid.js,,Does not work inside Docker with gid nobody
pseudo-tty/test-assert-colors.js,b/162801321,
pseudo-tty/test-assert-no-color.js,b/162801321,
pseudo-tty/test-assert-position-indicator.js,b/162801321,
@@ -48,11 +39,7 @@ pseudo-tty/test-tty-stdout-resize.js,b/162801321,
pseudo-tty/test-tty-stream-constructors.js,b/162801321,
pseudo-tty/test-tty-window-size.js,b/162801321,
pseudo-tty/test-tty-wrap.js,b/162801321,
-pummel/test-heapdump-http2.js,,Flaky
-pummel/test-net-pingpong.js,,
+pummel/test-net-pingpong.js,,Broken test
pummel/test-vm-memleak.js,b/162799436,
-pummel/test-watch-file.js,,Flaky - Timeout
-sequential/test-child-process-pass-fd.js,b/63926391,Flaky
-sequential/test-https-connect-localport.js,,Flaky - EADDRINUSE
-sequential/test-net-bytes-per-incoming-chunk-overhead.js,,flaky - timeout
-tick-processor/test-tick-processor-builtin.js,,
+pummel/test-watch-file.js,,Flaky - VFS1 only
+tick-processor/test-tick-processor-builtin.js,,Broken test
diff --git a/test/runtimes/exclude/python3.7.3.csv b/test/runtimes/exclude/python3.7.3.csv
index 8760f8951..911f22855 100644
--- a/test/runtimes/exclude/python3.7.3.csv
+++ b/test/runtimes/exclude/python3.7.3.csv
@@ -18,4 +18,3 @@ test_selectors,b/76116849,OSError not raised with epoll
test_smtplib,b/162980434,unclosed sockets
test_signal,,Flaky - signal: alarm clock
test_socket,b/75983380,
-test_subprocess,b/162980831,
diff --git a/test/runtimes/proctor/main.go b/test/runtimes/proctor/main.go
index e5607ac92..81cb68381 100644
--- a/test/runtimes/proctor/main.go
+++ b/test/runtimes/proctor/main.go
@@ -22,6 +22,7 @@ import (
"log"
"os"
"strings"
+ "syscall"
"gvisor.dev/gvisor/test/runtimes/proctor/lib"
)
@@ -33,6 +34,29 @@ var (
pause = flag.Bool("pause", false, "cause container to pause indefinitely, reaping any zombie children")
)
+// setNumFilesLimit changes the NOFILE soft rlimit if it is too high.
+func setNumFilesLimit() error {
+ // In docker containers, the default value of the NOFILE limit is
+ // 1048576. A few runtime tests (e.g. python:test_subprocess)
+ // enumerates all possible file descriptors and these tests can fail by
+ // timeout if the NOFILE limit is too high. On gVisor, syscalls are
+ // slower so these tests will need even more time to pass.
+ const nofile = 32768
+ rLimit := syscall.Rlimit{}
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ if err != nil {
+ return fmt.Errorf("failed to get RLIMIT_NOFILE: %v", err)
+ }
+ if rLimit.Cur > nofile {
+ rLimit.Cur = nofile
+ err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ if err != nil {
+ return fmt.Errorf("failed to set RLIMIT_NOFILE: %v", err)
+ }
+ }
+ return nil
+}
+
func main() {
flag.Parse()
@@ -74,6 +98,10 @@ func main() {
tests = strings.Split(*testNames, ",")
}
+ if err := setNumFilesLimit(); err != nil {
+ log.Fatalf("%v", err)
+ }
+
// Run tests.
cmds := tr.TestCmds(tests)
for _, cmd := range cmds {
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 36b7f1b97..572f39a5d 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -3624,6 +3624,7 @@ cc_binary(
"//test/util:signal_util",
"//test/util:test_util",
"//test/util:thread_util",
+ "//test/util:timer_util",
],
)
diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc
index 549141cbb..b286e84fe 100644
--- a/test/syscalls/linux/flock.cc
+++ b/test/syscalls/linux/flock.cc
@@ -216,14 +216,29 @@ TEST_F(FlockTest, TestSharedLockFailExclusiveHolderBlocking_NoRandomSave) {
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));
- // Register a signal handler for SIGALRM and set an alarm that will go off
- // while blocking in the subsequent flock() call. This will interrupt flock()
- // and cause it to return EINTR.
+ // Make sure that a blocking flock() call will return EINTR when interrupted
+ // by a signal. Create a timer that will go off while blocking on flock(), and
+ // register the corresponding signal handler.
+ auto timer = ASSERT_NO_ERRNO_AND_VALUE(
+ TimerCreate(CLOCK_MONOTONIC, sigevent_t{
+ .sigev_signo = SIGALRM,
+ .sigev_notify = SIGEV_SIGNAL,
+ }));
+
struct sigaction act = {};
act.sa_handler = trivial_handler;
ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds());
- ASSERT_THAT(ualarm(10000, 0), SyscallSucceeds());
+
+ // Now that the signal handler is registered, set the timer. Set an interval
+ // so that it's ok if the timer goes off before we call flock.
+ ASSERT_NO_ERRNO(
+ timer.Set(0, itimerspec{
+ .it_interval = absl::ToTimespec(absl::Milliseconds(10)),
+ .it_value = absl::ToTimespec(absl::Milliseconds(10)),
+ }));
+
ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallFailsWithErrno(EINTR));
+ timer.reset();
// Unlock
ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
@@ -258,14 +273,29 @@ TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderBlocking_NoRandomSave) {
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));
- // Register a signal handler for SIGALRM and set an alarm that will go off
- // while blocking in the subsequent flock() call. This will interrupt flock()
- // and cause it to return EINTR.
+ // Make sure that a blocking flock() call will return EINTR when interrupted
+ // by a signal. Create a timer that will go off while blocking on flock(), and
+ // register the corresponding signal handler.
+ auto timer = ASSERT_NO_ERRNO_AND_VALUE(
+ TimerCreate(CLOCK_MONOTONIC, sigevent_t{
+ .sigev_signo = SIGALRM,
+ .sigev_notify = SIGEV_SIGNAL,
+ }));
+
struct sigaction act = {};
act.sa_handler = trivial_handler;
ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds());
- ASSERT_THAT(ualarm(10000, 0), SyscallSucceeds());
+
+ // Now that the signal handler is registered, set the timer. Set an interval
+ // so that it's ok if the timer goes off before we call flock.
+ ASSERT_NO_ERRNO(
+ timer.Set(0, itimerspec{
+ .it_interval = absl::ToTimespec(absl::Milliseconds(10)),
+ .it_value = absl::ToTimespec(absl::Milliseconds(10)),
+ }));
+
ASSERT_THAT(flock(fd.get(), LOCK_EX), SyscallFailsWithErrno(EINTR));
+ timer.reset();
// Unlock
ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc
index ae65d366b..b96907b30 100644
--- a/test/syscalls/linux/mknod.cc
+++ b/test/syscalls/linux/mknod.cc
@@ -93,15 +93,15 @@ TEST(MknodTest, MknodOnExistingPathFails) {
}
TEST(MknodTest, UnimplementedTypesReturnError) {
- const std::string path = NewTempAbsPath();
+ // TODO(gvisor.dev/issue/1624): These file types are supported by some
+ // filesystems in VFS2, so this test should be deleted along with VFS1.
+ SKIP_IF(!IsRunningWithVFS1());
- if (IsRunningWithVFS1()) {
- ASSERT_THAT(mknod(path.c_str(), S_IFSOCK, 0),
- SyscallFailsWithErrno(EOPNOTSUPP));
- }
- // These will fail on linux as well since we don't have CAP_MKNOD.
- ASSERT_THAT(mknod(path.c_str(), S_IFCHR, 0), SyscallFailsWithErrno(EPERM));
- ASSERT_THAT(mknod(path.c_str(), S_IFBLK, 0), SyscallFailsWithErrno(EPERM));
+ const std::string path = NewTempAbsPath();
+ EXPECT_THAT(mknod(path.c_str(), S_IFSOCK, 0),
+ SyscallFailsWithErrno(EOPNOTSUPP));
+ EXPECT_THAT(mknod(path.c_str(), S_IFCHR, 0), SyscallFailsWithErrno(EPERM));
+ EXPECT_THAT(mknod(path.c_str(), S_IFBLK, 0), SyscallFailsWithErrno(EPERM));
}
TEST(MknodTest, Socket) {
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index c097c9187..06d9dbf65 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -569,30 +569,38 @@ TEST_P(PipeTest, Streaming) {
// Size() requires 2 syscalls, call it once and remember the value.
const int pipe_size = Size();
+ const size_t streamed_bytes = 4 * pipe_size;
absl::Notification notify;
- ScopedThread t([this, &notify, pipe_size]() {
+ ScopedThread t([&, this]() {
+ std::vector<char> buf(1024);
// Don't start until it's full.
notify.WaitForNotification();
- for (int i = 0; i < pipe_size; i++) {
- int rbuf;
- ASSERT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)),
- SyscallSucceedsWithValue(sizeof(rbuf)));
- EXPECT_EQ(rbuf, i);
+ ssize_t total = 0;
+ while (total < streamed_bytes) {
+ ASSERT_THAT(read(rfd_.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+ total += buf.size();
}
});
// Write 4 bytes * pipe_size. It will fill up the pipe once, notify the reader
// to start. Then we write pipe size worth 3 more times to ensure the reader
// can follow along.
+ //
+ // The size of each write (which is determined by buf.size()) must be smaller
+ // than the size of the pipe (which, in the "smallbuffer" configuration, is 1
+ // page) for the check for notify.Notify() below to be correct.
+ std::vector<char> buf(1024);
+ RandomizeBuffer(buf.data(), buf.size());
ssize_t total = 0;
- for (int i = 0; i < pipe_size; i++) {
- ssize_t written = write(wfd_.get(), &i, sizeof(i));
- ASSERT_THAT(written, SyscallSucceedsWithValue(sizeof(i)));
- total += written;
+ while (total < streamed_bytes) {
+ ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+ total += buf.size();
// Is the next write about to fill up the buffer? Wake up the reader once.
- if (total < pipe_size && (total + written) >= pipe_size) {
+ if (total < pipe_size && (total + buf.size()) >= pipe_size) {
notify.Notify();
}
}
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc
index f4b69c46c..831d96262 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic.cc
@@ -14,6 +14,7 @@
#include "test/syscalls/linux/socket_ip_tcp_generic.h"
+#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
@@ -979,6 +980,56 @@ TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) {
EXPECT_EQ(get, kAbove);
}
+#ifdef __linux__
+TEST_P(TCPSocketPairTest, SpliceFromPipe) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ FileDescriptor rfd(fds[0]);
+ FileDescriptor wfd(fds[1]);
+
+ // Fill with some random data.
+ std::vector<char> buf(kPageSize / 2);
+ RandomizeBuffer(buf.data(), buf.size());
+ ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+
+ EXPECT_THAT(
+ splice(rfd.get(), nullptr, sockets->first_fd(), nullptr, kPageSize, 0),
+ SyscallSucceedsWithValue(buf.size()));
+
+ std::vector<char> rbuf(buf.size());
+ ASSERT_THAT(read(sockets->second_fd(), rbuf.data(), rbuf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+ EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
+}
+
+TEST_P(TCPSocketPairTest, SpliceToPipe) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ FileDescriptor rfd(fds[0]);
+ FileDescriptor wfd(fds[1]);
+
+ // Fill with some random data.
+ std::vector<char> buf(kPageSize / 2);
+ RandomizeBuffer(buf.data(), buf.size());
+ ASSERT_THAT(write(sockets->first_fd(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+ shutdown(sockets->first_fd(), SHUT_WR);
+ EXPECT_THAT(
+ splice(sockets->second_fd(), nullptr, wfd.get(), nullptr, kPageSize, 0),
+ SyscallSucceedsWithValue(buf.size()));
+
+ std::vector<char> rbuf(buf.size());
+ ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
+ SyscallSucceedsWithValue(buf.size()));
+ EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
+}
+#endif // __linux__
+
TEST_P(TCPSocketPairTest, SetTCPWindowClampBelowMinRcvBufConnectedSocket) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Discover minimum receive buf by setting a really low value
diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc
index b3fcf8e7c..241ddad74 100644
--- a/test/syscalls/linux/socket_netlink_route.cc
+++ b/test/syscalls/linux/socket_netlink_route.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <arpa/inet.h>
+#include <fcntl.h>
#include <ifaddrs.h>
#include <linux/if.h>
#include <linux/netlink.h>
@@ -335,6 +336,49 @@ TEST(NetlinkRouteTest, MsgHdrMsgTrunc) {
EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC);
}
+TEST(NetlinkRouteTest, SpliceFromPipe) {
+ Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ FileDescriptor rfd(fds[0]);
+ FileDescriptor wfd(fds[1]);
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifm;
+ };
+
+ struct request req = {};
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_type = RTM_GETLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifm.ifi_family = AF_UNSPEC;
+ req.ifm.ifi_index = loopback_link.index;
+
+ ASSERT_THAT(write(wfd.get(), &req, sizeof(req)),
+ SyscallSucceedsWithValue(sizeof(req)));
+
+ EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0),
+ SyscallSucceedsWithValue(sizeof(req)));
+ close(wfd.release());
+ EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0),
+ SyscallSucceedsWithValue(0));
+
+ bool found = false;
+ ASSERT_NO_ERRNO(NetlinkResponse(
+ fd,
+ [&](const struct nlmsghdr* hdr) {
+ CheckLinkMsg(hdr, loopback_link);
+ found = true;
+ },
+ false));
+ EXPECT_TRUE(found) << "Netlink response does not contain any links.";
+}
+
TEST(NetlinkRouteTest, MsgTruncMsgHdrMsgTrunc) {
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc
index 952eecfe8..bdebea321 100644
--- a/test/syscalls/linux/socket_netlink_util.cc
+++ b/test/syscalls/linux/socket_netlink_util.cc
@@ -67,10 +67,21 @@ PosixError NetlinkRequestResponse(
RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0));
+ return NetlinkResponse(fd, fn, expect_nlmsgerr);
+}
+
+PosixError NetlinkResponse(
+ const FileDescriptor& fd,
+ const std::function<void(const struct nlmsghdr* hdr)>& fn,
+ bool expect_nlmsgerr) {
constexpr size_t kBufferSize = 4096;
std::vector<char> buf(kBufferSize);
+ struct iovec iov = {};
iov.iov_base = buf.data();
iov.iov_len = buf.size();
+ struct msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
// If NLM_F_MULTI is set, response is a series of messages that ends with a
// NLMSG_DONE message.
diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h
index e13ead406..f97276d44 100644
--- a/test/syscalls/linux/socket_netlink_util.h
+++ b/test/syscalls/linux/socket_netlink_util.h
@@ -41,6 +41,14 @@ PosixError NetlinkRequestResponse(
const std::function<void(const struct nlmsghdr* hdr)>& fn,
bool expect_nlmsgerr);
+// Call fn on all response netlink messages.
+//
+// To be used on requests with NLM_F_MULTI reponses.
+PosixError NetlinkResponse(
+ const FileDescriptor& fd,
+ const std::function<void(const struct nlmsghdr* hdr)>& fn,
+ bool expect_nlmsgerr);
+
// Send the passed request and call fn on all response netlink messages.
//
// To be used on requests without NLM_F_MULTI reponses.
diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc
index 4b3c44527..cac94d9e1 100644
--- a/test/syscalls/linux/timers.cc
+++ b/test/syscalls/linux/timers.cc
@@ -33,6 +33,7 @@
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
+#include "test/util/timer_util.h"
ABSL_FLAG(bool, timers_test_sleep, false,
"If true, sleep forever instead of running tests.");
@@ -215,99 +216,6 @@ TEST(TimerTest, ProcessKilledOnCPUHardLimit) {
EXPECT_GE(cpu, kHardLimit);
}
-// RAII type for a kernel "POSIX" interval timer. (The kernel provides system
-// calls such as timer_create that behave very similarly, but not identically,
-// to those described by timer_create(2); in particular, the kernel does not
-// implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on
-// these kernel interval timers.)
-//
-// Compare implementation to FileDescriptor.
-class IntervalTimer {
- public:
- IntervalTimer() = default;
-
- explicit IntervalTimer(int id) { set_id(id); }
-
- IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {}
-
- IntervalTimer& operator=(IntervalTimer&& orig) {
- if (this == &orig) return *this;
- reset(orig.release());
- return *this;
- }
-
- IntervalTimer(const IntervalTimer& other) = delete;
- IntervalTimer& operator=(const IntervalTimer& other) = delete;
-
- ~IntervalTimer() { reset(); }
-
- int get() const { return id_; }
-
- int release() {
- int const id = id_;
- id_ = -1;
- return id;
- }
-
- void reset() { reset(-1); }
-
- void reset(int id) {
- if (id_ >= 0) {
- TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0);
- MaybeSave();
- }
- set_id(id);
- }
-
- PosixErrorOr<struct itimerspec> Set(
- int flags, const struct itimerspec& new_value) const {
- struct itimerspec old_value = {};
- if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) {
- return PosixError(errno, "timer_settime");
- }
- MaybeSave();
- return old_value;
- }
-
- PosixErrorOr<struct itimerspec> Get() const {
- struct itimerspec curr_value = {};
- if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) {
- return PosixError(errno, "timer_gettime");
- }
- MaybeSave();
- return curr_value;
- }
-
- PosixErrorOr<int> Overruns() const {
- int rv = syscall(SYS_timer_getoverrun, id_);
- if (rv < 0) {
- return PosixError(errno, "timer_getoverrun");
- }
- MaybeSave();
- return rv;
- }
-
- private:
- void set_id(int id) { id_ = std::max(id, -1); }
-
- // Kernel timer_t is int; glibc timer_t is void*.
- int id_ = -1;
-};
-
-PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid,
- const struct sigevent& sev) {
- int timerid;
- int ret = syscall(SYS_timer_create, clockid, &sev, &timerid);
- if (ret < 0) {
- return PosixError(errno, "timer_create");
- }
- if (ret > 0) {
- return PosixError(EINVAL, "timer_create should never return positive");
- }
- MaybeSave();
- return IntervalTimer(timerid);
-}
-
// See timerfd.cc:TimerSlack() for rationale.
constexpr absl::Duration kTimerSlack = absl::Milliseconds(500);
diff --git a/test/util/save_util_linux.cc b/test/util/save_util_linux.cc
index d0aea8e6a..fbac94912 100644
--- a/test/util/save_util_linux.cc
+++ b/test/util/save_util_linux.cc
@@ -46,4 +46,4 @@ void MaybeSave() {
} // namespace testing
} // namespace gvisor
-#endif
+#endif // __linux__
diff --git a/test/util/timer_util.cc b/test/util/timer_util.cc
index 43a26b0d3..75cfc4f40 100644
--- a/test/util/timer_util.cc
+++ b/test/util/timer_util.cc
@@ -23,5 +23,23 @@ absl::Time Now(clockid_t id) {
return absl::TimeFromTimespec(now);
}
+#ifdef __linux__
+
+PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid,
+ const struct sigevent& sev) {
+ int timerid;
+ int ret = syscall(SYS_timer_create, clockid, &sev, &timerid);
+ if (ret < 0) {
+ return PosixError(errno, "timer_create");
+ }
+ if (ret > 0) {
+ return PosixError(EINVAL, "timer_create should never return positive");
+ }
+ MaybeSave();
+ return IntervalTimer(timerid);
+}
+
+#endif // __linux__
+
} // namespace testing
} // namespace gvisor
diff --git a/test/util/timer_util.h b/test/util/timer_util.h
index 31aea4fc6..926e6632f 100644
--- a/test/util/timer_util.h
+++ b/test/util/timer_util.h
@@ -16,6 +16,9 @@
#define GVISOR_TEST_UTIL_TIMER_UTIL_H_
#include <errno.h>
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
#include <sys/time.h>
#include <functional>
@@ -30,6 +33,9 @@
namespace gvisor {
namespace testing {
+// Returns the current time.
+absl::Time Now(clockid_t id);
+
// MonotonicTimer is a simple timer that uses a monotonic clock.
class MonotonicTimer {
public:
@@ -65,8 +71,92 @@ inline PosixErrorOr<Cleanup> ScopedItimer(int which,
}));
}
-// Returns the current time.
-absl::Time Now(clockid_t id);
+#ifdef __linux__
+
+// RAII type for a kernel "POSIX" interval timer. (The kernel provides system
+// calls such as timer_create that behave very similarly, but not identically,
+// to those described by timer_create(2); in particular, the kernel does not
+// implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on
+// these kernel interval timers.)
+//
+// Compare implementation to FileDescriptor.
+class IntervalTimer {
+ public:
+ IntervalTimer() = default;
+
+ explicit IntervalTimer(int id) { set_id(id); }
+
+ IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {}
+
+ IntervalTimer& operator=(IntervalTimer&& orig) {
+ if (this == &orig) return *this;
+ reset(orig.release());
+ return *this;
+ }
+
+ IntervalTimer(const IntervalTimer& other) = delete;
+ IntervalTimer& operator=(const IntervalTimer& other) = delete;
+
+ ~IntervalTimer() { reset(); }
+
+ int get() const { return id_; }
+
+ int release() {
+ int const id = id_;
+ id_ = -1;
+ return id;
+ }
+
+ void reset() { reset(-1); }
+
+ void reset(int id) {
+ if (id_ >= 0) {
+ TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0);
+ MaybeSave();
+ }
+ set_id(id);
+ }
+
+ PosixErrorOr<struct itimerspec> Set(
+ int flags, const struct itimerspec& new_value) const {
+ struct itimerspec old_value = {};
+ if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) {
+ return PosixError(errno, "timer_settime");
+ }
+ MaybeSave();
+ return old_value;
+ }
+
+ PosixErrorOr<struct itimerspec> Get() const {
+ struct itimerspec curr_value = {};
+ if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) {
+ return PosixError(errno, "timer_gettime");
+ }
+ MaybeSave();
+ return curr_value;
+ }
+
+ PosixErrorOr<int> Overruns() const {
+ int rv = syscall(SYS_timer_getoverrun, id_);
+ if (rv < 0) {
+ return PosixError(errno, "timer_getoverrun");
+ }
+ MaybeSave();
+ return rv;
+ }
+
+ private:
+ void set_id(int id) { id_ = std::max(id, -1); }
+
+ // Kernel timer_t is int; glibc timer_t is void*.
+ int id_ = -1;
+};
+
+// A wrapper around timer_create(2).
+PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid,
+ const struct sigevent& sev);
+
+#endif // __linux__
} // namespace testing
} // namespace gvisor