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