github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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/strings/str_cat.h" 25 #include "absl/strings/string_view.h" 26 #include "test/util/fs_util.h" 27 #include "test/util/posix_error.h" 28 29 namespace gvisor { 30 namespace testing { 31 32 // ProcMapsEntry contains the data from a single line in /proc/<xxx>/maps. 33 struct ProcMapsEntry { 34 uint64_t start; 35 uint64_t end; 36 bool readable; 37 bool writable; 38 bool executable; 39 bool priv; 40 uint64_t offset; 41 int major; 42 int minor; 43 int64_t inode; 44 std::string filename; 45 }; 46 47 // Parses a ProcMaps line or returns an error. 48 PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line); 49 PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( 50 absl::string_view contents); 51 52 // Returns true if vsyscall (emmulation or not) is enabled. 53 PosixErrorOr<bool> IsVsyscallEnabled(); 54 55 // Printer for ProcMapsEntry. 56 inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) { 57 std::string str = 58 absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-", 59 absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " "); 60 61 absl::StrAppend(&str, entry.readable ? "r" : "-"); 62 absl::StrAppend(&str, entry.writable ? "w" : "-"); 63 absl::StrAppend(&str, entry.executable ? "x" : "-"); 64 absl::StrAppend(&str, entry.priv ? "p" : "s"); 65 66 absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8), 67 " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":", 68 absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ", 69 entry.inode); 70 if (absl::string_view(entry.filename) != "") { 71 // Pad to column 74 72 int pad = 73 - str.length(); 73 if (pad > 0) { 74 absl::StrAppend(&str, std::string(pad, ' ')); 75 } 76 absl::StrAppend(&str, entry.filename); 77 } 78 os << str; 79 return os; 80 } 81 82 // Printer for std::vector<ProcMapsEntry>. 83 inline std::ostream& operator<<(std::ostream& os, 84 const std::vector<ProcMapsEntry>& vec) { 85 for (unsigned int i = 0; i < vec.size(); i++) { 86 os << vec[i]; 87 if (i != vec.size() - 1) { 88 os << "\n"; 89 } 90 } 91 return os; 92 } 93 94 // GMock printer for std::vector<ProcMapsEntry>. 95 inline void PrintTo(const std::vector<ProcMapsEntry>& vec, std::ostream* os) { 96 *os << vec; 97 } 98 99 // Checks that /proc/pid/maps contains all of the passed mappings. 100 // 101 // The major, minor, and inode fields are ignored. 102 MATCHER_P(ContainsMappings, mappings, 103 "contains mappings:\n" + ::testing::PrintToString(mappings)) { 104 auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps")); 105 if (!contents_or.ok()) { 106 *result_listener << "Unable to read mappings: " 107 << contents_or.error().ToString(); 108 return false; 109 } 110 111 auto maps_or = ParseProcMaps(contents_or.ValueOrDie()); 112 if (!maps_or.ok()) { 113 *result_listener << "Unable to parse mappings: " 114 << maps_or.error().ToString(); 115 return false; 116 } 117 118 auto maps = std::move(maps_or).ValueOrDie(); 119 120 // Does maps contain all elements in mappings? The comparator ignores 121 // the major, minor, and inode fields. 122 bool all_present = true; 123 std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) { 124 auto it = 125 std::find_if(maps.begin(), maps.end(), [&e1](const ProcMapsEntry& e2) { 126 return e1.start == e2.start && e1.end == e2.end && 127 e1.readable == e2.readable && e1.writable == e2.writable && 128 e1.executable == e2.executable && e1.priv == e2.priv && 129 e1.offset == e2.offset && e1.filename == e2.filename; 130 }); 131 if (it == maps.end()) { 132 // It wasn't found. 133 if (all_present) { 134 // We will output the message once and then a line for each mapping 135 // that wasn't found. 136 all_present = false; 137 *result_listener << "Got mappings:\n" 138 << maps << "\nThat were missing:\n"; 139 } 140 *result_listener << e1 << "\n"; 141 } 142 }); 143 144 return all_present; 145 } 146 147 } // namespace testing 148 } // namespace gvisor 149 150 #endif // GVISOR_TEST_UTIL_PROC_UTIL_H_