gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/clock_gettime.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <pthread.h>
    16  #include <sys/time.h>
    17  
    18  #include <cerrno>
    19  #include <cstdint>
    20  #include <ctime>
    21  #include <list>
    22  #include <memory>
    23  #include <string>
    24  
    25  #include "gmock/gmock.h"
    26  #include "gtest/gtest.h"
    27  #include "absl/time/clock.h"
    28  #include "absl/time/time.h"
    29  #include "test/util/test_util.h"
    30  #include "test/util/thread_util.h"
    31  
    32  namespace gvisor {
    33  namespace testing {
    34  
    35  namespace {
    36  
    37  int64_t clock_gettime_nsecs(clockid_t id) {
    38    struct timespec ts;
    39    TEST_PCHECK(clock_gettime(id, &ts) == 0);
    40    return (ts.tv_sec * 1000000000 + ts.tv_nsec);
    41  }
    42  
    43  // Spin on the CPU for at least ns nanoseconds, based on
    44  // CLOCK_THREAD_CPUTIME_ID.
    45  void spin_ns(int64_t ns) {
    46    int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID);
    47    int64_t end = start + ns;
    48  
    49    do {
    50      constexpr int kLoopCount = 1000000;  // large and arbitrary
    51      // volatile to prevent the compiler from skipping this loop.
    52      for (volatile int i = 0; i < kLoopCount; i++) {
    53      }
    54    } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end);
    55  }
    56  
    57  // Test that CLOCK_PROCESS_CPUTIME_ID is a superset of CLOCK_THREAD_CPUTIME_ID.
    58  TEST(ClockGettime, CputimeId) {
    59    constexpr int kNumThreads = 13;  // arbitrary
    60  
    61    absl::Duration spin_time = absl::Seconds(1);
    62  
    63    // Start off the worker threads and compute the aggregate time spent by
    64    // the workers. Note that we test CLOCK_PROCESS_CPUTIME_ID by having the
    65    // workers execute in parallel and verifying that CLOCK_PROCESS_CPUTIME_ID
    66    // accumulates the runtime of all threads.
    67    int64_t start = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID);
    68  
    69    // Create a kNumThreads threads.
    70    std::list<ScopedThread> threads;
    71    for (int i = 0; i < kNumThreads; i++) {
    72      threads.emplace_back(
    73          [spin_time] { spin_ns(absl::ToInt64Nanoseconds(spin_time)); });
    74    }
    75    for (auto& t : threads) {
    76      t.Join();
    77    }
    78  
    79    int64_t end = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID);
    80  
    81    // The aggregate time spent in the worker threads must be at least
    82    // 'kNumThreads' times the time each thread spun.
    83    ASSERT_GE(end - start, kNumThreads * absl::ToInt64Nanoseconds(spin_time));
    84  }
    85  
    86  TEST(ClockGettime, JavaThreadTime) {
    87    clockid_t clockid;
    88    ASSERT_EQ(0, pthread_getcpuclockid(pthread_self(), &clockid));
    89    struct timespec tp;
    90    ASSERT_THAT(clock_getres(clockid, &tp), SyscallSucceeds());
    91    EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0);
    92    // A thread cputime is updated each 10msec and there is no approximation
    93    // if a task is running.
    94    do {
    95      ASSERT_THAT(clock_gettime(clockid, &tp), SyscallSucceeds());
    96    } while (tp.tv_sec == 0 && tp.tv_nsec == 0);
    97    EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0);
    98  }
    99  
   100  // There is not much to test here, since CLOCK_REALTIME may be discontiguous.
   101  TEST(ClockGettime, RealtimeWorks) {
   102    struct timespec tp;
   103    EXPECT_THAT(clock_gettime(CLOCK_REALTIME, &tp), SyscallSucceeds());
   104  }
   105  
   106  class MonotonicClockTest : public ::testing::TestWithParam<clockid_t> {};
   107  
   108  TEST_P(MonotonicClockTest, IsMonotonic) {
   109    auto end = absl::Now() + absl::Seconds(5);
   110  
   111    struct timespec tp;
   112    EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds());
   113  
   114    auto prev = absl::TimeFromTimespec(tp);
   115    while (absl::Now() < end) {
   116      EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds());
   117      auto now = absl::TimeFromTimespec(tp);
   118      EXPECT_GE(now, prev);
   119      prev = now;
   120    }
   121  }
   122  
   123  std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
   124    switch (info.param) {
   125      case CLOCK_MONOTONIC:
   126        return "CLOCK_MONOTONIC";
   127      case CLOCK_MONOTONIC_COARSE:
   128        return "CLOCK_MONOTONIC_COARSE";
   129      case CLOCK_MONOTONIC_RAW:
   130        return "CLOCK_MONOTONIC_RAW";
   131      case CLOCK_BOOTTIME:
   132        // CLOCK_BOOTTIME is a monotonic clock.
   133        return "CLOCK_BOOTTIME";
   134      default:
   135        return absl::StrCat(info.param);
   136    }
   137  }
   138  
   139  INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicClockTest,
   140                           ::testing::Values(CLOCK_MONOTONIC,
   141                                             CLOCK_MONOTONIC_COARSE,
   142                                             CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME),
   143                           PrintClockId);
   144  
   145  TEST(ClockGettime, UnimplementedReturnsEINVAL) {
   146    SKIP_IF(!IsRunningOnGvisor());
   147  
   148    struct timespec tp;
   149    EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp),
   150                SyscallFailsWithErrno(EINVAL));
   151    EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp),
   152                SyscallFailsWithErrno(EINVAL));
   153  }
   154  
   155  TEST(ClockGettime, InvalidClockIDReturnsEINVAL) {
   156    struct timespec tp;
   157    EXPECT_THAT(clock_gettime(-1, &tp), SyscallFailsWithErrno(EINVAL));
   158  }
   159  
   160  }  // namespace
   161  
   162  }  // namespace testing
   163  }  // namespace gvisor