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_