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_