gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/test_util.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 "test/util/test_util.h"
    16  
    17  #include <limits.h>
    18  #include <stdlib.h>
    19  #include <string.h>
    20  #include <sys/stat.h>
    21  #include <sys/types.h>
    22  #include <sys/uio.h>
    23  #include <sys/utsname.h>
    24  #include <unistd.h>
    25  
    26  #include <ctime>
    27  #include <iostream>
    28  #include <vector>
    29  
    30  #include "absl/base/attributes.h"
    31  #include "absl/flags/flag.h"   // IWYU pragma: keep
    32  #include "absl/flags/parse.h"  // IWYU pragma: keep
    33  #include "absl/strings/numbers.h"
    34  #include "absl/strings/str_cat.h"
    35  #include "absl/strings/str_split.h"
    36  #include "absl/time/time.h"
    37  #include "test/util/fs_util.h"
    38  #include "test/util/posix_error.h"
    39  
    40  namespace gvisor {
    41  namespace testing {
    42  
    43  constexpr char kGvisorNetwork[] = "GVISOR_NETWORK";
    44  constexpr char kIOUringEnabled[] = "IOURING_ENABLED";
    45  
    46  bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; }
    47  
    48  const std::string GvisorPlatform() {
    49    // Set by runner.go.
    50    const char* env = getenv(kTestOnGvisor);
    51    if (!env) {
    52      return Platform::kNative;
    53    }
    54    return std::string(env);
    55  }
    56  
    57  bool IsRunningWithHostinet() {
    58    const char* env = getenv(kGvisorNetwork);
    59    return env && strcmp(env, "host") == 0;
    60  }
    61  
    62  bool IsIOUringEnabled() {
    63    const char* env = getenv(kIOUringEnabled);
    64    return env && strcmp(env, "TRUE") == 0;
    65  }
    66  
    67  // Inline cpuid instruction.  Preserve %ebx/%rbx register. In PIC compilations
    68  // %ebx contains the address of the global offset table. %rbx is occasionally
    69  // used to address stack variables in presence of dynamic allocas.
    70  #if defined(__x86_64__)
    71  #define GETCPUID(a, b, c, d, a_inp, c_inp) \
    72    asm("mov %%rbx, %%rdi\n"                 \
    73        "cpuid\n"                            \
    74        "xchg %%rdi, %%rbx\n"                \
    75        : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \
    76        : "a"(a_inp), "2"(c_inp))
    77  
    78  CPUVendor GetCPUVendor() {
    79    uint32_t eax, ebx, ecx, edx;
    80    std::string vendor_str;
    81    // Get vendor string (issue CPUID with eax = 0)
    82    GETCPUID(eax, ebx, ecx, edx, 0, 0);
    83    vendor_str.append(reinterpret_cast<char*>(&ebx), 4);
    84    vendor_str.append(reinterpret_cast<char*>(&edx), 4);
    85    vendor_str.append(reinterpret_cast<char*>(&ecx), 4);
    86    if (vendor_str == "GenuineIntel") {
    87      return CPUVendor::kIntel;
    88    } else if (vendor_str == "AuthenticAMD") {
    89      return CPUVendor::kAMD;
    90    }
    91    return CPUVendor::kUnknownVendor;
    92  }
    93  #endif  // defined(__x86_64__)
    94  
    95  bool operator==(const KernelVersion& first, const KernelVersion& second) {
    96    return first.major == second.major && first.minor == second.minor &&
    97           first.micro == second.micro;
    98  }
    99  
   100  PosixErrorOr<KernelVersion> ParseKernelVersion(absl::string_view vers_str) {
   101    KernelVersion version = {};
   102    std::vector<std::string> values =
   103        absl::StrSplit(vers_str, absl::ByAnyChar(".-"));
   104    if (values.size() == 2) {
   105      ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0]));
   106      ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1]));
   107      return version;
   108    } else if (values.size() >= 3) {
   109      ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0]));
   110      ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1]));
   111      ASSIGN_OR_RETURN_ERRNO(version.micro, Atoi<int>(values[2]));
   112      return version;
   113    }
   114    return PosixError(EINVAL, absl::StrCat("Unknown kernel release: ", vers_str));
   115  }
   116  
   117  PosixErrorOr<KernelVersion> GetKernelVersion() {
   118    utsname buf;
   119    RETURN_ERROR_IF_SYSCALL_FAIL(uname(&buf));
   120    return ParseKernelVersion(buf.release);
   121  }
   122  
   123  std::string CPUSetToString(const cpu_set_t& set, size_t cpus) {
   124    std::string str = "cpuset[";
   125    for (unsigned int n = 0; n < cpus; n++) {
   126      if (CPU_ISSET(n, &set)) {
   127        if (n != 0) {
   128          absl::StrAppend(&str, " ");
   129        }
   130        absl::StrAppend(&str, n);
   131      }
   132    }
   133    absl::StrAppend(&str, "]");
   134    return str;
   135  }
   136  
   137  // An overloaded operator<< makes it easy to dump the value of an OpenFd.
   138  std::ostream& operator<<(std::ostream& out, OpenFd const& ofd) {
   139    out << ofd.fd << " -> " << ofd.link;
   140    return out;
   141  }
   142  
   143  // An overloaded operator<< makes it easy to dump a vector of OpenFDs.
   144  std::ostream& operator<<(std::ostream& out, std::vector<OpenFd> const& v) {
   145    for (const auto& ofd : v) {
   146      out << ofd << std::endl;
   147    }
   148    return out;
   149  }
   150  
   151  PosixErrorOr<std::vector<OpenFd>> GetOpenFDs() {
   152    // Get the results from /proc/self/fd.
   153    ASSIGN_OR_RETURN_ERRNO(auto dir_list,
   154                           ListDir("/proc/self/fd", /*skipdots=*/true));
   155  
   156    std::vector<OpenFd> ret_fds;
   157    for (const auto& str_fd : dir_list) {
   158      OpenFd open_fd = {};
   159      ASSIGN_OR_RETURN_ERRNO(open_fd.fd, Atoi<int>(str_fd));
   160      std::string path = absl::StrCat("/proc/self/fd/", open_fd.fd);
   161  
   162      // Resolve the link.
   163      char buf[PATH_MAX] = {};
   164      int ret = readlink(path.c_str(), buf, sizeof(buf));
   165      if (ret < 0) {
   166        if (errno == ENOENT) {
   167          // The FD may have been closed, let's be resilient.
   168          continue;
   169        }
   170  
   171        return PosixError(
   172            errno, absl::StrCat("readlink of ", path, " returned errno ", errno));
   173      }
   174      open_fd.link = std::string(buf, ret);
   175      ret_fds.emplace_back(std::move(open_fd));
   176    }
   177    return ret_fds;
   178  }
   179  
   180  PosixErrorOr<uint64_t> Links(const std::string& path) {
   181    struct stat st;
   182    if (stat(path.c_str(), &st)) {
   183      return PosixError(errno, absl::StrCat("Failed to stat ", path));
   184    }
   185    return static_cast<uint64_t>(st.st_nlink);
   186  }
   187  
   188  void RandomizeBuffer(char* buffer, size_t len) {
   189    struct timespec ts = {};
   190    clock_gettime(CLOCK_MONOTONIC, &ts);
   191    uint32_t seed = static_cast<uint32_t>(ts.tv_nsec);
   192    for (size_t i = 0; i < len; i++) {
   193      buffer[i] = rand_r(&seed) % 255;
   194    }
   195  }
   196  
   197  std::vector<std::vector<struct iovec>> GenerateIovecs(uint64_t total_size,
   198                                                        void* buf,
   199                                                        size_t buflen) {
   200    std::vector<std::vector<struct iovec>> result;
   201    for (uint64_t offset = 0; offset < total_size;) {
   202      auto& iovec_array = *result.emplace(result.end());
   203  
   204      for (; offset < total_size && iovec_array.size() < IOV_MAX;
   205           offset += buflen) {
   206        struct iovec iov = {};
   207        iov.iov_base = buf;
   208        iov.iov_len = std::min<uint64_t>(total_size - offset, buflen);
   209        iovec_array.push_back(iov);
   210      }
   211    }
   212  
   213    return result;
   214  }
   215  
   216  uint64_t Megabytes(uint64_t n) {
   217    // Overflow check, upper 20 bits in n shouldn't be set.
   218    TEST_CHECK(!(0xfffff00000000000 & n));
   219    return n << 20;
   220  }
   221  
   222  bool Equivalent(uint64_t current, uint64_t target, double tolerance) {
   223    auto abs_diff = target > current ? target - current : current - target;
   224    return abs_diff <= static_cast<uint64_t>(tolerance * target);
   225  }
   226  
   227  }  // namespace testing
   228  }  // namespace gvisor