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