summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/itimer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/itimer.cc')
-rw-r--r--test/syscalls/linux/itimer.cc366
1 files changed, 0 insertions, 366 deletions
diff --git a/test/syscalls/linux/itimer.cc b/test/syscalls/linux/itimer.cc
deleted file mode 100644
index ac113e6da..000000000
--- a/test/syscalls/linux/itimer.cc
+++ /dev/null
@@ -1,366 +0,0 @@
-// 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.
-
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <atomic>
-#include <functional>
-#include <iostream>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/string_view.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/logging.h"
-#include "test/util/multiprocess_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/signal_util.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-#include "test/util/timer_util.h"
-
-namespace gvisor {
-namespace testing {
-namespace {
-
-constexpr char kSIGALRMToMainThread[] = "--itimer_sigarlm_to_main_thread";
-constexpr char kSIGPROFFairnessActive[] = "--itimer_sigprof_fairness_active";
-constexpr char kSIGPROFFairnessIdle[] = "--itimer_sigprof_fairness_idle";
-
-// Time period to be set for the itimers.
-constexpr absl::Duration kPeriod = absl::Milliseconds(25);
-// Total amount of time to spend per thread.
-constexpr absl::Duration kTestDuration = absl::Seconds(20);
-// Amount of spin iterations to perform as the minimum work item per thread.
-// Chosen to be sub-millisecond range.
-constexpr int kIterations = 10000000;
-// Allow deviation in the number of samples.
-constexpr double kNumSamplesDeviationRatio = 0.2;
-
-TEST(ItimerTest, ItimervalUpdatedBeforeExpiration) {
- constexpr int kSleepSecs = 10;
- constexpr int kAlarmSecs = 15;
- static_assert(
- kSleepSecs < kAlarmSecs,
- "kSleepSecs must be less than kAlarmSecs for the test to be meaningful");
- constexpr int kMaxRemainingSecs = kAlarmSecs - kSleepSecs;
-
- // Install a no-op handler for SIGALRM.
- struct sigaction sa = {};
- sigfillset(&sa.sa_mask);
- sa.sa_handler = +[](int signo) {};
- auto const cleanup_sa =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
-
- // Set an itimer-based alarm for kAlarmSecs from now.
- struct itimerval itv = {};
- itv.it_value.tv_sec = kAlarmSecs;
- auto const cleanup_itimer =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv));
-
- // After sleeping for kSleepSecs, the itimer value should reflect the elapsed
- // time even if it hasn't expired.
- absl::SleepFor(absl::Seconds(kSleepSecs));
- ASSERT_THAT(getitimer(ITIMER_REAL, &itv), SyscallSucceeds());
- EXPECT_TRUE(
- itv.it_value.tv_sec < kMaxRemainingSecs ||
- (itv.it_value.tv_sec == kMaxRemainingSecs && itv.it_value.tv_usec == 0))
- << "Remaining time: " << itv.it_value.tv_sec << " seconds + "
- << itv.it_value.tv_usec << " microseconds";
-}
-
-ABSL_CONST_INIT static thread_local std::atomic_int signal_test_num_samples =
- ATOMIC_VAR_INIT(0);
-
-void SignalTestSignalHandler(int /*signum*/) { signal_test_num_samples++; }
-
-struct SignalTestResult {
- int expected_total;
- int main_thread_samples;
- std::vector<int> worker_samples;
-};
-
-std::ostream& operator<<(std::ostream& os, const SignalTestResult& r) {
- os << "{expected_total: " << r.expected_total
- << ", main_thread_samples: " << r.main_thread_samples
- << ", worker_samples: [";
- bool first = true;
- for (int sample : r.worker_samples) {
- if (!first) {
- os << ", ";
- }
- os << sample;
- first = false;
- }
- os << "]}";
- return os;
-}
-
-// Starts two worker threads and itimer id and measures the number of signal
-// delivered to each thread.
-SignalTestResult ItimerSignalTest(int id, clock_t main_clock,
- clock_t worker_clock, int signal,
- absl::Duration sleep) {
- signal_test_num_samples = 0;
-
- struct sigaction sa = {};
- sa.sa_handler = &SignalTestSignalHandler;
- sa.sa_flags = SA_RESTART;
- sigemptyset(&sa.sa_mask);
- auto sigaction_cleanup = ScopedSigaction(signal, sa).ValueOrDie();
-
- int socketfds[2];
- TEST_PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, socketfds) == 0);
-
- // Do the spinning in the workers.
- std::function<void*(int)> work = [&](int socket_fd) {
- FileDescriptor fd(socket_fd);
-
- absl::Time finish = Now(worker_clock) + kTestDuration;
- while (Now(worker_clock) < finish) {
- // Blocked on read.
- char c;
- RetryEINTR(read)(fd.get(), &c, 1);
- for (int i = 0; i < kIterations; i++) {
- // Ensure compiler won't optimize this loop away.
- asm("");
- }
-
- if (sleep != absl::ZeroDuration()) {
- // Sleep so that the entire process is idle for a while.
- absl::SleepFor(sleep);
- }
-
- // Unblock the other thread.
- RetryEINTR(write)(fd.get(), &c, 1);
- }
-
- return reinterpret_cast<void*>(signal_test_num_samples.load());
- };
-
- ScopedThread th1(
- static_cast<std::function<void*()>>(std::bind(work, socketfds[0])));
- ScopedThread th2(
- static_cast<std::function<void*()>>(std::bind(work, socketfds[1])));
-
- absl::Time start = Now(main_clock);
- // Start the timer.
- struct itimerval timer = {};
- timer.it_value = absl::ToTimeval(kPeriod);
- timer.it_interval = absl::ToTimeval(kPeriod);
- auto cleanup_itimer = ScopedItimer(id, timer).ValueOrDie();
-
- // Unblock th1.
- //
- // N.B. th2 owns socketfds[1] but can't close it until it unblocks.
- char c = 0;
- TEST_CHECK(write(socketfds[1], &c, 1) == 1);
-
- SignalTestResult result;
-
- // Wait for the workers to be done and collect their sample counts.
- result.worker_samples.push_back(reinterpret_cast<int64_t>(th1.Join()));
- result.worker_samples.push_back(reinterpret_cast<int64_t>(th2.Join()));
- cleanup_itimer.Release()();
- result.expected_total = (Now(main_clock) - start) / kPeriod;
- result.main_thread_samples = signal_test_num_samples.load();
-
- return result;
-}
-
-int TestSIGALRMToMainThread() {
- SignalTestResult result =
- ItimerSignalTest(ITIMER_REAL, CLOCK_REALTIME, CLOCK_REALTIME, SIGALRM,
- absl::ZeroDuration());
-
- std::cerr << "result: " << result << std::endl;
-
- // ITIMER_REAL-generated SIGALRMs prefer to deliver to the thread group leader
- // (but don't guarantee it), so we expect to see most samples on the main
- // thread.
- //
- // The number of SIGALRMs delivered to a worker should not exceed 20%
- // of the number of total signals expected (this is somewhat arbitrary).
- const int worker_threshold = result.expected_total / 5;
-
- //
- // Linux only guarantees timers will never expire before the requested time.
- // Thus, we only check the upper bound and also it at least have one sample.
- TEST_CHECK(result.main_thread_samples <= result.expected_total);
- TEST_CHECK(result.main_thread_samples > 0);
- for (int num : result.worker_samples) {
- TEST_CHECK_MSG(num <= worker_threshold, "worker received too many samples");
- }
-
- return 0;
-}
-
-// Random save/restore is disabled as it introduces additional latency and
-// unpredictable distribution patterns.
-TEST(ItimerTest, DeliversSIGALRMToMainThread) {
- pid_t child;
- int execve_errno;
- auto kill = ASSERT_NO_ERRNO_AND_VALUE(
- ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGALRMToMainThread},
- {}, &child, &execve_errno));
- EXPECT_EQ(0, execve_errno);
-
- int status;
- EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0),
- SyscallSucceedsWithValue(child));
-
- // Not required anymore.
- kill.Release();
-
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
-}
-
-// Signals are delivered to threads fairly.
-//
-// sleep indicates how long to sleep worker threads each iteration to make the
-// entire process idle.
-int TestSIGPROFFairness(absl::Duration sleep) {
- SignalTestResult result =
- ItimerSignalTest(ITIMER_PROF, CLOCK_PROCESS_CPUTIME_ID,
- CLOCK_THREAD_CPUTIME_ID, SIGPROF, sleep);
-
- std::cerr << "result: " << result << std::endl;
-
- // The number of samples on the main thread should be very low as it did
- // nothing.
- TEST_CHECK(result.main_thread_samples < 80);
-
- // Both workers should get roughly equal number of samples.
- TEST_CHECK(result.worker_samples.size() == 2);
-
- TEST_CHECK(result.expected_total > 0);
-
- // In an ideal world each thread would get exactly 50% of the signals,
- // but since that's unlikely to happen we allow for them to get no less than
- // kNumSamplesDeviationRatio of the total observed samples.
- TEST_CHECK_MSG(std::abs(result.worker_samples[0] - result.worker_samples[1]) <
- ((result.worker_samples[0] + result.worker_samples[1]) *
- kNumSamplesDeviationRatio),
- "one worker received disproportionate share of samples");
-
- return 0;
-}
-
-// Random save/restore is disabled as it introduces additional latency and
-// unpredictable distribution patterns.
-TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyActive) {
- // On the KVM and ptrace platforms, switches between sentry and application
- // context are sometimes extremely slow, causing the itimer to send SIGPROF to
- // a thread that either already has one pending or has had SIGPROF delivered,
- // but hasn't handled it yet (and thus therefore still has SIGPROF masked). In
- // either case, since itimer signals are group-directed, signal sending falls
- // back to notifying the thread group leader. ItimerSignalTest() fails if "too
- // many" signals are delivered to the thread group leader, so these tests are
- // flaky on these platforms.
- //
- // TODO(b/143247272): Clarify why context switches are so slow on KVM.
- const auto gvisor_platform = GvisorPlatform();
- SKIP_IF(gvisor_platform == Platform::kKVM ||
- gvisor_platform == Platform::kPtrace);
-
- pid_t child;
- int execve_errno;
- auto kill = ASSERT_NO_ERRNO_AND_VALUE(
- ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessActive},
- {}, &child, &execve_errno));
- EXPECT_EQ(0, execve_errno);
-
- int status;
- EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0),
- SyscallSucceedsWithValue(child));
-
- // Not required anymore.
- kill.Release();
-
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << "Exited with code: " << status;
-}
-
-// Random save/restore is disabled as it introduces additional latency and
-// unpredictable distribution patterns.
-TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyIdle) {
- // See comment in DeliversSIGPROFToThreadsRoughlyFairlyActive.
- const auto gvisor_platform = GvisorPlatform();
- SKIP_IF(gvisor_platform == Platform::kKVM ||
- gvisor_platform == Platform::kPtrace);
-
- pid_t child;
- int execve_errno;
- auto kill = ASSERT_NO_ERRNO_AND_VALUE(
- ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessIdle},
- {}, &child, &execve_errno));
- EXPECT_EQ(0, execve_errno);
-
- int status;
- EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0),
- SyscallSucceedsWithValue(child));
-
- // Not required anymore.
- kill.Release();
-
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << "Exited with code: " << status;
-}
-
-} // namespace
-} // namespace testing
-} // namespace gvisor
-
-namespace {
-void MaskSIGPIPE() {
- // Always mask SIGPIPE as it's common and tests aren't expected to handle it.
- // We don't take the TestInit() path so we must do this manually.
- struct sigaction sa = {};
- sa.sa_handler = SIG_IGN;
- TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0);
-}
-} // namespace
-
-int main(int argc, char** argv) {
- // These tests require no background threads, so check for them before
- // TestInit.
- for (int i = 0; i < argc; i++) {
- absl::string_view arg(argv[i]);
-
- if (arg == gvisor::testing::kSIGALRMToMainThread) {
- MaskSIGPIPE();
- return gvisor::testing::TestSIGALRMToMainThread();
- }
- if (arg == gvisor::testing::kSIGPROFFairnessActive) {
- MaskSIGPIPE();
- return gvisor::testing::TestSIGPROFFairness(absl::ZeroDuration());
- }
- if (arg == gvisor::testing::kSIGPROFFairnessIdle) {
- MaskSIGPIPE();
- // Sleep time > ClockTick (10ms) exercises sleeping gVisor's
- // kernel.cpuClockTicker.
- return gvisor::testing::TestSIGPROFFairness(absl::Milliseconds(25));
- }
- }
-
- gvisor::testing::TestInit(&argc, &argv);
- return gvisor::testing::RunAllTests();
-}