gvisor/test/util/proc_util.h

151 lines
4.7 KiB
C++

// 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_