summaryrefslogtreecommitdiffhomepage
path: root/test/util/proc_util.h
diff options
context:
space:
mode:
Diffstat (limited to 'test/util/proc_util.h')
-rw-r--r--test/util/proc_util.h150
1 files changed, 150 insertions, 0 deletions
diff --git a/test/util/proc_util.h b/test/util/proc_util.h
new file mode 100644
index 000000000..f8021d92e
--- /dev/null
+++ b/test/util/proc_util.h
@@ -0,0 +1,150 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef GVISOR_TEST_UTIL_PROC_UTIL_H_
+#define GVISOR_TEST_UTIL_PROC_UTIL_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "test/util/fs_util.h"
+#include "test/util/posix_error.h"
+
+namespace gvisor {
+namespace testing {
+
+// ProcMapsEntry contains the data from a single line in /proc/<xxx>/maps.
+struct ProcMapsEntry {
+ uint64_t start;
+ uint64_t end;
+ bool readable;
+ bool writable;
+ bool executable;
+ bool priv;
+ uint64_t offset;
+ int major;
+ int minor;
+ int64_t inode;
+ std::string filename;
+};
+
+// Parses a ProcMaps line or returns an error.
+PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line);
+PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps(
+ absl::string_view contents);
+
+// Returns true if vsyscall (emmulation or not) is enabled.
+PosixErrorOr<bool> IsVsyscallEnabled();
+
+// Printer for ProcMapsEntry.
+inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) {
+ std::string str =
+ absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-",
+ absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " ");
+
+ absl::StrAppend(&str, entry.readable ? "r" : "-");
+ absl::StrAppend(&str, entry.writable ? "w" : "-");
+ absl::StrAppend(&str, entry.executable ? "x" : "-");
+ absl::StrAppend(&str, entry.priv ? "p" : "s");
+
+ absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8),
+ " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":",
+ absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ",
+ entry.inode);
+ if (absl::string_view(entry.filename) != "") {
+ // Pad to column 74
+ int pad = 73 - str.length();
+ if (pad > 0) {
+ absl::StrAppend(&str, std::string(pad, ' '));
+ }
+ absl::StrAppend(&str, entry.filename);
+ }
+ os << str;
+ return os;
+}
+
+// Printer for std::vector<ProcMapsEntry>.
+inline std::ostream& operator<<(std::ostream& os,
+ const std::vector<ProcMapsEntry>& vec) {
+ for (unsigned int i = 0; i < vec.size(); i++) {
+ os << vec[i];
+ if (i != vec.size() - 1) {
+ os << "\n";
+ }
+ }
+ return os;
+}
+
+// GMock printer for std::vector<ProcMapsEntry>.
+inline void PrintTo(const std::vector<ProcMapsEntry>& vec, std::ostream* os) {
+ *os << vec;
+}
+
+// Checks that /proc/pid/maps contains all of the passed mappings.
+//
+// The major, minor, and inode fields are ignored.
+MATCHER_P(ContainsMappings, mappings,
+ "contains mappings:\n" + ::testing::PrintToString(mappings)) {
+ auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps"));
+ if (!contents_or.ok()) {
+ *result_listener << "Unable to read mappings: "
+ << contents_or.error().ToString();
+ return false;
+ }
+
+ auto maps_or = ParseProcMaps(contents_or.ValueOrDie());
+ if (!maps_or.ok()) {
+ *result_listener << "Unable to parse mappings: "
+ << maps_or.error().ToString();
+ return false;
+ }
+
+ auto maps = std::move(maps_or.ValueOrDie());
+
+ // Does maps contain all elements in mappings? The comparator ignores
+ // the major, minor, and inode fields.
+ bool all_present = true;
+ std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) {
+ auto it =
+ std::find_if(maps.begin(), maps.end(), [&e1](const ProcMapsEntry& e2) {
+ return e1.start == e2.start && e1.end == e2.end &&
+ e1.readable == e2.readable && e1.writable == e2.writable &&
+ e1.executable == e2.executable && e1.priv == e2.priv &&
+ e1.offset == e2.offset && e1.filename == e2.filename;
+ });
+ if (it == maps.end()) {
+ // It wasn't found.
+ if (all_present) {
+ // We will output the message once and then a line for each mapping
+ // that wasn't found.
+ all_present = false;
+ *result_listener << "Got mappings:\n"
+ << maps << "\nThat were missing:\n";
+ }
+ *result_listener << e1 << "\n";
+ }
+ });
+
+ return all_present;
+}
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_UTIL_PROC_UTIL_H_