gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/proc_util.h (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 #ifndef GVISOR_TEST_UTIL_PROC_UTIL_H_ 16 #define GVISOR_TEST_UTIL_PROC_UTIL_H_ 17 18 #include <ostream> 19 #include <string> 20 #include <vector> 21 22 #include "gmock/gmock.h" 23 #include "gtest/gtest.h" 24 #include "absl/algorithm/container.h" 25 #include "absl/strings/str_cat.h" 26 #include "absl/strings/string_view.h" 27 #include "absl/types/optional.h" 28 #include "test/util/fs_util.h" 29 #include "test/util/posix_error.h" 30 31 namespace gvisor { 32 namespace testing { 33 34 // ProcMapsEntry contains the data from a single line in /proc/<xxx>/maps. 35 struct ProcMapsEntry { 36 uint64_t start; 37 uint64_t end; 38 bool readable; 39 bool writable; 40 bool executable; 41 bool priv; 42 uint64_t offset; 43 int major; 44 int minor; 45 int64_t inode; 46 std::string filename; 47 }; 48 49 struct ProcSmapsEntry { 50 ProcMapsEntry maps_entry; 51 52 // These fields should always exist, as they were included in e070ad49f311 53 // "[PATCH] add /proc/pid/smaps". 54 size_t size_kb; 55 size_t rss_kb; 56 size_t shared_clean_kb; 57 size_t shared_dirty_kb; 58 size_t private_clean_kb; 59 size_t private_dirty_kb; 60 61 // These fields were added later and may not be present. 62 absl::optional<size_t> pss_kb; 63 absl::optional<size_t> referenced_kb; 64 absl::optional<size_t> anonymous_kb; 65 absl::optional<size_t> anon_huge_pages_kb; 66 absl::optional<size_t> shared_hugetlb_kb; 67 absl::optional<size_t> private_hugetlb_kb; 68 absl::optional<size_t> swap_kb; 69 absl::optional<size_t> swap_pss_kb; 70 absl::optional<size_t> kernel_page_size_kb; 71 absl::optional<size_t> mmu_page_size_kb; 72 absl::optional<size_t> locked_kb; 73 74 // Caution: "Note that there is no guarantee that every flag and associated 75 // mnemonic will be present in all further kernel releases. Things get 76 // changed, the flags may be vanished or the reverse -- new added." - Linux 77 // Documentation/filesystems/proc.txt, on VmFlags. Avoid checking for any 78 // flags that are not extremely well-established. 79 absl::optional<std::vector<std::string>> vm_flags; 80 }; 81 82 // Parses a ProcMaps line or returns an error. 83 PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line); 84 PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( 85 absl::string_view contents); 86 87 // Returns true if vsyscall (emmulation or not) is enabled. 88 PosixErrorOr<bool> IsVsyscallEnabled(); 89 90 // Parses /proc/pid/smaps type entries 91 PosixErrorOr<std::vector<ProcSmapsEntry>> ParseProcSmaps(absl::string_view); 92 // Reads and parses /proc/self/smaps 93 PosixErrorOr<std::vector<ProcSmapsEntry>> ReadProcSelfSmaps(); 94 // Reads and parses /proc/pid/smaps 95 PosixErrorOr<std::vector<ProcSmapsEntry>> ReadProcSmaps(pid_t); 96 97 // Returns the unique entry in entries containing the given address. 98 PosixErrorOr<ProcSmapsEntry> FindUniqueSmapsEntry( 99 std::vector<ProcSmapsEntry> const&, uintptr_t); 100 101 // Check if stack has nh flag. 102 bool StackTHPDisabled(std::vector<ProcSmapsEntry>); 103 104 // Returns true if THP is disabled for current process. 105 bool IsTHPDisabled(); 106 107 // Printer for ProcMapsEntry. 108 inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) { 109 std::string str = 110 absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-", 111 absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " "); 112 113 absl::StrAppend(&str, entry.readable ? "r" : "-"); 114 absl::StrAppend(&str, entry.writable ? "w" : "-"); 115 absl::StrAppend(&str, entry.executable ? "x" : "-"); 116 absl::StrAppend(&str, entry.priv ? "p" : "s"); 117 118 absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8), 119 " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":", 120 absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ", 121 entry.inode); 122 if (absl::string_view(entry.filename) != "") { 123 // Pad to column 74 124 int pad = 73 - str.length(); 125 if (pad > 0) { 126 absl::StrAppend(&str, std::string(pad, ' ')); 127 } 128 absl::StrAppend(&str, entry.filename); 129 } 130 os << str; 131 return os; 132 } 133 134 // Printer for std::vector<ProcMapsEntry>. 135 inline std::ostream& operator<<(std::ostream& os, 136 const std::vector<ProcMapsEntry>& vec) { 137 for (unsigned int i = 0; i < vec.size(); i++) { 138 os << vec[i]; 139 if (i != vec.size() - 1) { 140 os << "\n"; 141 } 142 } 143 return os; 144 } 145 146 // GoogleTest printer for std::vector<ProcMapsEntry>. 147 inline void PrintTo(const std::vector<ProcMapsEntry>& vec, std::ostream* os) { 148 *os << vec; 149 } 150 151 // Checks that /proc/pid/maps contains all of the passed mappings. 152 // 153 // The major, minor, and inode fields are ignored. 154 MATCHER_P(ContainsMappings, mappings, 155 "contains mappings:\n" + ::testing::PrintToString(mappings)) { 156 auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps")); 157 if (!contents_or.ok()) { 158 *result_listener << "Unable to read mappings: " 159 << contents_or.error().ToString(); 160 return false; 161 } 162 163 auto maps_or = ParseProcMaps(contents_or.ValueOrDie()); 164 if (!maps_or.ok()) { 165 *result_listener << "Unable to parse mappings: " 166 << maps_or.error().ToString(); 167 return false; 168 } 169 170 auto maps = std::move(maps_or).ValueOrDie(); 171 172 // Does maps contain all elements in mappings? The comparator ignores 173 // the major, minor, and inode fields. 174 bool all_present = true; 175 std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) { 176 auto it = 177 absl::c_find_if(maps, [&e1](const ProcMapsEntry& e2) { 178 return e1.start == e2.start && e1.end == e2.end && 179 e1.readable == e2.readable && e1.writable == e2.writable && 180 e1.executable == e2.executable && e1.priv == e2.priv && 181 e1.offset == e2.offset && e1.filename == e2.filename; 182 }); 183 if (it == maps.end()) { 184 // It wasn't found. 185 if (all_present) { 186 // We will output the message once and then a line for each mapping 187 // that wasn't found. 188 all_present = false; 189 *result_listener << "Got mappings:\n" 190 << maps << "\nThat were missing:\n"; 191 } 192 *result_listener << e1 << "\n"; 193 } 194 }); 195 196 return all_present; 197 } 198 199 // LimitType is an rlimit type 200 enum class LimitType { 201 kCPU, 202 kFileSize, 203 kData, 204 kStack, 205 kCore, 206 kRSS, 207 kProcessCount, 208 kNumberOfFiles, 209 kMemoryLocked, 210 kAS, 211 kLocks, 212 kSignalsPending, 213 kMessageQueueBytes, 214 kNice, 215 kRealTimePriority, 216 kRttime, 217 }; 218 219 const std::vector<LimitType> LimitTypes{ 220 LimitType::kCPU, 221 LimitType::kFileSize, 222 LimitType::kData, 223 LimitType::kStack, 224 LimitType::kCore, 225 LimitType::kRSS, 226 LimitType::kProcessCount, 227 LimitType::kNumberOfFiles, 228 LimitType::kMemoryLocked, 229 LimitType::kAS, 230 LimitType::kLocks, 231 LimitType::kSignalsPending, 232 LimitType::kMessageQueueBytes, 233 LimitType::kNice, 234 LimitType::kRealTimePriority, 235 LimitType::kRttime, 236 }; 237 238 std::string LimitTypeToString(LimitType type); 239 240 // ProcLimitsEntry contains the data from a single line in /proc/xxx/limits. 241 struct ProcLimitsEntry { 242 LimitType limit_type; 243 uint64_t cur_limit; 244 uint64_t max_limit; 245 }; 246 247 // Parses a single line from /proc/xxx/limits. 248 PosixErrorOr<ProcLimitsEntry> ParseProcLimitsLine(absl::string_view line); 249 250 // Parses an entire /proc/xxx/limits file into lines. 251 PosixErrorOr<std::vector<ProcLimitsEntry>> ParseProcLimits( 252 absl::string_view contents); 253 254 // Printer for ProcLimitsEntry. 255 std::ostream& operator<<(std::ostream& os, const ProcLimitsEntry& entry); 256 257 // Printer for std::vector<ProcLimitsEntry>. 258 std::ostream& operator<<(std::ostream& os, 259 const std::vector<ProcLimitsEntry>& vec); 260 261 // GoogleTest printer for std::vector<ProcLimitsEntry>. 262 inline void PrintTo(const std::vector<ProcLimitsEntry>& vec, std::ostream* os) { 263 *os << vec; 264 } 265 } // namespace testing 266 } // namespace gvisor 267 268 #endif // GVISOR_TEST_UTIL_PROC_UTIL_H_