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