summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/clock_gettime.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/clock_gettime.cc')
-rw-r--r--test/syscalls/linux/clock_gettime.cc156
1 files changed, 156 insertions, 0 deletions
diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc
new file mode 100644
index 000000000..5003928be
--- /dev/null
+++ b/test/syscalls/linux/clock_gettime.cc
@@ -0,0 +1,156 @@
+// Copyright 2018 Google LLC
+//
+// 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 <pthread.h>
+#include <sys/time.h>
+#include <cerrno>
+#include <cstdint>
+#include <ctime>
+#include <list>
+#include <memory>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+int64_t clock_gettime_nsecs(clockid_t id) {
+ struct timespec ts;
+ TEST_PCHECK(clock_gettime(id, &ts) == 0);
+ return (ts.tv_sec * 1000000000 + ts.tv_nsec);
+}
+
+// Spin on the CPU for at least ns nanoseconds, based on
+// CLOCK_THREAD_CPUTIME_ID.
+void spin_ns(int64_t ns) {
+ int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID);
+ int64_t end = start + ns;
+
+ do {
+ constexpr int kLoopCount = 1000000; // large and arbitrary
+ // volatile to prevent the compiler from skipping this loop.
+ for (volatile int i = 0; i < kLoopCount; i++) {
+ }
+ } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end);
+}
+
+// Test that CLOCK_PROCESS_CPUTIME_ID is a superset of CLOCK_THREAD_CPUTIME_ID.
+TEST(ClockGettime, CputimeId) {
+ constexpr int kNumThreads = 13; // arbitrary
+
+ absl::Duration spin_time = absl::Seconds(1);
+
+ // Start off the worker threads and compute the aggregate time spent by
+ // the workers. Note that we test CLOCK_PROCESS_CPUTIME_ID by having the
+ // workers execute in parallel and verifying that CLOCK_PROCESS_CPUTIME_ID
+ // accumulates the runtime of all threads.
+ int64_t start = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID);
+
+ // Create a kNumThreads threads.
+ std::list<ScopedThread> threads;
+ for (int i = 0; i < kNumThreads; i++) {
+ threads.emplace_back(
+ [spin_time] { spin_ns(absl::ToInt64Nanoseconds(spin_time)); });
+ }
+ for (auto& t : threads) {
+ t.Join();
+ }
+
+ int64_t end = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID);
+
+ // The aggregate time spent in the worker threads must be at least
+ // 'kNumThreads' times the time each thread spun.
+ ASSERT_GE(end - start, kNumThreads * absl::ToInt64Nanoseconds(spin_time));
+}
+
+TEST(ClockGettime, JavaThreadTime) {
+ clockid_t clockid;
+ ASSERT_EQ(0, pthread_getcpuclockid(pthread_self(), &clockid));
+ struct timespec tp;
+ ASSERT_THAT(clock_getres(clockid, &tp), SyscallSucceeds());
+ ASSERT_THAT(clock_gettime(clockid, &tp), SyscallSucceeds());
+ EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0);
+}
+
+// There is not much to test here, since CLOCK_REALTIME may be discontiguous.
+TEST(ClockGettime, RealtimeWorks) {
+ struct timespec tp;
+ EXPECT_THAT(clock_gettime(CLOCK_REALTIME, &tp), SyscallSucceeds());
+}
+
+class MonotonicClockTest : public ::testing::TestWithParam<clockid_t> {};
+
+TEST_P(MonotonicClockTest, IsMonotonic) {
+ auto end = absl::Now() + absl::Seconds(5);
+
+ struct timespec tp;
+ EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds());
+
+ auto prev = absl::TimeFromTimespec(tp);
+ while (absl::Now() < end) {
+ EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds());
+ auto now = absl::TimeFromTimespec(tp);
+ EXPECT_GE(now, prev);
+ prev = now;
+ }
+}
+
+std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
+ switch (info.param) {
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_MONOTONIC_COARSE:
+ return "CLOCK_MONOTONIC_COARSE";
+ case CLOCK_MONOTONIC_RAW:
+ return "CLOCK_MONOTONIC_RAW";
+ default:
+ return absl::StrCat(info.param);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(ClockGettime, MonotonicClockTest,
+ ::testing::Values(CLOCK_MONOTONIC,
+ CLOCK_MONOTONIC_COARSE,
+ CLOCK_MONOTONIC_RAW),
+ PrintClockId);
+
+TEST(ClockGettime, UnimplementedReturnsEINVAL) {
+ SKIP_IF(!IsRunningOnGvisor());
+
+ struct timespec tp;
+ EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME, &tp),
+ SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp),
+ SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(ClockGettime, InvalidClockIDReturnsEINVAL) {
+ struct timespec tp;
+ EXPECT_THAT(clock_gettime(-1, &tp), SyscallFailsWithErrno(EINVAL));
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor